Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/verified-confined-restore"
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?
What changed
Restore already confines archive members to the requested
path:roots (#3) and verifies the sha256 sidecar before extracting (#2). This closes the remaining gaps in the same pre-extraction pass, so a poisoned cache object cannot slip past it through a payload that is in-bounds but still hostile.tar -tvPlisting before any byte is written, so a single object cannot exhaust the runner's disk (decompression bomb) or inodes (entry flood). Defaults are generous sanity ceilings, tunable withRASTER_CACHE_MAX_BYTESandRASTER_CACHE_MAX_ENTRIES.Digest behavior is unchanged and already matches the intended policy: a present-but-wrong digest always fails the restore, while a legacy archive with no sidecar restores with a warning unless
RASTER_CACHE_REQUIRE_DIGESTis set. Flipping missing-digest to fail-by-default would brick existing digest-less caches, so it stays opt-in.How it was tested
make lintclean (actionlint/yq/shellcheck absent locally and skipped)make testgreen (84 tests, up from 78)CHANGELOG updated? yes
Closes #2
Closes #3
Non-author review: APPROVE (do not self-merge)
Reviewed
feat/verified-confined-restore@601b7d7. CI is green (test/unit, test/e2e, test/e2e-post-save all success). Real diff issrc/lib/tar.js+tests/restore-confine.test.js(plus CHANGELOG/README/SECURITY). I ran the suite locally: 84/84 pass.1. Special-file reject + size/count caps confine and refuse hostile archives; benign still restores
All four vetting passes run on the
tar -tvPlisting inextractArchive(src/lib/tar.js:262-265) before a single byte is written, first failure aborts:assertNoSpecialFiles(tar.js:192-200) rejects char/block device, fifo, socket (c/b/p/s); files, dirs, symlinks, hardlinks pass.assertWithinCaps(tar.js:236-245) caps entry count and summed declared size, both read from the listing pre-extraction, so an inode flood or a decompression bomb is refused before writing. Defaults are generous (1e6 entries / 100 GiB), overridable viaRASTER_CACHE_MAX_ENTRIES/RASTER_CACHE_MAX_BYTES; an unset/invalid value falls back to the safe default (capFromEnv,tar.js:213-217).assertConfined(absolute-path /../ escaping-link refusal from #3/#6) is still invoked.The size column is parsed by widening
ENTRY_REwith a capture group andparseSize(tar.js:101-105), which folds a device node'smajor,minorto 0 (those are rejected anyway). Tests prove refusal AND that nothing is written: fifo end-to-end (restore-confine.test.js:167), over-cap viaextractArchiveasserting!existsSync(:218), plus the kept poisoned-path/escaping-link tests (:74/:88/:106/:145). Benign path verified bynormal archive restores its files under the requested root(:61).2. (CRITICAL) Digest policy is correct and was NOT flipped
This PR touches no digest/restore source (only
tests/restore-confine.test.jsamong that set), so the #7 policy is inherited unchanged. Confirmed inverifyDigest(src/lib/restore-impl.js:134-158)::138-143).core.warningand restore proceeds (:153-157), so legacy digest-less caches still restore.requireDigestthrows (:147-152), and the default isfalse(src/lib/config.js:145,bool(RASTER_CACHE_REQUIRE_DIGEST, false)).Missing-digest is still warn-by-default, not fail-by-default. Covered by tests: tampered refused + nothing extracted (
restore-digest.test.js:116), missing warns by default (:145), missing +RASTER_CACHE_REQUIRE_DIGESTrefuses (:172).3. No regression to normal save/restore
The new checks only reject hostile archives; a normal archive passes all four passes and the round-trip + benign tests are green (84/84 local, CI green).
extractArchivekeeps its signature withcapsdefaulted, so existing callers are unaffected.4. Tests are real (poisoned / over-cap / mismatch / missing)
Behavioral, not happy-path: they build real archives (a real
mkfifo, a real over-cap tar) and assert both the refusal and that nothing landed on disk; the digest tests cover tamper, missing-warn, and missing-strict.Non-blocking note
The byte/inode caps bound what a restore can write; listing a poisoned object still streams the full decompression through
tar -tvto enumerate (CPU/time, no disk). That is the pre-existing listing approach from #3/#7, not introduced here, and the new caps correctly protect the write side (disk + inodes). Worth a future ceiling on listing work if compressed-bomb CPU ever matters, but not in scope for this PR.Verdict: APPROVE. All four properties verified against the code and the test suite. Leaving the merge to you.