Skip to content

fix: run prettier without a shell so filenames can't be executed#840

Open
ryanwaits wants to merge 1 commit into
PostHog:mainfrom
ryanwaits:fix/prettier-shell-sensitive-filenames
Open

fix: run prettier without a shell so filenames can't be executed#840
ryanwaits wants to merge 1 commit into
PostHog:mainfrom
ryanwaits:fix/prettier-shell-sensitive-filenames

Conversation

@ryanwaits

Copy link
Copy Markdown

Fixes #800

Problem

Two bugs, and the one that isn't in the issue is the one that bites everyone.

The parser breaks before the shell sees a path. git C-quotes anything with a space or a non-ASCII byte, /^\S+\s+(\S+)/ only takes the first token, and renames yield the pre-rename path. So an ordinary rename plus a delete is enough: prettier gets keep.ts oldname.ts "to, the shell hits an unterminated quote, exit 2, nothing formatted.

Then the reported bug. Filenames are interpolated into exec. Spaces get quoted by git, but ;, >, | and $ don't, so a file named a.ts;>PWNED executes the redirect.

Fix

Read git status --porcelain=v1 -z and pass filenames as argv entries instead of interpolating them into a shell command.

-z is git's documented machine-parsing format: no quoting, reversed rename order, guaranteed stable across versions and user config. Keep the post-rename path, skip deletes. Run prettier's CLI under process.execPath via execFile with shell: false, plus -- so a leading-dash filename isn't read as a flag.

execFile('npx', ...) was the smaller diff, but it puts the shell back on Windows, where npx is npx.cmd. Resolving prettier from node_modules avoids that everywhere.

Testing

10 new tests in setup-utils.test.ts; 8 fail on main. End to end on a repo with a rename, a delete, with space.ts and a.ts;>PWNED: before, shell error, nothing formatted. After, exit 0, all formatted, no PWNED. Build, tests and lint are green.

Caveat

Prettier declared but not resolvable from node_modules now returns quietly instead of letting npx fetch it. That covers running before install, and Yarn PnP, which has no node_modules at all. Happy to add an npx fallback on non-Windows, where execFile can reach it without a shell.

@ryanwaits ryanwaits requested a review from a team as a code owner July 9, 2026 15:35
@gewenyu99

Copy link
Copy Markdown
Collaborator

Yeah this is a weird one eh? Maybe it's time to hand this off to a subagent to come up with the environment specific linter calls... We've had quite a few issues with these tools

@gewenyu99 gewenyu99 self-assigned this Jul 9, 2026
@ryanwaits

Copy link
Copy Markdown
Author

yeah the step shells out and it assumes prettier, and this only fixes the shelling out.

the assumption is the bigger one imo. hasDeclaredDependency('prettier') means a project on biome, or dprint, or plain eslint --fix, silently gets nothing. biome is already in the agent's safe-tools allowlist, so the agent can run it but the format step can't.

detecting the project's own formatter and running its binary out of node_modules feels like the right shape. happy to pick that up as a follow-up, though it probably warrants its own issue rather than growing this one? just lmk!

@gewenyu99

Copy link
Copy Markdown
Collaborator

Yeah, I'm gonna review this one as is. That is a separate issue all together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

runPrettierStep breaks on shell-sensitive filenames

2 participants