0.3.6 - 2026-08-27
Release Notes
Everything here follows excelano/slipcase's reader-side hardening of the same
day, which added four requirements to SPEC §3 and a Security Considerations
section as SPEC §6. Two of the four this library already satisfied and now
proves it does; the other two are new work.
Security
-
A hostile metadata member no longer costs whatever it likes. Identifying a
container means decompressing the metadata member and parsing it as TOML, so a
reader spends the memory before it knows whether the file was a container at
all — which is not the position of a general ZIP consumer, who chooses what to
extract. Measured before the fix: a 204,151-byte container whose metadata
member deflates at a little over a thousand to one cost 620 MB resident here
and 1,020 MB in the desktop viewer, took 0.61 seconds, and was reported
conformant. At 1,019,488 bytes it was 5,019 MB and 5.1 seconds.Limitsbounds it, defaulting to 16 MiB, and the four entry points that read
a metadata member gained a_withtwin that takes one:Container::read_with,
Container::open_with,validate_withandmetadata_of_with. The same two
containers now cost 11 MB and 0.00 seconds. A container over the bound is
Verdict::Undetermined, neverNonConformant, because the bound belongs to
the reader: answering non-conformant would publish this build's configuration
as a property of somebody else's file, and two readers with different bounds
would then disagree about conformance.The size a central directory records is not the bound and cannot be. Measured
againstzip8.6: a directory rewritten to declare 100 bytes for that same
member still inflated the whole of it and still cost 621 MB, because nothing
in ZIP checks the two against each other. It is read first because it refuses
the ordinary case without spending anything, and the guarantee is the bound on
the bytes as they arrive.The parse-depth half of SPEC §6 is
toml_edit's and is already met. Measured
across nested arrays, nested inline tables, dotted keys and table headers: it
accepts nesting to depth 80 and refuses 81, with an error naming a recursion
limit rather than a stack overflow. -
display_nameescapes the Unicode bidirectional formatting characters.
SPEC §2.3 permits them inpayload.fileand DESIGN says why — they are legal
on every filesystem and a writer may hold a file that genuinely has one — so
SPEC §3 puts the rule on the display, where the problem is. A payload called
report<U+202E>fdp.exereads asreport.pdfwherever the override is applied,
beside whatever offers to open it.slipcase validatenow names the payload through it.slipcase infosplits
the waylsandgitsplit: redirected into a file or a pipe it still
reproduces the member byte for byte, which is what a caller redirecting it
asked for, and onto a terminal it escapes — a terminal being the one place
these characters are applied, and an unterminated override running to the end
of the paragraph rather than the end of the value it sat in.
Added
-
Container::payload_mode— the permission bits the archive records for
the payload, where it records any, with the file-type bits masked off. For
saying and not for applying: SPEC §3 forbids putting an archive's recorded
mode on an extracted file, and this exists so that something can tell a person
a payload was executable where it came from and that their copy will not be.Ok(None)where the container says nothing, which is the common case and the
reason this reads the external attributes off the central directory rather
than asking the ZIP crate. The crate'sunix_modeinvents a mode for an
archive made on DOS —S_IFREG | 0o664, or0o444where the read-only bit is
set — which for its purposes beats nothing and here would mean every container
written by a Windows tool got a confident answer to a question it never
answered.
Changed
RawNamebecameRecordedinternally, carrying the external attributes
alongside the name it was already reading from the same header. Not public.
Fixed
cargo test --all-featuresinside the published crate would not compile,
in 0.3.5 and in this release until it was caught.testsupportis a path
dev-dependency andcargo packagestrips a path dependency it cannot resolve,
so the test files using it referenced a crate the packaged manifest no longer
declared. That is what anybody vendoring or auditing the crate runs.
slpc/tests/provenance.rsandslipcase/tests/cli.rsare now excluded from
their packages rather than shipped broken: the helper they need marks a file
the way a platform's downloader does, and it is shared precisely because two
copies of it disagreed about the Windows arm within an hour, so inlining a
third would undo that on purpose. Those tests need the workspace and stay in
it.
Notes
Two SPEC §3 requirements were already met and are now pinned by tests rather
than by luck. Extraction refuses to replace an existing file through an atomic
no-clobber rename rather than a check before the write, and the new test stands
the race still: the file arrives after Destination::new returned and commit
still refuses. Nothing applies an archive's permission bits, structurally —
Container::payload hands back a reader and Destination takes a path, and the
two never meet — and the new test would catch somebody wiring them together,
which is a two-line change and more tempting now that payload_mode exists.
Install slipcase 0.3.6
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/excelano/slpc-rust/releases/download/v0.3.6/slipcase-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/excelano/slpc-rust/releases/download/v0.3.6/slipcase-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install excelano/tap/slipcaseDownload slipcase 0.3.6
| File | Platform | Checksum |
|---|---|---|
| slipcase-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| slipcase-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| slipcase-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| slipcase-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| slipcase-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |