diff options
| author | Takashi Kokubun <takashikkbn@gmail.com> | 2026-04-21 16:27:44 +0900 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2026-04-22 08:56:46 +0900 |
| commit | 85a0a76403ea52cf8af913f112d28c9f1d401eab (patch) | |
| tree | 2c1855257e722c8eb7e3c8a4d2c01dc3331f0e41 | |
| parent | 1bdce3585e2e135a3d69c5bf3e3ea446d4839851 (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 bc1615d7da..a7317c0856 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -463,6 +463,9 @@ class ERB # erb.def_method(MyClass, 'render(arg1, arg2)', filename) # print MyClass.new.render('foo', 123) 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 555345a140..1266b64e41 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -714,6 +714,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__ %> |
