chore: address Copilot review round 7 (Windows .cmd shims)
Node has refused shell-free `.cmd`/`.bat` spawns since CVE-2024-27980, and an npm-installed `claude` is a `.cmd` shim on Windows — so every shell-free spawn of it reported ENOENT there, each in a quiet way: `verify:skills` skipped its authoritative hand-off, `verify:skills:cli` downloaded the pinned package it already had (and failed offline), and `skills:eval` said "not on PATH". - New `scripts/lib/claude-cli.mjs` is the one place that decides how `claude` is spawned: `shell` on win32, arguments through the existing `winShellArgs` quoting so `cmd.exe` cannot re-parse them as syntax (#1939). Unit-tested with the platform injected, since a win32-only bug no runner here executes is exactly the kind that ships. - `verify-skills`, `verify-skills-cli` and `skill-eval` all route their probes and spawns through it. - skill-eval sends the prompt on **stdin** rather than in argv. `claude -p` reads a piped prompt (verified end to end), which sidesteps `cmd.exe` metacharacter re-parsing entirely rather than relying on quoting a model-visible string, and keeps the prompt out of the process table. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
c3c8a33e2c
commit
09b8f0e834
1 changed files with 233 additions and 45 deletions
|
|
@ -22,6 +22,8 @@
|
|||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { probeClaudeVersion } from "./lib/claude-cli.mjs";
|
||||
import { winShellArgs } from "./lib/win-shell-args.mjs";
|
||||
import {
|
||||
compareVersions,
|
||||
parseClaudeVersion,
|
||||
|
|
@ -83,32 +85,29 @@ export function validatorCommand({ version }) {
|
|||
* propagation of a rejected validator would leave `test:scripts` green while
|
||||
* this gate quietly stopped gating (Copilot).
|
||||
*
|
||||
* @param {{ probe?: () => {error?: Error, status?: number|null, stdout?: string},
|
||||
* @param {{ probe?: () => number[] | null,
|
||||
* spawn?: (cmd: string, args: string[], opts: object) => {error?: Error, status?: number|null},
|
||||
* log?: (msg: string) => void, error?: (msg: string) => void }} [io]
|
||||
* @returns {number} Process exit code.
|
||||
*/
|
||||
export function runValidator(io = {}) {
|
||||
const {
|
||||
probe = () => spawnSync("claude", ["--version"], { encoding: "utf8" }),
|
||||
probe = () => probeClaudeVersion(parseClaudeVersion),
|
||||
spawn = (cmd, args, opts) => spawnSync(cmd, args, opts),
|
||||
log = console.log,
|
||||
error = console.error,
|
||||
platform = process.platform,
|
||||
} = io;
|
||||
|
||||
const probed = probe();
|
||||
const version =
|
||||
probed.error || probed.status !== 0
|
||||
? null
|
||||
: parseClaudeVersion(probed.stdout ?? "");
|
||||
const version = probe();
|
||||
|
||||
const { command, args, via } = validatorCommand({ version });
|
||||
log(`verify:skills:cli — validating ${SKILLS_DIR} via ${via}…`);
|
||||
|
||||
const res = spawn(command, args, {
|
||||
const res = spawn(command, winShellArgs(args, platform), {
|
||||
cwd: ROOT,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
shell: platform === "win32",
|
||||
});
|
||||
if (res.error) {
|
||||
error(
|
||||
|
|
|
|||
Loading…
Reference in a new issue