[codex] Fix Windows allowedRoots bypass#40
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAllowed-root validation now rejects absolute results from ChangesWindows allowed-root validation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Potential Issues
Example: Allowed root:
G:\Projects\SafeFolder
Inside it:
G:\Projects\SafeFolder\escape
But escape is a junction pointing to:
C:\Users\Ahmed\SecretsDevSpace may think this is allowed because the typed path starts inside This is the most important remaining risk.
Example: C:\repo\file.txt
\\?\C:\repo\file.txtThese can refer to the same place, but the current check may treat them as different and reject the second one. That is safer than allowing too much, but it may annoy users with unusual Windows path formats.
Example: Z:\project
\\server\share\projectThese might point to the same folder, but the current check may not know that unless it resolves the real filesystem path. Likely result: false rejection, not a security bypass.
The PR only adds one Windows regression test: G:\allowed-root
C:\outside-rootThat proves the reported bug is fixed, but it does not cover junctions, symlinks, network paths, mapped drives, or weird Windows path formats.
If someone was accidentally relying on the old broken behavior, this PR will block them. Example: Allowed root:
G:\Projects
Previously accepted by mistake:
C:\Users\AhmedThat is a good break, because the old behavior was unsafe, but it is still a behavior change. Future PR That future PR should:
Use
If a path inside an allowed root points outside through a junction/symlink, reject it.
For reads, the file exists, so For writes, the file may not exist yet. In that case, resolve the nearest existing parent folder and make sure that parent is still inside the allowed root.
Add tests for: junction inside allowed root pointing outside
symlink inside allowed root pointing outside
same root path
child path
sibling path
C:\ vs G:\ cross-drive path
UNC paths like \\server\share
mapped-drive style paths
\\?\C:\... namespaced paths
mixed-case paths
trailing slash paths
The security docs should say that allowed roots are resolved to their real filesystem locations, and that symlinks/junctions cannot be used to escape them. So: PR #40 is good and fixes the reported bug. The next PR should be a broader “canonical path containment” security hardening pass. |
Summary
Fixes #13.
This PR closes a Windows-specific
allowedRootsbypass where a path on a different drive from the configured allowlist root could be treated as allowed.Root cause
isPathInsideRoot()usespath.relative(resolvedRoot, resolvedPath)to decide whether a requested path is contained by an allowed root. The existing check rejected relative paths that escaped the root with..segments:That is enough for same-drive Windows paths and POSIX paths, but it misses an important Windows behavior: when
path.relative()compares paths on different drives, it can return an absolute path instead of a..-prefixed relative path.For example:
Because
"C:\\Users\\Administrator"does not start with.., the old predicate could incorrectly accept it as inside the allowed root.Security impact
This undermines the filesystem allowlist boundary on Windows. A server configured with a narrow allowlist such as:
could still allow MCP clients to open a workspace under a different drive, such as:
That matches the class of behavior reported in #13 and can allow reads/writes outside the configured approved roots.
Changes
isAbsolutefromnode:path.path.relative()results insideisPathInsideRoot().C:\Users\Administratoris rejected when the only allowed root is onG:\....Why this fix
A valid contained path should produce either:
...So the containment check now requires the relationship to be non-absolute before accepting it:
This preserves the existing same-drive behavior while closing the cross-drive Windows bypass.
Validation
Automated checks run on Windows:
All passed.
Manual QA was also performed on Windows through ChatGPT / DevSpace Local5 with the fixed build running.
DevSpace was restarted with this configured allowed root:
Confirmed through ChatGPT / DevSpace Local5 that the configured root is accessible:
Confirmed that the previous DevSpace source folder is no longer accessible after restart:
Confirmed that unrelated paths on other drives are rejected as outside allowed roots:
Also confirmed broader parent/sibling probes are blocked, so ChatGPT cannot discover access by scanning upward outside the configured root.
Manual QA result:
C:\...andF:\...paths are blockedAdditional observation: DevSpace does not currently expose a tool that lists configured allowed roots directly to the MCP client. ChatGPT can only verify access by attempting to open a specific absolute path.
Summary by CodeRabbit