fix(delivery): validate project_path in set instead of mkdir-ing whatever it is given (#493) - #508
Open
Masashi-Ono0611 wants to merge 1 commit into
Open
Conversation
…ever it is given (fujibee#493) set built a hooks-file path straight from an unvalidated project_path and mkdir -p'd it, so a malformed literal - the report's case is a trailing newline from an agent-composed command - created a bogus sibling directory and installed hooks into it. agmsg_validate_project_path rejects rather than corrects: trimming is used to DETECT surrounding whitespace, not to absorb it, so a caller that generated a bad command gets a loud error naming the value instead of a lucky save that hides the bug upstream. A path that does not already exist is refused, never created. The status of the traversability check is tested explicitly rather than folded into a command substitution - printf returns 0 regardless, which would let a permission failure pass as a successful validation of an empty path. The caller's own path spelling is echoed back; canonicalizing would be a second, unrequested behavioral change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Masashi-Ono0611
marked this pull request as ready for review
July 27, 2026 23:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #493.
delivery.sh setdidn't validate<project_path>at all, soresolve_hooks_fileconcatenated whatever it was given andagmsg_delivery_applymkdir -p'd the result — which is how a directory literally namedmyproject\nended up next to the real one, containing only.codex/hooks.jsonwith the newline baked into the hook commands' project path.agmsg_validate_project_path()now runs once indo_set, before any type-specific apply logic.The policy, and why
Reject, don't correct. A trailing newline is detected by trimming, then the value is refused because the trimmed form differs from what was passed. Silently "fixing" it would make a malformed command work this one time and hide the bug in whatever generated it. As the issue notes, agmsg's callers are largely LLM agents composing commands from SKILL.md — for that audience a loud error naming the exact value is worth more than a lucky save.
Never create implicitly. A path that doesn't already exist as a directory is refused rather than created. That is the actual defect from the report: a directory nobody asked for.
Also rejected: empty or whitespace-only, an embedded newline or carriage return, and a directory that exists but can't be entered (
-dpasses for a directory with no execute bit, and every apply implementation then tries to write inside it — better to fail here with a clear message than later with a confusingmkdirerror).Two things I changed after review
printf '%s' "$(cd "$raw" && pwd)".printfreturns 0 regardless, so a permission failure or race inside the substitution would have produced a successful validation of an empty path. The status is now checked explicitly, andcd -- "$raw"keeps a real directory named like an option (-P,-L) from being parsed as one. A validator that fails open is worse than no validator.cd && pwd, mirroringspawn.sh:188. But that's a second, unrequested behavioral change — it rewrites relative paths to absolute and collapses./.., so anything downstream comparing or persisting this value would start seeing a different string than the caller passed.delivery.sh setsilently mkdirs a bogus project dir when passed a malformed path (no existence check / trim) #493 is about refusing malformed input, not normalizing well-formed input. The function now echoes the caller's own spelling back.Verification
bats tests/test_delivery.bats→ 148 ok, 0 not ok, exit 0.myproject\nrepro (asserting no directory is created), nonexistent path, empty, whitespace-only, leading/trailing spaces where the trimmed path does exist (proving it doesn't quietly fall back), embedded newline, and the un-enterable directory (skipped when running as root, where permission bits don't restrict traversal).codex exec(gpt-5.6-sol, high). The fail-openprintfand thecd --hardening are its findings.Scope notes
do_setrather than in each apply implementation. That covers the default JSON-hooks path andrulefile_applyas far as I traced, but I have not done a full call-graph audit, so I'm not claiming categorically that no other entry point can reach the samemkdir. If you know of one, say so and I'll extend it.pwd-style normalization can rewriteC:/repoto/c/repo; dropping the canonicalization sidesteps that question entirely rather than answering it.