Skip to content

Commit 60e8042

Browse files
hsbtclaude
authored andcommitted
[ruby/rubygems] Refactor validate_tag! to enforce permitted_classes consistently with Psych
Align YAMLSerializer's `permitted_classes` validation with Psych's whitelist semantics: an empty `permitted_classes` list denies all tagged classes, matching `Psych::ClassLoader::Restricted` behavior. - Rename `@permitted_tags` to `@permitted_classes` and simplify initialization - Extract `raise_disallowed_class!` from `validate_tag!` for clarity - Move `check_anchor!` before `validate_tag!` in `build_mapping` - Add test for `Gem::Version::Requirement` tag used by old gems like `escape` ruby/rubygems@3c5855e833 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8723c8b commit 60e8042

2 files changed

Lines changed: 31 additions & 10 deletions

File tree

lib/rubygems/yaml_serializer.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,7 @@ class Builder
423423
MAX_ALIAS_RESOLUTIONS = 1_000
424424

425425
def initialize(permitted_classes: [], permitted_symbols: [], aliases: true)
426-
@permitted_tags = Array(permitted_classes).map do |c|
427-
"!ruby/object:#{c.is_a?(Module) ? c.name : c}"
428-
end
426+
@permitted_classes = permitted_classes.map {|c| "!ruby/object:#{c}" }
429427
@permitted_symbols = permitted_symbols
430428
@aliases = aliases
431429
@anchor_values = {}
@@ -474,8 +472,8 @@ def store_anchor(name, value)
474472
end
475473

476474
def build_mapping(node)
477-
validate_tag!(node.tag) if node.tag
478475
check_anchor!(node)
476+
validate_tag!(node.tag) if node.tag
479477

480478
result = case node.tag
481479
when "!ruby/object:Gem::Version"
@@ -605,12 +603,15 @@ def pairs_to_hash(node)
605603
end
606604

607605
def validate_tag!(tag)
608-
unless @permitted_tags.include?(tag)
609-
if defined?(Psych::VERSION)
610-
raise Psych::DisallowedClass.new("load", tag)
611-
else
612-
raise Psych::DisallowedClass, "Tried to load unspecified class: #{tag}"
613-
end
606+
return if @permitted_classes.include?(tag)
607+
raise_disallowed_class!(tag)
608+
end
609+
610+
def raise_disallowed_class!(tag)
611+
if defined?(Psych::VERSION)
612+
raise Psych::DisallowedClass.new("load", tag)
613+
else
614+
raise Psych::DisallowedClass, "Tried to load unspecified class: #{tag}"
614615
end
615616
end
616617

test/rubygems/test_gem_safe_yaml.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,4 +1216,24 @@ def test_binary_tag_decoded_in_sequence_item_inline
12161216
result = yaml_load(yaml)
12171217
assert_equal ["SHA1"], result
12181218
end
1219+
1220+
def test_version_requirement_tag_always_permitted
1221+
yaml = <<~YAML
1222+
--- !ruby/object:Gem::Specification
1223+
name: escape
1224+
version: !ruby/object:Gem::Version
1225+
version: 0.0.4
1226+
required_ruby_version: !ruby/object:Gem::Version::Requirement
1227+
requirements:
1228+
- - ">"
1229+
- !ruby/object:Gem::Version
1230+
version: 0.0.0
1231+
version:
1232+
YAML
1233+
1234+
result = yaml_load(yaml)
1235+
assert_kind_of Gem::Specification, result
1236+
assert_equal "escape", result.name
1237+
assert_kind_of Gem::Requirement, result.required_ruby_version
1238+
end
12191239
end

0 commit comments

Comments
 (0)