From fce0a26bca7b9a7c7e6642bd77ce0311664a4e31 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 21 Apr 2026 16:27:44 +0900 Subject: [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 --- lib/erb.rb | 3 +++ test/erb/test_erb.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) 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__ %> -- cgit v1.2.3