Summary
harness check (cli pre-commit mode) is vulnerable to arbitrary shell command execution via a crafted staged filename. Confirmed live, not just from reading the code.
Root cause
src/cli/run.ts:
export function stagedContent(path: string): string {
return execSync(`git show ":${path}"`, { encoding: "utf8" });
}
path comes from stagedFiles(), which is just git diff --cached --name-only --diff-filter=ACMR output — i.e. the literal names of staged files. Interpolating that into a shell string handed to execSync means any staged filename containing shell metacharacters runs as shell syntax.
Git's default core.quotepath=true does escape filenames containing an embedded " or \, which neutralizes the most obvious injection attempt (and is why my first try, using an embedded quote, didn't fire). But it does not escape backticks or $(...), so those go through untouched.
Reproduction (safe — uses touch, tested in a throwaway repo)
mkdir /tmp/harness-poc && cd /tmp/harness-poc && git init -q
npm i @fusengine/harness
FILENAME='x`touch pwned.txt`.ts'
echo "content" > "$FILENAME"
git add -- "$FILENAME"
npx harness check
# git then fails on the mangled remaining path ("x.ts" doesn't exist), but
# by that point the backtick command substitution has already run:
ls pwned.txt # <- exists
Tested against 0.1.79 (latest as of 2026-07-20).
Impact
Anyone running harness check as a pre-commit hook (per the README's own .husky/pre-commit example) against a repo whose staged file list isn't fully trusted — e.g. right after checking out a contributor's branch/PR and running git add . — gets arbitrary command execution. This is exactly the scenario a pre-commit check is usually run in.
Suggested fix
Use execFileSync with an argv array instead of a shell string, so the filename is never parsed by a shell regardless of its content:
import { execFileSync } from "node:child_process";
export function stagedContent(path: string): string {
return execFileSync("git", ["show", `:${path}`], { encoding: "utf8" });
}
Happy to send a PR for this if useful — just didn't want to open one unprompted before flagging the issue itself.
Summary
harness check(cli pre-commit mode) is vulnerable to arbitrary shell command execution via a crafted staged filename. Confirmed live, not just from reading the code.Root cause
src/cli/run.ts:pathcomes fromstagedFiles(), which is justgit diff --cached --name-only --diff-filter=ACMRoutput — i.e. the literal names of staged files. Interpolating that into a shell string handed toexecSyncmeans any staged filename containing shell metacharacters runs as shell syntax.Git's default
core.quotepath=truedoes escape filenames containing an embedded"or\, which neutralizes the most obvious injection attempt (and is why my first try, using an embedded quote, didn't fire). But it does not escape backticks or$(...), so those go through untouched.Reproduction (safe — uses
touch, tested in a throwaway repo)Tested against
0.1.79(latest as of 2026-07-20).Impact
Anyone running
harness checkas a pre-commit hook (per the README's own.husky/pre-commitexample) against a repo whose staged file list isn't fully trusted — e.g. right after checking out a contributor's branch/PR and runninggit add .— gets arbitrary command execution. This is exactly the scenario a pre-commit check is usually run in.Suggested fix
Use
execFileSyncwith an argv array instead of a shell string, so the filename is never parsed by a shell regardless of its content:Happy to send a PR for this if useful — just didn't want to open one unprompted before flagging the issue itself.