Defer hostile-same-UID credential store TOCTOU to a non-bypassable primitive #1065

Open
opened 2026-08-05 22:49:43 +00:00 by be-coder-06 · 0 comments
Collaborator

Security finding (verbatim)

Severity: MEDIUM
Title: Credential generation check is vulnerable to TOCTOU overwrite
File: packages/mosaic/src/credentials/file-credential-store.ts
Lines: 313–335
CWE: CWE-367
OWASP: A04:2021-Insecure Design

FileCredentialStore.put() verifies expectedGeneration by taking a snapshot, then separately creates and renames the replacement envelope. Another process can change the credential after the generation comparison but before the rename, causing this stale operation to overwrite the newer generation. The lifecycle advisory lock does not close this gap because the documented threat model explicitly permits same-UID processes to replace or bypass its inode while claiming generation/CAS transitions as the same-UID safety boundary. TeaLoginStore uses the same check-then-commit pattern. Consequently, the asserted CAS boundary is not actually atomic and credential rotation or rollback can corrupt or restore stale credential state.

Remediation (verbatim):

Implement the generation transition as an atomic compare-and-swap under a non-bypassable state primitive. Options include storing generations in a transactional database with a conditional UPDATE, using a trusted broker running under a separate identity, or holding a lock whose ownership and inode cannot be replaced by actors included in the threat model. If same-UID adversaries remain explicitly out of scope, remove the claim that generation checks form a same-UID security boundary and document that these checks protect only cooperating processes. Apply the same atomicity guarantee to TeaLoginStore mutations and add a test that pauses between generation validation and rename while a competing writer commits a new generation.

Disposition from MC-CRED-01 / PR #1059

Phase 1 narrows the claim rather than weakening a check:

  • lifecycle flock and generation preconditions protect cooperating mosaic cred mutators;
  • provider authority is the authorization boundary;
  • generation preconditions are optimistic concurrency, not atomic CAS;
  • hostile same-UID direct filesystem mutation is explicitly out of scope and deferred to this issue.

The approved classes of non-bypassable primitive that can close this issue are:

  1. transactional service;
  2. broker / distinct identity;
  3. equivalent non-bypassable primitive.

No broker or new service is authorized in MC-CRED-01. PR #1059 retains regressions proving the claimed cooperative behavior: a competing generation is preserved, stale replacement/removal is rejected, and the lifecycle result becomes rollback-incomplete rather than overstating recovery.

Source advisory evidence: /home/hermes/agent-work/be-coder-06/review-evidence/security-review-remediation-uncommitted-8.json (author-run advisory; independent security review remains required).

## Security finding (verbatim) **Severity:** MEDIUM **Title:** Credential generation check is vulnerable to TOCTOU overwrite **File:** `packages/mosaic/src/credentials/file-credential-store.ts` **Lines:** 313–335 **CWE:** CWE-367 **OWASP:** A04:2021-Insecure Design > FileCredentialStore.put() verifies expectedGeneration by taking a snapshot, then separately creates and renames the replacement envelope. Another process can change the credential after the generation comparison but before the rename, causing this stale operation to overwrite the newer generation. The lifecycle advisory lock does not close this gap because the documented threat model explicitly permits same-UID processes to replace or bypass its inode while claiming generation/CAS transitions as the same-UID safety boundary. TeaLoginStore uses the same check-then-commit pattern. Consequently, the asserted CAS boundary is not actually atomic and credential rotation or rollback can corrupt or restore stale credential state. **Remediation (verbatim):** > Implement the generation transition as an atomic compare-and-swap under a non-bypassable state primitive. Options include storing generations in a transactional database with a conditional UPDATE, using a trusted broker running under a separate identity, or holding a lock whose ownership and inode cannot be replaced by actors included in the threat model. If same-UID adversaries remain explicitly out of scope, remove the claim that generation checks form a same-UID security boundary and document that these checks protect only cooperating processes. Apply the same atomicity guarantee to TeaLoginStore mutations and add a test that pauses between generation validation and rename while a competing writer commits a new generation. ## Disposition from MC-CRED-01 / PR #1059 Phase 1 narrows the claim rather than weakening a check: - lifecycle flock and generation preconditions protect **cooperating `mosaic cred` mutators**; - **provider authority is the authorization boundary**; - generation preconditions are optimistic concurrency, not atomic CAS; - hostile same-UID direct filesystem mutation is explicitly out of scope and deferred to this issue. The approved classes of non-bypassable primitive that can close this issue are: 1. **transactional service**; 2. **broker / distinct identity**; 3. **equivalent non-bypassable primitive**. No broker or new service is authorized in MC-CRED-01. PR #1059 retains regressions proving the claimed cooperative behavior: a competing generation is preserved, stale replacement/removal is rejected, and the lifecycle result becomes `rollback-incomplete` rather than overstating recovery. Source advisory evidence: `/home/hermes/agent-work/be-coder-06/review-evidence/security-review-remediation-uncommitted-8.json` (author-run advisory; independent security review remains required).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1065