fix(undo): skip symlinks when walking snapshots - #1493
Merged
Conversation
Contributor
PR Review SummarySize
Affected crates
Blast radius — ContainedThis PR touches: source code Updated automatically on each push to this PR. |
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
kurtisc
force-pushed
the
kurtisc/symlink-rep
branch
from
July 23, 2026 13:44
3137013 to
c7c0d2f
Compare
There was a problem hiding this comment.
nogent code review
No critical vulnerabilities found; 2 consistency bugs identified in symlink preflight checks.
Findings (not tied to a changed line):
- 🔒 [HIGH · security]
crates/nono/src/undo/snapshot.rs:256— By completely skipping symlinks inwalk_current, any new symlink created by the untrusted sandboxed agent during its execution will be omitted fromcurrent_files. Consequently, the cleanup loop inrestore_to(which deletes files present incurrent_filesbut absent from the manifest) will never see or delete these symlinks, allowing untrusted symlinks to persist across session rollbacks. To fix this,walk_currentshould yield symlink paths with a sentinel size/hash (or a dedicated symlink variant/collection) so thatrestore_tocan safely detect and clean them up. - 🐛 [MEDIUM · bug]
crates/nono-cli/src/rollback_preflight.rs:142— The preflight helperdetect_heavy_dirsdoes not check if the tracked root itself is a symlink. If a tracked root is a symlink targeting a directory,detect_heavy_dirswill traverse it usingstd::fs::read_dir, whereasSnapshotManager::walk_and_storeandwalk_currentwill explicitly skip it. Check the root'sfs::symlink_metadataand skip it if it is a symlink, to ensure consistency between preflight detection and the actual snapshot walk. - 🐛 [LOW · bug]
crates/nono-cli/src/rollback_preflight.rs:227— Inexceeds_file_threshold,entry.path().is_file()is used to count files, which follows symlinks to their targets. This causes symlinked files inside a directory to be counted toward the preflight budget limits, despite being ignored during snapshot walking. Update the check to ensure symlinks are skipped (e.g. by checking!entry.file_type().is_symlink() && entry.path().is_file()) to match the snapshot walk logic.
Automated code + security review. CI already covers clippy, rustfmt, tests, cargo-audit and commit-lint.
lukehinds
self-requested a review
July 27, 2026 08:29
Member
|
Once the nogent security issues are addressed, we can review and likely merge! |
connrg
approved these changes
Jul 27, 2026
Asserts create_baseline keeps a symlink to a large file within the rollback budget and omits the link from the tracked file set. Fails until the follow-symlink fix lands. Signed-off-by: Kurtis Charnock <kurtis@nolabs.ai>
Excludes symlink entries from walk_and_store and walk_current via the file type WalkDir already lstat'd, and guards symlinked tracked roots. Keeps both walks in agreement so a symlink never surfaces as a spurious Created/Deleted change. Captures a symlink target inside a write grant via the real parent during the crawl. Signed-off-by: Kurtis Charnock <kurtis@nolabs.ai>
Covers symlinks created after the baseline: a link to a file outside the tracked roots, and a dangling link. Both survive restore_to, since walk_current skips symlinks entirely and the cleanup loop only iterates what that walk returned. The dry-run preview misses them for the same reason. Signed-off-by: Kurtis Charnock <kurtis@nolabs.ai>
Walks return files and symlinks separately, and SnapshotManifest records the link paths present at snapshot time. Restore unlinks any link absent from the manifest and leaves the rest alone; a path the manifest holds as a file is left to the restore loop, which replaces it via atomic rename. Link targets are not stored, so a deleted link is not recreated. Cleanup validates the restore target first, so a parent component swapped for a symlink cannot redirect the unlink outside the tracked root. The manifest field defaults on load, so sessions written before this change still open. Signed-off-by: Kurtis Charnock <kurtis@nolabs.ai>
exists, is_dir, and is_file all follow links, so preflight traversed symlinked tracked roots, reported symlinked children as heavy directories, and counted links toward the size threshold. The last of those is the reproducer from the linked issue: a directory of redundant links no longer aborts the walk, but still gets offered for exclusion. An is_symlink helper guards each check, matching the snapshot walk. exceeds_file_threshold takes the limit as an argument so the counting logic can be covered without creating 10,001 files. Signed-off-by: Kurtis Charnock <kurtis@nolabs.ai>
kurtisc
force-pushed
the
kurtisc/symlink-rep
branch
from
July 27, 2026 14:00
c7c0d2f to
22b97f3
Compare
Member
|
/nogent review |
1 task
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.
Linked Issue
Closes #1480
Summary
Skips symlinks when walking snapshots to avoid including the size erroneously in budgets.
Test Plan
Steps to reproduce
Red/green test added
Checklist
CHANGELOG.mdif needed