Hi actions maintainers,
packages/tool-cache/scripts/externals/7zdec.exe has been committed as a binary blob in this repository for several years. It's used by tc.extract7z on Windows to decompress .7z archives downloaded by setup-* actions. The binary itself is a public-domain helper from the LZMA SDK, so licensing isn't the concern, provenance is.
The current state, as a downstream consumer sees it:
- No record in the repo of which LZMA SDK release the binary came from.
- No
SHA256SUMS or detached signature alongside it.
- No SLSA build provenance attestation.
- No automated update path;
git log shows it has been touched only a handful of times since 2019.
- Upstream 7-Zip is itself unsigned (no Authenticode, no PGP), so there is no publisher-rooted integrity primitive available to verify it against.
The net effect is that every action depending on @actions/tool-cache ships an opaque ~40 KB Windows executable whose origin can't be audited. This isn't hypothetical for us: we run into it on the Apache Software Foundation side, where apache/infrastructure-actions allowlists every action SHA used by ASF projects and runs a verify-action-build step that rejects in-tree binaries lacking provenance. The check fires on every action that transitively depends on @actions/tool-cache (e.g. apache/infrastructure-actions#857 for manusa/actions-setup-minikube):
✗ node_modules/@actions/tool-cache/scripts/externals/7zdec.exe: no SLSA attestation and release vX.Y.Z has no SHA256SUMS
We're approving these PRs case by case because the binary isn't introduced by the bumped action, but the underlying gap is structural and lives here.
Proposal
Split 7zdec.exe out of this repository into its own platform package, with a verifiable build pipeline:
- New package
@actions/tool-cache-7zdec-win32-x64 (or similar) with "os": ["win32"], "cpu": ["x64"], containing only 7zdec.exe and a one-line entry point that resolves the path.
- Build it in CI in this repository. The release workflow fetches the LZMA SDK release from
https://github.com/ip7z/7zip/releases/... against a SHA-256 pinned in a small 7zdec.lock.json checked into the package source, verifies the archive hash, extracts 7zdec.exe, verifies the extracted file's hash, and packs the tarball. Updating the binary becomes a reviewable PR that bumps the lockfile.
- Publish with
npm publish --provenance so the npm registry attaches SLSA build provenance binding the tarball to this repo's CI run.
- Reference from
@actions/tool-cache via optionalDependencies. The runtime code does require.resolve('@actions/tool-cache-7zdec-win32-x64/bin/7zdec.exe') when on win32 + x64; on other platforms or when absent, fall through to the existing behaviour (or invoke a system 7z/7zz if present).
- Remove the committed binary from this repo.
The end-to-end chain a consumer can verify becomes: upstream LZMA SDK release → pinned SHA-256 in this repo → CI build → SLSA attestation on npm → SRI hash in the consumer's package-lock.json. Each link is auditable. Every other action that needs 7z extraction on Windows benefits transparently, without any changes on their side beyond a normal npm update.
A possible alternative worth mentioning: switch tc.extract7z to a WASM LZMA decoder (e.g. 7z-wasm) and drop the native binary entirely. This eliminates the Windows-specific path but changes runtime characteristics and pulls in a different dependency surface, so it's a larger design conversation. The platform-package route preserves current behaviour and is purely additive on the consumer side.
Happy to send the PR if there's interest in either direction: let me know which you'd prefer.
Thanks,
Piotr
Hi
actionsmaintainers,packages/tool-cache/scripts/externals/7zdec.exehas been committed as a binary blob in this repository for several years. It's used bytc.extract7zon Windows to decompress.7zarchives downloaded bysetup-*actions. The binary itself is a public-domain helper from the LZMA SDK, so licensing isn't the concern, provenance is.The current state, as a downstream consumer sees it:
SHA256SUMSor detached signature alongside it.git logshows it has been touched only a handful of times since 2019.The net effect is that every action depending on
@actions/tool-cacheships an opaque ~40 KB Windows executable whose origin can't be audited. This isn't hypothetical for us: we run into it on the Apache Software Foundation side, whereapache/infrastructure-actionsallowlists every action SHA used by ASF projects and runs averify-action-buildstep that rejects in-tree binaries lacking provenance. The check fires on every action that transitively depends on@actions/tool-cache(e.g.apache/infrastructure-actions#857formanusa/actions-setup-minikube):We're approving these PRs case by case because the binary isn't introduced by the bumped action, but the underlying gap is structural and lives here.
Proposal
Split
7zdec.exeout of this repository into its own platform package, with a verifiable build pipeline:@actions/tool-cache-7zdec-win32-x64(or similar) with"os": ["win32"],"cpu": ["x64"], containing only7zdec.exeand a one-line entry point that resolves the path.https://github.com/ip7z/7zip/releases/...against a SHA-256 pinned in a small7zdec.lock.jsonchecked into the package source, verifies the archive hash, extracts7zdec.exe, verifies the extracted file's hash, and packs the tarball. Updating the binary becomes a reviewable PR that bumps the lockfile.npm publish --provenanceso the npm registry attaches SLSA build provenance binding the tarball to this repo's CI run.@actions/tool-cacheviaoptionalDependencies. The runtime code doesrequire.resolve('@actions/tool-cache-7zdec-win32-x64/bin/7zdec.exe')when onwin32+x64; on other platforms or when absent, fall through to the existing behaviour (or invoke a system7z/7zzif present).The end-to-end chain a consumer can verify becomes: upstream LZMA SDK release → pinned SHA-256 in this repo → CI build → SLSA attestation on npm → SRI hash in the consumer's
package-lock.json. Each link is auditable. Every other action that needs 7z extraction on Windows benefits transparently, without any changes on their side beyond a normalnpm update.A possible alternative worth mentioning: switch
tc.extract7zto a WASM LZMA decoder (e.g.7z-wasm) and drop the native binary entirely. This eliminates the Windows-specific path but changes runtime characteristics and pulls in a different dependency surface, so it's a larger design conversation. The platform-package route preserves current behaviour and is purely additive on the consumer side.Happy to send the PR if there's interest in either direction: let me know which you'd prefer.
Thanks,
Piotr