feat(core): harden the Unix file removal against a same-user actor in the quarantine directory (#445)

Closed
QN
3d ago
+858-74

User description

Part of #359 (R-15 Gate 4, slice H1a-2), part 2b of 5, stacked on #1069 (base = its branch). test-support only: no production caller, no production deletion release.

Why a part of its own: the Unix file removal reached 1,306 meaningful lines through its review waves (tier 1,200). The hardening against a same-user actor in the quarantine directory and the durability of the failure paths is a separate proof boundary; it is cut out of #1069 (one revert commit there, no force-push) and re-applied here, so the removal itself stays reviewable at 1,109 lines and this part at 263.

What this adds

  • Durability of failure paths: the parent is synced after a rollback, a stranded entry, a put-back and a vanished name (best effort after a failure, so the sync's own failure does not replace the failure being reported); an entry that stays in the quarantine directory is retained with a best-effort sync of that directory too. The BeforeSync stage is reached, which tests observe.
  • Cleanup bound to what was created: the quarantine directory records its identity; discard removes the name only if it still leads to that directory (at most an empty directory can be removed by a name-based rmdir).
  • Renamed-away quarantine: an entry is reported as retained (Swapped { restored: false }, Stranded) only if the recorded name still leads to its directory; otherwise the outcome is Disturbed (the entry cannot be found by name). Disturbed documentation widened accordingly.
  • Owner-masking umask: a quarantine directory that cannot be opened is refused (PermissionDenied, fail closed), pinned by a test.
  • Stated, not closable: that the directory behind the quarantine name is the one mkdirat made cannot be proved (no descriptor is returned); the quarantine is not exclusive anyway.

Proof

Forced cases: failure at each stage with the sync stage observed after a move and not before it; the put-back blocked by taking the name again (Stranded, whoever runs it, also for root); a quarantine directory renamed away and replaced by an empty one (cleanup leaves it alone); a quarantine renamed away while the put-back is blocked (Disturbed, the entry intact in the renamed directory); a vanished name (sync reached); the permission-based Stranded case skips for root with a message. Local: fmt, clippy -D warnings on Linux / Windows / macOS targets, lib and isolation tests.

Answers the Codex / CodeRabbit / CodeAnt findings on #1069 raised after its first head (sync after rollback, replacement quarantine directory, root, creation race, quarantine sync, vanish sync, owner-masking umask, adopt-error sync, renamed quarantine).

Status: H1A_2_UNIX_HARDENING=IMPLEMENTED_TEST_SUPPORT_ONLY, H1_PROVED=NO, R1_PRODUCTION_RECLAMATION=NOT_ADMITTED, GATE4_TERMINAL=NO.

Summary by Sourcery

Harden the test-only Unix file removal flow against quarantine tampering while preserving durable and truthful failure outcomes.

Bug Fixes:

  • Harden test-only Unix file removal against quarantine-directory tampering and same-user interference.
  • Ensure failed removal paths preserve and accurately report stranded, disturbed, vanished, and unconfirmed outcomes.

Enhancements:

  • Add durability handling for both the parent and quarantine directories across rollback, put-back, stranded, vanished, and removal paths.
  • Restrict quarantine cleanup and retention reporting to the directory and entry identities originally observed.
  • Fail closed when a quarantine directory cannot be opened and document the remaining limits of name-based race protection.

Documentation:

  • Document the quarantine durability, identity checks, tamper outcomes, and remaining Unix race boundary in the Gate 4 evidence journal.
  • Record the test-support hardening in the unreleased changelog.

Tests:

  • Add Unix tests covering sync failures, renamed or replaced quarantine directories, blocked put-backs, vanished entries, cleanup identity checks, and permission failures.

Summary by cubic

Hardens the Unix file removal in test-support against a same-user actor manipulating the quarantine directory, making failure paths durable and reporting tampering clearly. No production caller touches this code.

  • The parent is synced after a rollback, a failed move, a put-back and a vanished name, best effort so a failed sync never replaces the failure reported. The quarantine directory is synced after every move through it; for a put-back, a kept entry, or an entry found gone at the inspection or unlink, a failed sync is an error reported after the parent was synced (not another outcome), for a rollback, a stranded entry and tampering it is best effort, and for an unlink it leaves the removal Removed(NotConfirmed).
  • The quarantine records the identity of the directory it created; discard removes the name only if it still leads to that directory, and the same identity check applies when a failed setup is cleaned up. An entry is reported as retained only if the recorded name still leads there and the entry is still the authenticated object (or the identity a swapped-in object was seen with); a proven absence or a replacement is Disturbed.
  • A quarantine directory the owner cannot open is refused (PermissionDenied, fail closed).
  • Tests force each failure case, including failed syncs through a test-only seam, the quarantine renamed away, the put-back blocked, and the cleanup identity check, so they also run for root. No new unsafe or package.

Summary by CodeRabbit

  • Bug Fixes
    • Unix file removal now checks that the quarantine directory remains unchanged before reporting retained entries or removing the directory.
    • Removal durability is confirmed only when both relevant directories sync successfully. Sync failures after removal are reported as unconfirmed; failures during rollback or when an entry is stranded preserve the corresponding error or outcome.
    • Removal is refused if the quarantine directory cannot be opened.
  • Documentation
    • Updated Unix removal guidance to describe directory-sync requirements and removal outcomes.

CodeAnt-AI Description

Harden test-only Unix file removal against quarantine tampering and sync failures

What Changed

  • Unix file removal now checks that a quarantine name still leads to the directory it created before removing it, and reports entries as retained only when they remain there.
  • Syncs the quarantine directory after entries move through it. A failed sync makes a removal unconfirmed or returns an error after a put-back, without changing where the entry was left.
  • Syncs the parent after rollback, stranded-entry, and vanished-name paths; refuses removal if the quarantine directory cannot be opened. These changes are limited to test-support and have no production caller.

Impact

βœ… Test-only removal leaves replacement quarantine directories untouched
βœ… Tampered or missing quarantine entries are reported as disturbed
βœ… Failed quarantine syncs do not report removals as confirmed

πŸ’‘ Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.