Skip to content

[12.x] Guard JsonSchema deserializer against unbounded $ref expansion#60524

Merged
taylorotwell merged 1 commit into
laravel:12.xfrom
pushpak1300:fix-jsonschema-ref-expansion-12x
Jun 16, 2026
Merged

[12.x] Guard JsonSchema deserializer against unbounded $ref expansion#60524
taylorotwell merged 1 commit into
laravel:12.xfrom
pushpak1300:fix-jsonschema-ref-expansion-12x

Conversation

@pushpak1300

Copy link
Copy Markdown
Member

Backport of #60517 to 12.x.

When deserializing a schema whose $defs reference each other, fromArray() inlines each $ref into the type tree. With a lot of nested cross-references this expansion can grow very large and use a surprising amount of time and memory before you get anything back.

This adds a cap on how many fragments a single schema can expand into, throwing InvalidArgumentException once it goes past the limit instead of churning. Normal schemas are nowhere near it, so valid input is unaffected. Also includes two small behavior-preserving tweaks in the same method (array_flip lookup for required, and memoizing lookupRef).

Repro

A small schema where each $defs entry references the next one twice expands exponentially with depth:

use Illuminate\JsonSchema\JsonSchema;

$depth = 25;
$defs = [];

for ($i = 0; $i < $depth; $i++) {
    $defs["a{$i}"] = ['type' => 'object', 'properties' => [
        'x' => ['$ref' => "#/\$defs/a" . ($i + 1)],
        'y' => ['$ref' => "#/\$defs/a" . ($i + 1)],
    ]];
}

$defs["a{$depth}"] = ['type' => 'string'];

// A ~2 KB schema that previously expanded into millions of nodes.
JsonSchema::fromArray(['$defs' => $defs, '$ref' => '#/$defs/a0']);

With this change the same input throws InvalidArgumentException once it crosses the limit instead of consuming large amounts of memory and time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taylorotwell taylorotwell merged commit 08971b6 into laravel:12.x Jun 16, 2026
71 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants