diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb index 475444e5..f37d7879 100644 --- a/lib/psych/visitors/to_ruby.rb +++ b/lib/psych/visitors/to_ruby.rb @@ -152,6 +152,7 @@ def visit_Psych_Nodes_Sequence o when '!omap', 'tag:yaml.org,2002:omap' map = register(o, Psych::Omap.new) o.children.each { |a| + next unless a.children && a.children.size >= 2 map[accept(a.children.first)] = accept a.children.last } map diff --git a/test/psych/test_omap.rb b/test/psych/test_omap.rb index 6de02864..b61d2b14 100644 --- a/test/psych/test_omap.rb +++ b/test/psych/test_omap.rb @@ -72,5 +72,29 @@ def test_load_shorthand eoyml assert_equal list, map.to_a end + + # Regression test for OSS-Fuzz crash: malformed omap with scalar children + # Bug: NoMethodError: undefined method `first' for nil when omap has scalar child + # The omap processing code assumed all children would be sequences with .children + # returning an array, but scalar nodes have .children == nil + def test_oss_fuzz_crash_omap_scalar_child + # This YAML reproduces the OSS-Fuzz crash found by fuzz_load fuzzer + # An !omap tag with a bare "-" creates a scalar child instead of sequence + # When the code tries to access scalar.children.first, it calls nil.first + yaml = <<~YAML + --- + !omap + - + YAML + + # Use the exact same method as the fuzzer: Psych.safe_load_stream + # This processes the omap tag and triggers the crash without the fix + result = Psych.safe_load_stream(yaml) + assert_kind_of Array, result + # The omap should be created but empty (malformed entry skipped) + omap = result.first + assert_kind_of Hash, omap # safe_load_stream returns plain Hash, not Omap + assert_equal 0, omap.size + end end end