Skip to content

Let a scroll expand an uncompressed tarball - #14

Merged
suffro merged 3 commits into
mainfrom
plain-tar-assets
Sep 12, 2026
Merged

Let a scroll expand an uncompressed tarball#14
suffro merged 3 commits into
mainfrom
plain-tar-assets

Conversation

@suffro

@suffro suffro commented Sep 11, 2026

Copy link
Copy Markdown
Owner

What

assetArchives accepted zip or tar.gz only. A publisher who ships a plain .tar could not be consumed at all — and that is the usual choice when every member inside is already compressed, where a second gzip pass costs minutes to save nothing.

tar now joins the enum and goes through the same listing, validation and extraction as tar.gz. Nothing about the safety rules changes: entries are listed and checked before anything is written, and links and special entries are still refused outright.

One thing that turned out not to be true

The read paths passed gzip: true to node-tar, which reads like a guarantee that the archive really is gzipped. It is not one — node-tar reads the header and decompresses or not on its own, and its gzip option applies to writing. Probed directly on all four combinations before deciding:

plain.tar     gzip=false -> OK      packed.tar.gz gzip=false -> OK
plain.tar     gzip=true  -> OK      packed.tar.gz gzip=true  -> OK

So the option is dropped rather than threaded through, and the white paper now says what actually holds the author to their claim: the scroll's tar / tar.gz distinction records what they pinned, and the asset's SHA-256 is what makes it binding.

Tests

Two added to archive-security.test.mjs: a plain tarball expands with stripComponents, and the entry rules apply to it exactly as to a compressed one.

Full suite on Linux: 566 passed, 36 files. On a Windows host 43 tests fail with EPERM: operation not permitted, symlink — pre-existing, unrelated, and true on main too.

Where it came from

A project whose model bundles 1.86 GB of pickled molecule definitions as a plain mols.tar. Third gap this line of work has found by real use, after PyPI licences (1.1.0) and download-then-expand (1.1.1).

🤖 Generated with Claude Code

`assetArchives` accepted `zip` or `tar.gz`, so a publisher who ships a plain
`.tar` could not be consumed at all — and that is the usual choice when every
member inside is already compressed, where a second pass costs minutes to save
nothing. The archive goes through the same listing, validation and extraction as
`tar.gz`; the only thing missing was permission to say so.

Compression is now left to the library on read, which is what it was doing
anyway: node-tar reads the header and decompresses or not, and its `gzip` option
applies to writing. Passing `gzip: true` on a read looked like a guarantee and
was not one. The `tar` / `tar.gz` distinction records what the author pinned, and
the asset's SHA-256 is what holds them to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 11, 2026

Copy link
Copy Markdown

Deploying scrollcase with  Cloudflare Pages  Cloudflare Pages

Latest commit: 64cb078
Status: ✅  Deploy successful!
Preview URL: https://e40a0391.scrollcase.pages.dev
Branch Preview URL: https://plain-tar-assets.scrollcase.pages.dev

View logs

suffro and others added 2 commits September 12, 2026 12:55
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@suffro
suffro merged commit 946ab6e into main Sep 12, 2026
11 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant