Skip to content

security: handle-based destination checks against junction races - #120

Merged
nextestudios merged 1 commit into
mainfrom
feature/81-handle-based-destination-checks
Sep 27, 2026
Merged

nextestudios merged 1 commit into
mainfrom
feature/81-handle-based-destination-checks

Conversation

@nextestudios

Copy link
Copy Markdown
Owner

Summary

  • New 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 with FILE_FLAG_OPEN_REPARSE_POINT — without FILE_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; GetFinalPathNameByHandle equals parent's final path + name). Moves into the pinned folder use SetFileInformationByHandle(FileRenameInfo) with RootDirectory = 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.
  • Extraction: DestinationGuard now pins instead of path revalidation; SafeExtractor.PlaceAsync places, keeps both and replaces through the pinned parent. Conflict questions happen unpinned; the chain is pinned again before acting.
  • Copy/move (FileOperationService): the temp file is written and placed with the destination chain pinned; same-volume moves (files and folders), keep-both and replace go through MoveHere; destination subfolders are created through the pin. Pin failures become per-item Blocked (link) results.
  • Infrastructure.Archives now references Infrastructure.Windows (lock files updated). Non-Windows keeps path-based checks (dev/test only).
  • docs/security-model.md updated 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_writes and ..._copy_or_move_writes (Copy/Move): a thread keeps swapping dest/sub for 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.
  • Existing extractor/file-operation unit and integration tests exercise the new placement path on Windows.

Closes #81

🤖 Generated with Claude Code

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
nextestudios force-pushed the feature/81-handle-based-destination-checks branch from 23408cc to 9f60609 Compare September 27, 2026 03:10
@nextestudios
nextestudios merged commit 13f4cff into main Sep 27, 2026
1 check passed
@nextestudios
nextestudios deleted the feature/81-handle-based-destination-checks branch September 27, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: handle-based destination checks against junction races

1 participant