From 486a4b8f680f60100cebd1dcd0aa750e3924229b Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 6 Oct 2025 16:25:12 +0200 Subject: [PATCH] Escape special chars in shell scripts --- packages/safe-chain/src/utils/safeSpawn.js | 12 +++++++++--- packages/safe-chain/src/utils/safeSpawn.spec.js | 9 +++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/safe-chain/src/utils/safeSpawn.js b/packages/safe-chain/src/utils/safeSpawn.js index 826ab7d..f45e4ff 100644 --- a/packages/safe-chain/src/utils/safeSpawn.js +++ b/packages/safe-chain/src/utils/safeSpawn.js @@ -1,9 +1,15 @@ import { spawnSync, spawn } from "child_process"; function escapeArg(arg) { - // If argument contains spaces or quotes, wrap in double quotes and escape double quotes - if (arg.includes(" ") || arg.includes('"') || arg.includes("'")) { - return '"' + arg.replaceAll('"', '\\"') + '"'; + // Shell metacharacters that need escaping + // These characters have special meaning in shells and need to be quoted + const shellMetaChars = /[ "&'|;<>()$`\\!*?[\]{}~#]/; + + // If argument contains shell metacharacters, wrap in double quotes + // and escape characters that are special even inside double quotes + if (shellMetaChars.test(arg)) { + // Inside double quotes, we need to escape: " $ ` \ + return '"' + arg.replace(/(["`$\\])/g, '\\$1') + '"'; } return arg; } diff --git a/packages/safe-chain/src/utils/safeSpawn.spec.js b/packages/safe-chain/src/utils/safeSpawn.spec.js index d325f8a..020b59a 100644 --- a/packages/safe-chain/src/utils/safeSpawn.spec.js +++ b/packages/safe-chain/src/utils/safeSpawn.spec.js @@ -105,5 +105,14 @@ describe("safeSpawn", () => { assert.strictEqual(spawnCalls[0].command, "npm install axios --save"); assert.strictEqual(spawnCalls[0].options.shell, true); }); + + it(`should escape ampersand character (${variant})`, async () => { + await runSafeSpawn(variant, "npx", ["cypress", "run", "--env", "password=foo&bar"]); + + assert.strictEqual(spawnCalls.length, 1); + // & should be escaped by wrapping the arg in quotes + assert.strictEqual(spawnCalls[0].command, 'npx cypress run --env "password=foo&bar"'); + assert.strictEqual(spawnCalls[0].options.shell, true); + }); } }); \ No newline at end of file