diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-19 10:42:22 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-19 10:42:22 +0200 |
| commit | bca7dabd2b436b8fe8de21736c59437b5ea990af (patch) | |
| tree | 3a8c203dd2af616cf1193f0ddeb3b67550a6a249 /lib/rites | |
| parent | b90678e560808dd788fa7d7eb319d93a83005db4 (diff) | |
| download | colitur-bca7dabd2b436b8fe8de21736c59437b5ea990af.tar.gz colitur-bca7dabd2b436b8fe8de21736c59437b5ea990af.zip | |
fix(cli): publish --prune refuses a manifest entry that escapes --out
CRITICAL: .colitur-manifest lives INSIDE the tree publish writes into --
the very tree this feature exists to have committed into a git repo. A
manifest entry with a ".." path component, or an absolute path, let
--prune Sys.remove/Unix.rmdir a file OUTSIDE --out. No attacker is
required: an ordinary bad merge, a conflict resolved the wrong way, or
a hand-edit of that file is enough to plant such an entry, and publish's
own stated contract -- it never deletes a file it does not own -- broke
outright the moment one was present.
Two independent checks, both required, applied before every deletion:
- structural (manifest_entry_is_safe): reject an entry that is
absolute or has a ".." path COMPONENT, by splitting on '/' and
comparing components, not by substring-matching ".." (which would
wrongly reject a legitimate name like foo..bar).
- containment (resolves_under): resolve both --out and the candidate
with Unix.realpath (closing a symlink-inside-out gap the structural
check alone would miss) and verify the candidate is a genuine path
descendant of --out, not merely a string with the same prefix.
Applied at both the file-deletion loop and prune_empty_dirs' own
directory removals. A rejected entry is skipped with a one-line stderr
warning; publish completes rather than aborting -- a corrupted manifest
must not make the tool itself unusable.
test/cli.t reproduces the exact canary scenario (a ".." entry
surviving deletion of a file outside --out), an absolute-path entry,
and a legitimate dotted filename (no .. component) still pruning
normally, alongside the existing --prune coverage.
Diffstat (limited to 'lib/rites')
0 files changed, 0 insertions, 0 deletions
