From c21818e093cc6a60b3fc2786dc5dd06f837469d4 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Tue, 11 Aug 2020 13:57:50 -0700 Subject: [PATCH] check-openapi: Disallow siblings of $ref. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The specification says “any sibling elements of a $ref are ignored”, so their presence, although not invalid, indicates a mistake. yamole incorrectly merges these siblings into the referenced object, but we should not rely on this nonstandard behavior. https://swagger.io/docs/specification/using-ref/#sibling Signed-off-by: Anders Kaseorg --- tools/check-openapi | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/tools/check-openapi b/tools/check-openapi index 0f04f7ef53..85c2d5a4b5 100755 --- a/tools/check-openapi +++ b/tools/check-openapi @@ -7,17 +7,39 @@ const jsyaml = require("js-yaml"); const ExampleValidator = require("openapi-examples-validator"); const SwaggerParser = require("swagger-parser"); +function checkRefSiblings(file, path, data) { + let ok = true; + if (typeof data === "object" && data !== null) { + if ("$ref" in data && Object.entries(data).length !== 1) { + console.error( + `${file}: Siblings of $ref have no effect at ${JSON.stringify( + path, + )}: ${JSON.stringify(data, undefined, 2)}`, + ); + ok = false; + } + for (const [key, child] of Array.isArray(data) ? data.entries() : Object.entries(data)) { + if (!checkRefSiblings(file, [...path, key], child)) { + ok = false; + } + } + } + return ok; +} + (async () => { // Iterate through the changed files, passed in the arguments. // The two first arguments are the call to the Node interpreter and this // script, hence the starting point at 2. for (const file of process.argv.slice(2)) { try { - if ( - jsyaml.safeLoad(await fs.promises.readFile(file, "utf8"), { - filename: file, - }).openapi !== undefined - ) { + const data = jsyaml.safeLoad(await fs.promises.readFile(file, "utf8"), { + filename: file, + }); + if (data.openapi !== undefined) { + if (!checkRefSiblings(file, [], data)) { + process.exitCode = 1; + } await SwaggerParser.validate(file); const res = await ExampleValidator.validateFile(file); if (!res.valid) {