diff --git a/src/lib/config-io.ts b/src/lib/config-io.ts index 00cbbe7ac9..afd516ab79 100644 --- a/src/lib/config-io.ts +++ b/src/lib/config-io.ts @@ -7,7 +7,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { shellQuote } from "./runner"; +import { shellQuote } from "./shell-quote"; function buildRemediation(): string { const home = process.env.HOME || os.homedir(); diff --git a/src/lib/shell-quote.ts b/src/lib/shell-quote.ts new file mode 100644 index 0000000000..55b88d5162 --- /dev/null +++ b/src/lib/shell-quote.ts @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Shell-quote a value for safe interpolation into bash -c strings. + * Wraps in single quotes and escapes embedded single quotes. + */ +export function shellQuote(value: string): string { + return `'${String(value).replace(/'/g, `'\\''`)}'`; +} diff --git a/test/runner.test.ts b/test/runner.test.ts index 5f824203af..f1fc6c261e 100644 --- a/test/runner.test.ts +++ b/test/runner.test.ts @@ -429,8 +429,11 @@ describe("regression guards", () => { defs.push(path.relative(repoRoot, file)); } } - expect(defs).toHaveLength(1); - expect(defs[0]).toBe(path.join("src", "lib", "runner.ts")); + // runner.ts (CJS consumers) and shell-quote.ts (ESM consumers like config-io.ts) + expect(defs.sort()).toEqual([ + path.join("src", "lib", "runner.ts"), + path.join("src", "lib", "shell-quote.ts"), + ]); }); it("CLI rejects malicious sandbox names before shell commands (e2e)", () => {