fix: prevent path traversal in proposeCommit (CWE-22)#36
Open
sebastiondev wants to merge 1 commit intoforloopcodes:mainfrom
Open
fix: prevent path traversal in proposeCommit (CWE-22)#36sebastiondev wants to merge 1 commit intoforloopcodes:mainfrom
sebastiondev wants to merge 1 commit intoforloopcodes:mainfrom
Conversation
Validate that the resolved file path stays within rootDir before writing. Without this check, an agent-supplied filePath containing "../" sequences or absolute paths could escape the project root and write to arbitrary filesystem locations. The fix resolves both rootDir and the combined path, then checks that the result starts with rootDir + "/". This blocks: - Relative traversal (e.g., "../../../etc/cron.d/malicious") - Nested traversal (e.g., "subdir/../../escaped.txt") - Absolute paths outside rootDir
|
@sebastiondev is attempting to deploy a commit to the ForLoopCodes' projects Team on Vercel. A member of the Team first needs to authorize it. |
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.
Summary
This PR fixes a path traversal vulnerability (CWE-22) in
proposeCommitthat allows an MCP client to write arbitrary files outside the project root.Vulnerability
src/tools/propose-commit.tsproposeCommitThe MCP tool
propose_commitacceptsfile_pathas a parameter directly from the MCP client. Before this fix, the implementation did:path.resolvehappily collapses..segments and treats absolute paths as overrides. With no containment check, afile_pathlike../../../.ssh/authorized_keys,../../../etc/cron.d/x, or an absolute path such as/tmp/anywhereresolves outsiderootDirand is then written to. There were no other guards on the path between the parameter and the write.Data flow
propose_commitwith attacker-controlledfile_path.proposeCommitresolves the path againstrootDirwith no boundary check.new_content) is written to the resolved location.The MCP server's intended threat model treats
rootDiras the sandbox boundary; this bug breaks that boundary.Fix
Resolve
rootDirto an absolute, canonical form, resolve the joined path, and reject anything that is not equal torootDiror underrootDir + "/":This is the standard prefix-containment check after canonicalization, and it runs before any filesystem write, so the bad path never reaches
writeFile.Tests
Added
test/main/propose-commit-traversal.test.mjscovering:../traversal (e.g.../escaped.txt) — rejected../../../tmp/evil.txt) — rejectedsubdir/../../escaped.txt) — rejectedAll new tests pass; existing tests are unaffected (only this function's preamble changed).
A note on portability: the check uses
/as the separator, matching the existing POSIX-style usage in this file. On Windows the check would needpath.sep; happy to follow up with that if you'd like it in the same PR.Adversarial review
Before submitting we tried to disprove this. We checked whether anything upstream of
proposeCommitalready constrainsfile_path— the MCP tool schema accepts it as a free-form string, the handler passes it through unchanged, and there is no other validator in the call chain. We also considered whether the MCP server's process boundary alone is sufficient; it isn't, because the server typically runs with the developer's own filesystem permissions, so "stay inside the project root" has to be enforced in code. The exploitation precondition is just "the MCP client can be induced to callpropose_commitwith a crafted path," which is exactly the prompt-injection / malicious-context surface MCP tools are expected to defend against.cc @lewiswigmore