fix: harden cache migration symlink checks#232
Merged
Conversation
This was referenced May 24, 2026
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
Follow-up hardening for merged PR #230. The cache migration now refuses a symlinked legacy cache root before composing
records/energy-wal.binchild paths, and it treats any destination filesystem entry, including a dangling symlink, as occupied so the no-overwrite migration invariant holds.Target PR reviewed
Linked Original Issues Reviewed
Review Findings
~/.cache/all-smi/recordsand~/.cache/all-smi/energy-wal.bin, but not at the legacy root~/.cache/all-smiitself. A symlinked legacy root could therefore be followed while resolving child migration targets, which conflicts with the threat model documented in PR feat: unify cache paths via platform-aware dirs::cache_dir() (#229) #230 and the users-CSV cache-dir symlink refusal.Path::exists(), which treats a dangling destination symlink as absent. That does not redirect writes, but it weakens the stated "never overwrite destination" invariant because an existing filesystem entry could be replaced.Correctness Fixes
migrate_legacy_cache_root(old_root, new_root)so tests can exercise the root-level decision without mutating process-global home/cache state.symlink_metadata()on the legacy root and return beforecreate_dir_all(new_root)or target joins when the root is a symlink or cannot be inspected.symlink_metadata()for destination occupancy so files, directories, symlinks, and dangling symlinks all preserve the existing destination and skip migration.Security Fixes
~/.cache/all-smisymlink is skipped rather than followed into attacker-controlled paths.Performance And Reliability
Validation
env -u NO_COLOR RUSTFLAGS="-L /tmp/drmlinks" cargo test --bin all-smi cache_migration::tests::-> passed, 7 tests.env -u NO_COLOR RUSTFLAGS="-L /tmp/drmlinks" cargo test --bin all-smi-> passed, 1174 passed, 1 ignored.env -u NO_COLOR RUSTFLAGS="-L /tmp/drmlinks" cargo test --test config_file_integration_test-> passed, 15 tests.RUSTFLAGS="-L /tmp/drmlinks" cargo clippy --bin all-smi --tests -- -D warnings-> passed.cargo fmt --check-> passed.NO_COLOR=1becauseui::renderers::memory_renderer::tests::test_swap_row_active_uses_red_colorintentionally asserts emitted red SGR; unsettingNO_COLORmatches CI behavior and passes.Refs #229
Refs #230