Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/2026-07-17-build-archive-bundle-source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Changed
- `build_plugin_archive.py` now sources the activation runtime bundle from an out-of-tree signed handoff via `--bundle-source <leaf .bundle dir>` instead of the git checkout — the signed runtime ships as a release asset and is never committed. The committed `runtime-manifest.json` alone decides the mode (design decision D1): an activation manifest without `--bundle-source` is a hard error (never a silent policy-only downgrade), a policy-only manifest forbids the flag, and an in-tree `runtime/` conflicts with an external source. The bundle source is treated as hostile input (symlink argument rejected before resolution; bounded non-following enumeration; member-set equality; per-member O_NOFOLLOW regular-file/uid/nlink/mode/size/sha256 checks with the strict 0o500 install mode preserved), and its payloads are re-read + re-digested through fresh descriptors at pack time.
- `verify_archive` is rearchitected to **regenerate the canonical tar from trusted inputs and byte-compare** rather than parse a possibly-hostile archive: it never hands candidate tar bytes to `tarfile`. A shared deterministic `USTAR` serializer (used by both build and verify) reproduces the canonical tar with runtime payloads zero-filled; the candidate is only bounded-inflated and then sliced at offsets the verifier computed. Every structural property — headers, exact typeflags, ordering, padding, EOF, dialect, and the absence of hidden PAX/GNU/sparse records — is bound by the byte-compare outside the runtime ranges; each runtime payload is bound to the frozen-manifest digest; the archived `runtime-manifest.json` bytes are bound to the frozen snapshot. Reading the candidate is fd-only (`O_NOFOLLOW`/`O_NONBLOCK` + `fstat`, no path-swap window) under hard compressed- and decompressed-size caps, with a canonical gzip-header check and single-stream (no trailing/concatenated data) validation. The archive is built into an exclusive temp that is verified before an atomic rename, so a failed build never leaves a publishable artifact. Hardened through a 2-round up-front adversarial design review of the hostile-archive-reading threat model (the regenerate-and-compare pivot) before implementation.
Loading
Loading