Relationships
#2707 Extension archives have no size cap: cap the compressed and decompressed size on pull and push
Opened by hammz · 9/29/2026· Shipped 9/29/2026
Summary
Extension archives have no size limit on the client. listTarGzEntries and extractTarGz (src/infrastructure/archive/tar_archive.ts:303, :124) gunzip without any byte limit. There is also no constant for the largest archive a pull will accept or a push will send; the nearest limit is the safety analyzer's 10 MB per-source limit. A small archive that expands to gigabytes (a gzip bomb) fills the temp dir during extension pull / install, the auto-resolver, or serve's extension.install.
This is unit U2 of the swamp-club#2612 split (see the planning ripples there). It affects every repo, has no dependencies, and ships on its own. #2612 needs it before archives are read back from the config tier, where a tier writer, not the registry, supplies the bytes.
Scope
- Constants in one place:
MAX_EXTENSION_ARCHIVE_BYTES(compressed) andMAX_EXTENSION_ARCHIVE_DECOMPRESSED_BYTES. Set the compressed cap to at least the registry's publish limit (confirm that value on the server) so every published archive still installs. - Decompressed-size cap on both tar passes: a byte-counting stream placed after
DecompressionStream, exposed as an option onlistTarGzEntriesandextractTarGz. The listing pass and the extract pass each count separately; the cap applies to each. Exceeding it aborts with aUserErrornaming the extension and the limit, and leaves nothing extracted outside the temp dir. - Compressed cap on pull: refuse archive bytes over
MAX_EXTENSION_ARCHIVE_BYTESbefore writing them to the temp dir (pull.ts~926). Where the download is streamed, count while reading instead of buffering first. - Push checks the same compressed constant (
src/libswamp/extensions/push.ts) so an author gets the error at push time, and the two limits cannot drift. - File handles at
pull.ts:930and:945. The #2612 triage flagged these as never closed.FsFile.readablecloses on stream end or cancel, so first confirm whether an error path (e.g. a gunzip failure, or the size cap aborting) leaves a handle open. Close explicitly (using/finally) only if one does.
Tests
- Unit: a crafted gzip that expands past the cap is rejected by both
listTarGzEntriesandextractTarGz, with nothing written outside the extract root; an archive exactly at the cap succeeds. - Unit: pull refuses oversize compressed bytes; push refuses the same.
- Property (fast-check): for random archives under the cap, capped and uncapped extraction produce identical trees.
- If a handle leak is confirmed, a test that fails without the fix.
Docs
design/primitives/extensions.md: the archive limits, alongside the existing integrity-verification section.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.