From 7a3748266b3790de241b0dce90e0ebbe581850f4 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Thu, 11 Dec 2025 17:36:30 -0600 Subject: [PATCH] Disallow anonymous Data from being dumped This feature leaked out without much discussion, but there's many reasons why it should not be supported: * Each instance of an anonymous Data in the stream will cause a new Data subtype to be defined. * Multiple objects of the same anonymous Data cannot be round- tripped, since they will no longer be of the same type after loading. As with Marshal's behavior for anonymous classes and structs, Psych should reject such objects rather than attempt to fake a Data class that appears to fit the original structure. --- lib/psych/visitors/yaml_tree.rb | 1 + test/psych/test_object_references.rb | 4 +++- test/psych/visitors/test_yaml_tree.rb | 7 ++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/psych/visitors/yaml_tree.rb b/lib/psych/visitors/yaml_tree.rb index b6c86f4c..632dc141 100644 --- a/lib/psych/visitors/yaml_tree.rb +++ b/lib/psych/visitors/yaml_tree.rb @@ -163,6 +163,7 @@ def visit_Object o alias :visit_Delegator :visit_Object def visit_Data o + raise TypeError, "can't dump anonymous Data: #{o}" unless o.class.name ivars = o.instance_variables if ivars.empty? tag = ['!ruby/data', o.class.name].compact.join(':') diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb index 0498d54e..be245cc6 100644 --- a/test/psych/test_object_references.rb +++ b/test/psych/test_object_references.rb @@ -31,9 +31,11 @@ def test_struct_has_references assert_reference_trip Struct.new(:foo).new(1) end + D = Data.define(:foo) unless RUBY_VERSION < "3.2" + def test_data_has_references omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" - assert_reference_trip Data.define(:foo).new(1) + assert_reference_trip D.new(1) end def assert_reference_trip obj diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb index bd3919f8..d99dc050 100644 --- a/test/psych/visitors/test_yaml_tree.rb +++ b/test/psych/visitors/test_yaml_tree.rb @@ -83,13 +83,14 @@ def test_data def test_data_anon omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" d = Data.define(:foo).new('bar') - obj = Psych.unsafe_load(Psych.dump(d)) - assert_equal d.foo, obj.foo + assert_raise(TypeError) { Psych.dump(d) } end + D2 = Data.define(:method) unless RUBY_VERSION < "3.2" + def test_data_override_method omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" - d = Data.define(:method).new('override') + d = D2.new('override') obj = Psych.unsafe_load(Psych.dump(d)) assert_equal d.method, obj.method end