{
  "equivalence_confidence": "high",
  "same_sink": true,
  "same_vulnerable_code": true,
  "same_root_cause": true,
  "same_trust_boundary": true,
  "same_attacker_controlled_input": true,
  "explanation": "All tested alternate triggers (Oj.dump, Oj::StringWriter, Oj::StreamWriter, Oj.default_options) reach the same vulnerable sink: fill_indent() in ext/oj/dump.h, which performs an unbounded memset whose size is indent * depth. The out->indent value is always populated from the Options struct, which is always set by the shared option parser oj_parse_options() / parse_options_cb(). Therefore the same unbounded integer-to-memset path is exercised, and the upstream MAX_INDENT validation in parse_options_cb prevents it for every entry point.",
  "sink": {
    "file": "ext/oj/dump.h",
    "function": "fill_indent",
    "lines": "73-80"
  },
  "shared_option_parser": {
    "file": "ext/oj/oj.c",
    "function": "parse_options_cb",
    "lines": "760-790"
  },
  "entry_points": [
    "Oj.dump({a: 1}, indent: 2147483647)",
    "Oj::StringWriter.new(indent: 2147483647).push_array.push_value(1).pop_all",
    "Oj::StreamWriter.new(io, indent: 2147483647).push_array.push_value(1).pop_all",
    "Oj.default_options = {indent: 2147483647}; Oj.dump({a: 1})"
  ],
  "why_not_distinct": "The alternate entry points are different Ruby APIs but do not cross a different trust boundary. They all pass the same attacker-controlled option through the same option parser to the same sink. The upstream fix covers them all, so they do not represent a distinct bypass or materially different variant."
}
