How to zip encryption detection in developer workflows
- Step 1Use it during code review — When a PR adds or changes a
.zipartifact, download the file and drop it into /archive-tools/encrypted-archive-detector. You get the per-entry encryption verdict without checking out the branch or rebuilding. - Step 2Gate a release on the JSON report — Download
<name>-encryption.jsonand assert on it:jq -e '.encryptionSummary["ZipCrypto (legacy)"] == 0 and .encryptionSummary.Unencrypted == 0' report.json. A non-zero count fails the gate. - Step 3Verify a candidate password separately — The detector confirms the algorithm, not the password. To check that a known password actually decrypts an entry, use archive-password-tester, which tests against the first encrypted entry.
- Step 4Re-encrypt weak artifacts — If the report shows ZipCrypto, rebuild the artifact with AES-256 via encrypted-zip-creator and re-run the detector to confirm an all-AES-256 summary before merging.
- Step 5Add a content manifest for supply-chain attestation — Run checksum-generator to emit a SHA-256 manifest of the entries alongside the encryption report — together they attest both how the artifact is protected and what it contains.
- Step 6Confirm the container first when in doubt — The detector is ZIP-only and throws on non-ZIP inputs. If a build produces a
.tar.gzor.7z, run auto-format-detector — this detector won't classify their encryption.
Report fields a pipeline can assert on
The downloaded JSON is stable, so these paths are safe to gate on with jq. Counts are integers.
| JSON path | Meaning | Example gate |
|---|---|---|
encryptionSummary["ZipCrypto (legacy)"] | Count of broken-scheme entries | must equal 0 |
encryptionSummary.Unencrypted | Count of plaintext entries | must equal 0 for a 'fully secured' artifact |
encryptionSummary["AES-256"] | Count of AES-256 entries | should equal totalEntries for AES-256-only |
totalEntries | Number of central-directory entries | sanity-check against expected file count |
entries[].method | Per-entry verdict (ZipCrypto/AES-256/None…) | list which file is weak |
entries[].encrypted | Per-entry boolean | find unencrypted leaks |
Where the detector fits vs other archive tools
The detector reads encryption posture. These siblings cover the adjacent jobs in a release pipeline.
| Need | Tool | What it does |
|---|---|---|
| Is it ZipCrypto or AES? | encrypted-archive-detector | Per-entry encryption-type report (this tool) |
| Does this password work? | archive-password-tester | Tests a candidate against the first encrypted entry |
| Build an AES-256 ZIP | encrypted-zip-creator | Creates an AES-256 password-encrypted ZIP |
| Are entries intact (CRC)? | archive-integrity-tester | Verifies per-entry CRC32 (password optional for AES) |
| Content manifest | checksum-generator | Per-file SHA-256/-1/MD5 CSV + sums file |
| What changed between builds? | archive-diff | Added/removed/changed entries between two ZIPs |
Cookbook
Developer-shaped checks. The JSON shown is the real downloaded report; shell snippets show how to gate on it.
Release gate: fail on any ZipCrypto entry
After building the release ZIP, run the detector, download the report, and fail the release if any entry is ZipCrypto or unencrypted.
# release-encryption.json downloaded from the detector
jq -e '.encryptionSummary["ZipCrypto (legacy)"] == 0
and .encryptionSummary.Unencrypted == 0' \
release-encryption.json \
&& echo 'PASS: artifact is fully AES-encrypted' \
|| { echo 'FAIL: weak or plaintext entries'; exit 1; }Review a PR artifact without checking out the branch
A PR adds dist/bundle.zip. Download just that file from the PR and drop it into the detector — no clone, no build.
Report (bundle-encryption.json):
{
"totalEntries": 6,
"encryptionSummary": {
"AES-256": 6, "ZipCrypto (legacy)": 0, "Unencrypted": 0
}
}
Review note: 'Artifact is AES-256 across all 6 entries — approved.'Catch a build that silently fell back to ZipCrypto
A library upgrade changed the default ZIP encryption. The detector flags the regression the moment the artifact is built.
Report after upgrade:
{
"encryptionSummary": {
"AES-256": 0, "ZipCrypto (legacy)": 6, "Unencrypted": 0
}
}
→ Build now emits ZipCrypto. Pin the encryption option or
re-encrypt via /archive-tools/encrypted-zip-creator.Detect + manifest for an attestation bundle
Combine the encryption report with a SHA-256 manifest so your release notes attest both protection scheme and contents.
1) encrypted-archive-detector → release-encryption.json
AES-256 = 12, others 0
2) checksum-generator (SHA-256) → release-sha256.csv
name,size,sha256
app.bin,5242880,3b1f...c9
...
Attach both to the GitHub release as attestation.Pre-flight before sharing a customer deliverable
Before emailing an encrypted deliverable, confirm AES-256 with the detector, then confirm the password actually works with the password tester.
Step 1 — detector verdict:
"encryptionSummary": { "AES-256": 3, "ZipCrypto (legacy)": 0 }
Step 2 — password tester (/archive-tools/archive-password-tester):
password: hunter2-ENT-2026 → verdict: correct
Both green → safe to send.Edge cases and what actually happens
No server API to call from CI
By designAll archive tools run in the browser/runner — there's no POST /run endpoint for the detector. The supported automation pattern is to produce the JSON report and gate on it with jq. For fully headless pipelines, the @jadapps/runner executes archive tools in a short-lived headless browser on paid tiers; otherwise treat the JSON gate as a manual release step.
Build silently fell back to ZipCrypto
CaughtA dependency upgrade or changed default can switch a build from AES to ZipCrypto without any error. The detector makes the regression visible immediately — a release gate asserting encryptionSummary["ZipCrypto (legacy)"] == 0 fails the build before it ships.
Artifact is a .tar.gz or .7z, not a ZIP
Not a valid ZIP archiveThe detector parses the ZIP central directory only. A .tar.gz has no EOCD record and a .7z uses a different layout, so the tool throws Not a valid ZIP archive. Run auto-format-detector to confirm; this tool can't classify non-ZIP encryption.
Mixed AES + ZipCrypto in one artifact
FailEncryption is per-entry, so a build can emit some AES and some ZipCrypto entries. The summary shows both counts non-zero. Gate on the ZipCrypto count being zero, not on 'AES-256 > 0', or a mixed artifact slips through.
Plaintext entry in a 'secured' artifact
FailA single unencrypted file (encrypted: false, method: None, counted under Unencrypted) leaks data even though the archive 'has encryption.' Add encryptionSummary.Unencrypted == 0 to your gate to catch it.
AES-256 confirmed but password untested
PartialThe detector verifies the scheme, not that your stored password decrypts the file. After a green encryption verdict, run archive-password-tester to confirm the password is correct before shipping the deliverable.
Artifact exceeds the tier size cap
Tier limit exceededFree caps at 50 MB, Pro at 500 MB, Pro+Media/Developer at 2 GB. Large release bundles need a paid tier. The whole file is read into memory to scan the central directory, so very large artifacts are also RAM-heavy in the browser.
Deterministic, but not order-sensitive
ExpectedRe-running on the same ZIP yields the same counts. The entries array follows central-directory order, so two functionally-identical archives built in a different file order produce the same summary but a differently-ordered entries list — assert on encryptionSummary, not on array index.
Frequently asked questions
Is there an API I can call from CI?
No — archive tools have no server-side endpoint; they run in the browser/runner. The supported pattern is to run the detector, download the stable JSON report, and gate on it with jq. On paid tiers the @jadapps/runner executes archive tools in a headless browser, but there's no public REST endpoint for this tool.
How do I fail a release on ZipCrypto?
Assert on the report: jq -e '.encryptionSummary["ZipCrypto (legacy)"] == 0' release-encryption.json. Add and .encryptionSummary.Unencrypted == 0 to also catch plaintext entries. A non-zero count returns a non-zero exit code, failing the step.
Does the detector confirm my password works?
No. It verifies the encryption algorithm from plaintext metadata without decrypting. To confirm a candidate password actually decrypts an entry, use /archive-tools/archive-password-tester, which tries the password against the first encrypted entry.
Can I run it on a .tar.gz or .7z build artifact?
No — it's ZIP-only and throws Not a valid ZIP archive. on those. TAR.GZ has no ZIP central directory and 7z uses a different encryption layout. Run /archive-tools/auto-format-detector to confirm the container; this tool can't classify non-ZIP encryption.
Why gate on the ZipCrypto count instead of AES-256 > 0?
Because encryption is per-entry, an artifact can have both AES-256 and ZipCrypto entries — so AES-256 > 0 would pass a mixed, partly-broken archive. Gating on ZipCrypto (legacy) == 0 (and Unencrypted == 0) is the correct, strict check.
Is the report deterministic across machines?
Yes. It reads fixed central-directory metadata, so the same ZIP always yields the same counts. The entries array follows central-directory order; if two builds differ only in file ordering, assert on encryptionSummary rather than array positions.
Does running it leak my artifact?
No. The file is read in the browser via file.arrayBuffer() and never uploaded. You can vet artifacts from untrusted PRs without writing them to a shared build server — the bytes stay in one ephemeral tab.
How do I produce a supply-chain attestation?
Pair the detector with /archive-tools/checksum-generator. The detector's report attests the encryption scheme; the checksum tool's SHA-256 CSV + sums file attests the contents. Attach both to the release as a combined record.
What if the build fell back to ZipCrypto after a dependency upgrade?
That's exactly the regression the detector catches. A changed default in a zipping library can silently switch to ZipCrypto. A release gate asserting zero ZipCrypto entries fails the build, so you pin the encryption option or re-encrypt with /archive-tools/encrypted-zip-creator before shipping.
How large an artifact can I check?
Free: 50 MB / 500 entries. Pro: 500 MB / 50,000 entries. Pro+Media and Developer: 2 GB / 500,000 entries. Enterprise: unlimited. Oversized files are blocked before processing with a tier-limit error.
Can it run offline in a sandboxed build box?
Yes, once the page is loaded — detection is pure client-side ZIP parsing with no network calls. That makes it usable in network-restricted or air-gapped build environments where outbound requests are blocked.
Does it need any configuration?
No. The tool has no options — no password field, no algorithm picker. Drop the ZIP and read the report. That zero-config surface is intentional: there's nothing to misconfigure in a pipeline gate.
Privacy first
Every JAD Archive tool runs entirely in your browser using fflate, @zip.js/zip.js, and the libarchive WASM bridge. Your archives never leave your device — verified by zero outbound network requests during processing.