Repository navigation
fix(agent): descriptor-rooted, no-follow file operations - #570
Merged
Merged
Conversation
Validate paths lexically, then run every operation relative to a directory descriptor opened on the root with O_NOFOLLOW per component, so filesystem changes made after validation cannot redirect operations outside the root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Add tests that verify file operations stay confined even when a path component is swapped for a symlink between validation and use (TOCTOU races). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Update specs and security documentation to reflect that file operations validate paths lexically then run through directory descriptors rooted at the data directory, preventing TOCTOU symlink attacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ording
Wrap fd int→uintptr conversion in fileFromFD with a negative-fd guard for
gosec G115 compliance. Preserve http.ServeFile error response bodies ("404
page not found", "403 Forbidden") in read/download handlers. Update mods
package documentation wording.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Delegate all openFile error handling through h.fail() for consistent error classification and response formatting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 12 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI Report - 407ba35run 37342845735 - attempt 1 0 failed - 14 passed - 6 skipped - 27m54s elapsed Coverageno coverage/* commit statuses found on 407ba35 Changed
Slowest jobs
e2e buckets run
|
Resolve agent/specs.md after #569: keep the moderation-reason sentence on the console-injection guard and this branch's descriptor-rooted path and write text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
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
The agent file browser (
agent/internal/files) used to validate a path and then reopen it by string. A component swapped for a symlink between the check and the open could therefore redirect a read, write or delete outside the data root (TOCTOU). Every operation now runs through directory descriptors opened from a trusted root descriptor and refuses links at every component.resolve()is now purely lexical; it does the dotfile and../escape checks. Each directory component is opened withopenat(O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC)relative to its parent's descriptor. The final component is opened withO_NOFOLLOW.O_EXCL|O_NOFOLLOW), thenfchmod,fsyncandrenameatin the same directory.mkdiratandunlinkat. Recursive delete walks descriptors.ServeContent; listing reads the opened directory descriptor.httpErr("not found" / "forbidden" / "internal error").os.Rootoropenat2:os.Rootfollows in-root symlinks.openat2(RESOLVE_NO_SYMLINKS)needs kernel 5.6+ plus this same walk as a fallback.fd→*os.Filegoes through onefileFromFDhelper with anfd < 0guard, so gosec G115 is satisfied.Behaviour changes on purpose:
filespackage builds only on Linux (rooted_linux.go). The agent ships only as Linux images.Tests
rooted_test.go(new): race tests use a synchronousbeforeOpenseam. It swaps a checked directory for a symlink to outside the root just before the final open. The tests check that read, list, write, mkdir, upload and delete all fail and never touch anything outside the root. There are also unit tests for the walk helpers and forfileFromFD.files_test.go, nothing deleted):TestResolve_SymlinkEscapenow asserts throughh.list.TestResolve_SymlinkToDotfileRejectedasserts through read, write and delete, with stronger checks.…RejectsInRootSymlinkDestination/TestUpload_RejectsSymlinkDestinationInRoot. The "symlinked root still serves" half moved toTestRooted_SymlinkedRootStillServes.Test plan
go (agent / amd64|arm64)and lint (gosec, noctx, revive)api-agentbucket (file browser over the agent)Nothing was built or run locally, at the maintainer's request; CI is the verification. Briefed at sonnet, implemented at haiku, reviewed at opus; the review fixes were verified at sonnet.
🤖 Generated with Claude Code
https://claude.ai/code/session_018MnGTGHzHhFQCa22wRknUS