Repository navigation
security: handle-based destination checks against junction races - #120
Merged
Merged
Conversation
PinnedDirectory opens the destination chain (root to target folder) by handle with FILE_FLAG_OPEN_REPARSE_POINT and without FILE_SHARE_DELETE, verifies each component is not a reparse point and resolves to the expected child, and moves files in with SetFileInformationByHandle (FileRenameInfo) relative to the pinned folder handle. Extraction (DestinationGuard/SafeExtractor) and copy/move (FileOperationService) place every file this way; the root identity is fixed on first use. Adds adversarial Windows integration tests that keep swapping a destination folder for a junction during extraction, copy and move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nextestudios
force-pushed
the
feature/81-handle-based-destination-checks
branch
from
September 27, 2026 03:10
23408cc to
9f60609
Compare
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
PinnedDirectory(Infrastructure.Windows): opens the destination root and every folder down to the target by handle — root following links (it is the user's choice), each component withFILE_FLAG_OPEN_REPARSE_POINT— withoutFILE_SHARE_DELETE, so no folder in the chain can be renamed, deleted or replaced by a junction while held. Each component is verified by handle (attributes: not a reparse point, is a directory;GetFinalPathNameByHandleequals parent's final path + name). Moves into the pinned folder useSetFileInformationByHandle(FileRenameInfo)withRootDirectory= the folder handle (falls back to the pinned folder's full path when the file system rejects relative names). Root identity is fixed on first use, so a swapped root refuses later writes.DestinationGuardnow pins instead of path revalidation;SafeExtractor.PlaceAsyncplaces, keeps both and replaces through the pinned parent. Conflict questions happen unpinned; the chain is pinned again before acting.FileOperationService): the temp file is written and placed with the destination chain pinned; same-volume moves (files and folders), keep-both and replace go throughMoveHere; destination subfolders are created through the pin. Pin failures become per-itemBlocked(link) results.Infrastructure.Archivesnow referencesInfrastructure.Windows(lock files updated). Non-Windows keeps path-based checks (dev/test only).docs/security-model.mdupdated with what is now covered and the remaining limits (root and folders above it, temp files created by path inside the pinned folder, metadata applied after placement, network file systems). Architecture doc and both changelogs updated.Tests (Windows CI)
JunctionRaceTests.Racing_junction_swap_cannot_redirect_extraction_writesand..._copy_or_move_writes(Copy/Move): a thread keeps swappingdest/subfor a junction to an outside folder while 300 files are extracted/copied/moved into it; the outside folder must stay empty.A_pinned_folder_cannot_be_renamed_or_replaced_while_held: deterministic proof that the pinned folder and its parent cannot be renamed while held.A_junction_in_the_chain_is_refused_by_handle.Closes #81
🤖 Generated with Claude Code