milestone-prune archive mode follows a pre-existing symlink out of the planning tree #138
Labels
No labels
already-shipped
bug
documentation
duplicate
enhancement
external-review
good first issue
help wanted
in progress
invalid
needs-decision
proposal
question
security
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: crenshawdev/cadence-archived#138
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Source: deep dive F3; strict re-review C-05 (independent convergence)
Verdict: AGREE - low, self-documented residual
Claim as reviewed
The archive containment check is lexical. A pre-existing
_archive-<label>path that is itself a symlink outside the tree still resolves lexically inside it, andrenameSyncthen follows the directory symlink.At HEAD
f354864(v3.3.0)cadence-core/bin/planning.mjs:3536-3547. The guard isresolvePath(archiveRoot).startsWith(resolvePath(dir) + sep), and the comment at:3541-3543states the residual in the source: "a pre-existing_archive-<label>that is itself a symlink out of the tree still resolves inside it."At
:3586-3590archive mode callsmkdirSync(archiveRoot, { recursive: true })- which succeeds silently against an existing symlink-to-directory - thenrenameSync(src, join(archiveRoot, String(n))), which resolves through the link at syscall time.Note
Threat model bounds this hard: it needs a symlink planted inside
.planning/, which requires the write access someone would use to editROADMAP.mddirectly.Fix shape:
lstateach existing path component and refuse symlinks; if the archive root exists require a real directory whose real parent descends from the real planning root; refuse any pre-existing per-phase destination. Cheapest to land alongside the prune transactionality fix.