fix: prevent infinite recursion from symlink cycles in workspace file…#1149
fix: prevent infinite recursion from symlink cycles in workspace file…#1149jasonjcwu wants to merge 2 commits intonesquena:masterfrom
Conversation
… tree When workspace contains symlinks pointing outside the workspace root (e.g. ln -s /root/obsidian ~/workspace/obsidian), the directory listing could recurse infinitely if the symlink target contains a symlink back to the workspace or its ancestors. The original cycle detection only ran when the symlink target was inside the workspace root, missing the common case of external symlinks. Changes: - Move cycle detection outside the workspace-relative check so it runs for all symlinks regardless of target location - Skip symlinks that point to the workspace root, the current directory, or any ancestor of the current directory - Properly type symlink entries with 'type: symlink' and 'is_dir' field so the frontend can render them correctly (folder icon + expand arrow) - Maintain workspace-relative paths for entries inside symlink targets so navigation continues to work across the workspace boundary
|
Thanks for this fix, @fxd-jason! Symlink cycle detection in workspace file trees is a real footgun — this is a good catch. The approach is correct:
File changed:
Suggestions for the test plan: Overall this looks like a solid fix for a real crash-inducing edge case. The scope is appropriately narrow — only |
7 test cases covering: - External symlink dirs listed with correct type/is_dir/target - Browsing into external symlink dirs via workspace-relative path - Self-referencing symlinks filtered (ln -s . link) - Ancestor symlinks filtered (ln -s .. link) - Cycle symlinks inside symlink targets filtered - Symlink file entries with size - Path traversal still blocked with symlink support
|
The follow-up test commit is a great addition! The 7-case test suite in
...addresses exactly the regression-proof coverage that was missing in the initial fix. This makes the cycle detection behavior locked against future regressions, which is important given how easy it is to accidentally revert ordering-sensitive detection logic. The PR now covers both the fix and meaningful test coverage. Flagging for maintainer review and merge. 🙏 |
Merged as v0.50.227. 2634 tests passing, browser QA 21/21 (desktop + mobile). Full attribution below. Thanks to all 12 contributors: @jundev0001 (#1138), @franksong2702 (#1142, #1157, #1162), @dso2ng (#1143), @bergeouss (#1145, #1146, #1156, #1159), @jasonjcwu (#1149), @ccqqlo (#1161), @frap129 (#1165) Two fixes applied during integration and two more by the independent reviewer (@nesquena): - messages.js: per-turn cost delta capture order (#1159) - workspace.py: symlink target blocked-roots check + HOME sanity guard (#1149, #1165) - panels.js: cron unread counter bookkeeping (in-loop increment bug) - tests/test_symlink_cycle_detection.py: register workspace before session/new
|
Merged as v0.50.227 via batch PR #1168. Thank you @jasonjcwu! 🎉 |
Merged as v0.50.227. 2634 tests passing, browser QA 21/21 (desktop + mobile). Full attribution below. Thanks to all 12 contributors: @jundev0001 (nesquena#1138), @franksong2702 (nesquena#1142, nesquena#1157, nesquena#1162), @dso2ng (nesquena#1143), @bergeouss (nesquena#1145, nesquena#1146, nesquena#1156, nesquena#1159), @jasonjcwu (nesquena#1149), @ccqqlo (nesquena#1161), @frap129 (nesquena#1165) Two fixes applied during integration and two more by the independent reviewer (@nesquena): - messages.js: per-turn cost delta capture order (nesquena#1159) - workspace.py: symlink target blocked-roots check + HOME sanity guard (nesquena#1149, nesquena#1165) - panels.js: cron unread counter bookkeeping (in-loop increment bug) - tests/test_symlink_cycle_detection.py: register workspace before session/new
… tree
When workspace contains symlinks pointing outside the workspace root (e.g. ln -s /root/obsidian ~/workspace/obsidian), the directory listing could recurse infinitely if the symlink target contains a symlink back to the workspace or its ancestors.
The original cycle detection only ran when the symlink target was inside the workspace root, missing the common case of external symlinks.
Changes: