actions / actions/toolkit

Ship `7zdec.exe` as build-provenanced npm package instead of vendoring it

Open
#2,413 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
5.9k
Forks
1.8k
PR merge metrics
No merged PRs in 30d

Description

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:

  1. 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.
  2. 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.
  3. Publish with npm publish --provenance so the npm registry attaches SLSA build provenance binding the tarball to this repo's CI run.
  4. 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).
  5. 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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/tool-cache/scripts/externals/7zdec.exe and the tc.extract7z entry point, then review the package and release CI changes needed for the proposed lockfile and platform package. The maintainer must first choose between the platform-package and WASM alternatives. Done means the selected path provides verifiable provenance, preserves Windows extraction and fallback behavior, and removes the committed binary.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, node.js, typescript
Domain
build-system, release, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.