Skip to content

openapi: a merge chain past MergeDepthLimit hides an $id, expanding a $dynamicRef across a resource boundary #401

Description

@OmarAlJarrah

declaresResourceIDAbove (compilers/openapi/internal/schema/schema.go) reads $id off each node on the path from the document root down to a position, through nodeview. When a node's own merge chain is longer than MergeDepthLimit (64), View.expand returns no pairs at all, so ChildByToken(n, "\$id") finds nothing and the walk reports no boundary.

That is the direction the function's own comment says it must not err in: a false boundary costs an expansion that would have been safe, a missed one mints a reference the IR cannot express.

Reproducer

openapi: 3.1.0
info: {title: Deep, version: "1"}
paths: {}
x-l0: &l0 {$id: 'https://example.com/deep'}
x-l1: &l1 {<<: *l0}
# ... 70 links total
components:
  schemas:
    Node: {$dynamicAnchor: T, type: string}
    A: {<<: *l70, $dynamicRef: '#T'}

A's $dynamicRef is expanded to t/openapi/components/schemas/Node, crossing the $id. Shortening the chain to two links degrades correctly.

Why it is not fixed in passing

View.Exhausted() is set when the bound is hit, but the boundary walk builds its own view and never consults it, and there is no channel for "this verdict rests on an expansion that was truncated". The anchor index has the same situation and reports it as openapi/cycle-scan-failed; this walk needs an equivalent, which is a design decision rather than a one-line guard. Noted in the code comment on declaresResourceIDAbove.

Predates the empty-pointer-segment fix in #362; found while probing it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions