summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2026-04-21 16:27:44 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2026-04-21 17:16:04 +0900
commitfce0a26bca7b9a7c7e6642bd77ce0311664a4e31 (patch)
treee1f86d2075aba567f025c068c90a5bad6115b6f4
parentd3da9fec828481422cc4084892454bf572d7af5a (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.rb3
-rw-r--r--test/erb/test_erb.rb27
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__ %>