From acbc61c095e17688b81ccf06b78c53b4c4d1aace Mon Sep 17 00:00:00 2001 From: Asad Tariq Date: Thu, 11 Jun 2026 21:30:45 +0300 Subject: [PATCH] fix(v3): don't inject empty webhooks for OpenAPI 3.0 docs with same-named scalar extractWebhooks called ExtractMap unconditionally and guarded on the returned key/value nodes. When a document has no top-level `webhooks` key, ExtractMap resolves the label through the index and can match a same-named scalar value elsewhere (e.g. `service: webhooks`), producing a non-nil empty webhooks map that renders as `webhooks: {}` -- invalid in OpenAPI 3.0.x. Gate extraction on the genuine top-level `webhooks` node collected in documentTopLevelNodes, mirroring extractServers/extractTags/extractPaths. Fixes #586 --- datamodel/low/v3/create_document.go | 5 +++++ datamodel/low/v3/create_document_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index 7ae8f838..bfc06e94 100644 --- a/datamodel/low/v3/create_document.go +++ b/datamodel/low/v3/create_document.go @@ -483,6 +483,11 @@ func extractPaths(ctx context.Context, root *yaml.Node, nodes documentTopLevelNo } func extractWebhooks(ctx context.Context, root *yaml.Node, nodes documentTopLevelNodes, doc *Document, idx *index.SpecIndex) error { + // without a genuine top-level key, ExtractMap can match a same-named scalar + // (e.g. "webhooks" in an extension value) and create an empty webhooks map. + if nodes.webhooks.value == nil { + return nil + } hooks, hooksL, hooksN, err := low.ExtractMap[*PathItem](ctx, WebhooksLabel, root, idx) if err != nil { return err diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 9eec996d..873a1eff 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -496,6 +496,28 @@ webhooks: assert.Len(t, utils.UnwrapErrors(err), 1) } +// a "webhooks" scalar value (here in an extension) must not create a webhooks map. +func TestCreateDocument_WebHooks_NoFalsePositive(t *testing.T) { + yml := `openapi: 3.0.3 +info: + title: t + version: "1.0.0" +x-foo: + service: webhooks +paths: + /a: + get: + responses: + '200': + description: OK` + + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{}) + require.NoError(t, err) + assert.Nil(t, d.Webhooks.Value) + assert.Nil(t, d.Webhooks.KeyNode) +} + func TestCreateDocument_Servers(t *testing.T) { initTest() assert.Len(t, doc.Servers.Value, 2)