From eacfd6084ae5b6e4f58592d0490e87593c84c34f Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 11 Oct 2026 19:24:32 -0500 Subject: [PATCH] test(cli): the default Claude pin goes through the exec and character checks (#1572, row 78) Two default-pin cases (no --claude): the only claude on PATH is mode 0644, and claude in a PATH dir with a space. render exits 1 with stdout empty for each. PATH holds only the test's dirs. Author Rocko, reviewer Filbert (27560), candidate manifest 3016dbf1. Co-Authored-By: Claude Opus 5.5 --- packages/cli/tests/host.test.mjs | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/cli/tests/host.test.mjs b/packages/cli/tests/host.test.mjs index 13a621ae9..6f0961afa 100644 --- a/packages/cli/tests/host.test.mjs +++ b/packages/cli/tests/host.test.mjs @@ -611,3 +611,41 @@ test("bus-service.sh refuses a directory pin and keeps a symlink default unresol assert.equal(pinned(r.stdout), join(linkBin, "claude")); assert.ok(!r.stdout.includes(real), "the target never reaches the unit"); }); + +// Row 78 (#1572): the default from `command -v claude` takes the same checks +// as --claude. PATH holds only the test's dirs, so a real claude elsewhere on +// the host can't stand in for the one under test. +test("bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space", (t) => { + const dir = tmp(t); + const script = join(REPO, "scripts", "bus-service.sh"); + const tools = join(dir, "tools"); + mkdirSync(tools); + symlinkSync(process.execPath, join(tools, "node")); + for (const tool of ["dirname", "sed", "readlink"]) { + const at = ["/usr/bin", "/bin"].map((d) => join(d, tool)).find((p) => existsSync(p)); + symlinkSync(at, join(tools, tool)); + } + const bash = ["/usr/bin/bash", "/bin/bash"].find((p) => existsSync(p)); + const run = (bin) => spawnSync(bash, [script, "render"], { encoding: "utf8", env: { PATH: `${bin}:${tools}`, HOME: dir } }); + const stand = (bin, mode) => { + mkdirSync(bin); + writeFileSync(join(bin, "claude"), '#!/bin/sh\necho "9.9.9 (Claude Code)"\n'); + chmodSync(join(bin, "claude"), mode); + return bin; + }; + + // The only claude on PATH has no execute bit: `command -v` still returns + // it, and the -x check refuses it. + let r = run(stand(join(dir, "noexec-bin"), 0o644)); + assert.equal(r.status, 1); + assert.match(r.stderr, /not an executable file/); + assert.equal(r.stdout, ""); + + // claude in a PATH dir with a space: systemd would split the pin there. + const spaced = stand(join(dir, "spaced bin"), 0o755); + r = run(spaced); + assert.equal(r.status, 1); + assert.match(r.stderr, /cannot carry/); + assert.ok(r.stderr.includes(join(spaced, "claude")), r.stderr); + assert.equal(r.stdout, ""); +});