From cbff6d1d2ae6eb3ccf7a60cbeb09e1abed2a6550 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 15 May 2024 11:28:25 +0900 Subject: [PATCH 1/2] Split to `Unmarshalable` module --- lib/erb.rb | 21 +++++++++++++++++---- test/erb/test_erb.rb | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index bc1615d..f86f220 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -268,6 +268,21 @@ def self.version VERSION end + module Unmarshalable # :nodoc: + def initialize_for_marshal + @_init = self.class.singleton_class + end + private :initialize_for_marshal + + def pre_eval_check + unless @_init.equal?(self.class.singleton_class) + raise ArgumentError, "not initialized" + end + end + private :pre_eval_check + end + include Unmarshalable + # # Constructs a new ERB object with the template specified in _str_. # @@ -351,7 +366,7 @@ def initialize(str, safe_level=NOT_GIVEN, legacy_trim_mode=NOT_GIVEN, legacy_eou @src, @encoding, @frozen_string = *compiler.compile(str) @filename = nil @lineno = 0 - @_init = self.class.singleton_class + initialize_for_marshal end NOT_GIVEN = Object.new private_constant :NOT_GIVEN @@ -423,9 +438,7 @@ def run(b=new_toplevel) # code evaluation. # def result(b=new_toplevel) - unless @_init.equal?(self.class.singleton_class) - raise ArgumentError, "not initialized" - end + pre_eval_check eval(@src, b, (@filename || '(erb)'), @lineno) end diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 555345a..10ea491 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -714,6 +714,21 @@ def test_prohibited_marshal_load assert_raise(ArgumentError) {erb.result} end + class MarshalableERB < ERB + Unmarshalable.private_instance_methods.each do |m| + define_method(m) {} + end + end + + def test_marshalable_subclass_marshal + erb = MarshalableERB.new("<%=x%>") + dump = assert_nothing_raised(TypeError) {Marshal.dump(erb)} + loaded = Marshal.load(dump) + bind = binding + bind.local_variable_set(:x, 42) + assert_equal("42", loaded.result(bind)) + end + def test_multi_line_comment_lineno erb = ERB.new(<<~EOS) <%= __LINE__ %> From ad02d05ed1205e939191a8e361fa9a83c4715ff7 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 17 May 2024 13:26:37 +0900 Subject: [PATCH 2/2] Define `ERB::Marshalable` module A sub class of `ERB` that `include`s this module can be marshal dumped/loaded. When using such classes, be careful not to load untrusted data. --- lib/erb.rb | 7 +++++++ test/erb/test_erb.rb | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index f86f220..91d917a 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -283,6 +283,13 @@ def pre_eval_check end include Unmarshalable + # Module to make ERB marshalable. + module Marshalable # :nodoc: + Unmarshalable.private_instance_methods.each do |m| + define_method(m) {} + end + end + # # Constructs a new ERB object with the template specified in _str_. # diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 10ea491..180897a 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -715,9 +715,7 @@ def test_prohibited_marshal_load end class MarshalableERB < ERB - Unmarshalable.private_instance_methods.each do |m| - define_method(m) {} - end + include Marshalable end def test_marshalable_subclass_marshal