diff options
| author | Takashi Kokubun <takashikkbn@gmail.com> | 2026-04-21 16:27:44 +0900 |
|---|---|---|
| committer | Takashi Kokubun <takashikkbn@gmail.com> | 2026-04-21 17:16:04 +0900 |
| commit | fce0a26bca7b9a7c7e6642bd77ce0311664a4e31 (patch) | |
| tree | e1f86d2075aba567f025c068c90a5bad6115b6f4 | |
| parent | d3da9fec828481422cc4084892454bf572d7af5a (diff) | |
[ruby/erb] Prohibit def_method on marshal-loaded ERB instances
Extends the @_init guard to def_method so that an ERB object created
via Marshal.load (which bypasses initialize) raises ArgumentError
instead of evaluating arbitrary source. def_module and def_class both
delegate to def_method and are covered by the same check.
Co-authored-by: Tristan Madani <TristanInSec@gmail.com>
| -rw-r--r-- | lib/erb.rb | 3 | ||||
| -rw-r--r-- | test/erb/test_erb.rb | 27 |
2 files changed, 30 insertions, 0 deletions
diff --git a/lib/erb.rb b/lib/erb.rb index 445d4795b0..1266ee8169 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -1086,6 +1086,9 @@ class ERB # ``` # def def_method(mod, methodname, fname='(ERB)') + unless @_init.equal?(self.class.singleton_class) + raise ArgumentError, "not initialized" + end src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n" mod.module_eval do eval(src, binding, fname, -1) diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 09496d31e2..9eec43da15 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -664,6 +664,33 @@ EOS assert_raise(ArgumentError) {erb.result} end + def test_prohibited_marshal_load_def_method + erb = ERB.allocate + erb.instance_variable_set(:@src, "") + erb.instance_variable_set(:@lineno, 1) + erb.instance_variable_set(:@_init, true) + erb = Marshal.load(Marshal.dump(erb)) + assert_raise(ArgumentError) {erb.def_method(Class.new, 'render')} + end + + def test_prohibited_marshal_load_def_module + erb = ERB.allocate + erb.instance_variable_set(:@src, "") + erb.instance_variable_set(:@lineno, 1) + erb.instance_variable_set(:@_init, true) + erb = Marshal.load(Marshal.dump(erb)) + assert_raise(ArgumentError) {erb.def_module} + end + + def test_prohibited_marshal_load_def_class + erb = ERB.allocate + erb.instance_variable_set(:@src, "") + erb.instance_variable_set(:@lineno, 1) + erb.instance_variable_set(:@_init, true) + erb = Marshal.load(Marshal.dump(erb)) + assert_raise(ArgumentError) {erb.def_class} + end + def test_multi_line_comment_lineno erb = ERB.new(<<~EOS) <%= __LINE__ %> |
