Skip to content

Release/v1.1.0 - #14

Merged
Nizoka merged 22 commits into
mainfrom
release/v1.1.0
Oct 19, 2026
Merged

Nizoka merged 22 commits into
mainfrom
release/v1.1.0

Conversation

@Nizoka

@Nizoka Nizoka commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

feat: zipnative-cli 1.1.0 — zipnative 1.1.0 parity and hardening

Description

zipnative-cli 1.1.0 on zipnative 1.1.0 (^1.1.0, still the only runtime dependency). The
release covers three things:

  • Engine parity: 106/106 exports bridged and every one reachable — new flags on the existing
    commands, and two new commands, canonicalize and merge (17 commands).
  • Runtime hardening to the pkinative-cli level.
  • Release integrity at the level of zipnative 1.1.0.

Inputs that 1.0.0 accepted and ignored or misread are now refused. They ship as security fixes
with a migration (CHANGELOG ### Security). Everything else is additive: the envelopes, the
E_* codes, the exit codes and the ZIP_* mapping are unchanged, and the 37 archives of the
1.0.0 veraZIP corpus are byte-identical.

Release notes: release-notes/v1.1.0.md. Decisions:
docs/adr/ (ADRs 0001–0012). The independent reviews and the audit
are summarised below (§Independent audit); the triage of the open PRs and alerts is the table
under §Human follow-ups.

Related Issues

Supersedes #1, #3, #4, #5, #7 (one commit moves every codeql-action step together) and #11
(the engine is bumped by hand). Closes #10 (@types/node majors ignored: the floor is Node 22).
Resolves Dependabot alerts #1, #3 and Scorecard alerts #3, #8; prepares #1 (rulesets as code).
Upstream: closes the CLI's engine asks answered by zipnative 1.1.0
(zipnative#9–#12).

Commits

Commit Scope
ci(deps) codeql-action v4.37.9 on all four steps, scorecard-action v2.4.4
feat(core) 106-export bridge, --zip64, --name-encoding, --timeout, --progress, analyzeDeterminism, --long timestamps / unix ids, stream --codec via createDecompressor, compensations removed, scripts/sync-engine-data.mjs
fix(security) presentation-only config, strict invocation, finite bounds, one-descriptor reads, lstat + wx / temp + rename writes, terminal-safe text, the early-error unlink fix, ADRs 0001–0008
ci(supply-chain) four-job publish (five after the review fixes), scripts/gate.mjs, harden-runner / --ignore-scripts everywhere, zizmor / actionlint / reuse, audit + OSV, dependency review, rulesets, SECURITY-INSIGHTS, security.txt, ADR 0009
test(fuzz) seeded parser fuzzing (one bug found and fixed), stricter tsconfig / ESLint, veraZIP re-port (WF/DESCRIPTOR-WIDTH), corpus 39, docs link / anchor / EOL tests, ADR 0010
chore(release) 1.1.0: CHANGELOG, ROADMAP, release notes, docs refresh, four new samples, typescript-eslint 8.71.1
fix(review) findings of two independent pre-PR reviews (correctness, security), each reproduced first: stream --timeout no longer exits 0 with a truncated file, a timeout in a decompressor is E_LIMIT, config false never turns a switch on, batch accepts the create flags it forwards, one deadline per process, FIFO reads bounded in memory, per-field terminal escaping (LF, invisible characters, remedies), the consumer check moved to a read-only fifth publish job, manifest comment-file anchored
fix(security) (2nd review) strict switch values (--overwrite=flase was ON), bounded batch --task verify, mode-strict batch, process-wide flags refused in tasks, --workers 0–64, prototype-free aliases, extract file/dir conflict and missing --entry refused up front
fix(control) --timeout fires while an input is stalled (stdin, pipes, FIFOs)
fix(contract) schemas match the output (conformance test), typed schema manifest, safety facts in inspect --summary, failing names in verify errors, verify --name-encoding, compact schema under --json, text-mode failure lines
chore(supply-chain) Node security floor `^22.17.1
docs second-review fixes to the contract docs, exact migration notes, ### Security, AI-instruction lists, count pins
feat(core) every export reachable: canonicalize, merge, --range (+ --max-entry-compressed-size), manifest raw entries — 17 commands
fix(review) (counter-verification) canonicalize counts hostile names (unsafeNames, encrypted) and warns, a raw DEFLATE payload refuses trailing bytes, ZIPNATIVE_* values strict, docs
chore(agents) the release-audit Claude Code skill tracked under .claude/skills/, a tracked .claude/settings.json (attribution off, the GitHub and npm writes denied), .audit/ ignored, tests/tools/agent-settings.test.ts
chore(release) the review and triage reports moved out of the tree (.audit/1.1.0/, .github/drafts/), summarised here
fix(audit) the 29 actionable findings of /release-audit (§Independent audit): a bound is never a verdict in verify, a missing file is E_IO, the env-switch envelope, zip64Entries, remedies, CRC shapes, batch rows, the documentation findings
docs(release) the audit verdict and the final figures in this draft
chore(release) the release date 2026-10-20 and the dates derived from it (1.0.x security fixes until 2027-04-20, security.txt Expires 2027-10-20)
fix(audit) (release day) the round-4 findings: batch --task verify parses the bounds once before the pool, the raw ZIPNATIVE_JSON is still judged under --json and the refusal names the command, the instruction files counted by the docs test, the licensing sentence, ADR 0009's five jobs, the batch --dry-run wording
docs(release) (release day) the round-4 verdict, the triage re-read and the 2026-10-20 figures in this draft
test(io) CodeQL js/insecure-temporary-file on the PR: a unit test handed the shared temp root itself to readFileOrStdin; it now uses a private mkdtemp directory and asserts E_IO (no src/ change)

Behaviour changes (reviewer attention)

  1. .zipnativerc.json: only format, fields, json, pretty, quiet, no-color,
    progress, strict, long and summary are allowed; any other key is E_USAGE naming the
    file (ADR 0001).
  2. An undeclared, repeated or surplus flag or operand is E_USAGE (exit 2) before any input is
    read, including for every batch task (ADR 0003).
  3. none / inf on a bound is E_USAGE (ADR 0002).
  4. Text errors read error E_X (ZIP_Y): message (1.0.0 printed the bare message).
  5. A manifest comment-file resolves against the manifest directory (was the current
    directory) and is refused on ..; a manifest task cannot carry timeout.

No security default was loosened. dosTimeMode is deliberately not adopted (ADR 0008).
maxEntryCompressedSize is behind --max-entry-compressed-size (ADR 0011, superseding 0007).

Independent audit

Agent-run, on the local branch, before the maintainer's own pass. Round 1: two independent
pre-PR reviews (correctness, security) of the branch diff. Round 2: two independent reviewers
over seven criteria — A (conformance and product: standards against pkinative-cli and
pdfnative-cli, coverage of the core, the AI instructions, every documented figure, measured
token cost) and B (adversarial: the surface diff from the published 1.0.0, a hardening matrix
against pkinative-cli, attacks on the binary and the workflows) — and a third, separate
verifier that reproduced every finding and ruled it CONFIRMED, REJECTED or DEFER. Round 2b: a
fourth agent re-ran the original scenario of every confirmed finding on the built binary.
Round 3: /release-audit (the family's audit skill, ported in this release: two auditors, an
adversarial verifier, an autonomy pass, a second verifier, a GO/NO-GO ledger) at HEAD.

Round Verdict Confirmed Resolution
1 — every finding reproduced on the built binary (the stream --timeout hang, a decompressor timeout, config false, batch create flags, one deadline per process, FIFO reads, per-field escaping, the consumer job, manifest comment-file) fixed in e2e820e, one regression test per finding (tests/integration/review-fixes-1-1.test.ts, built-binary-smoke.test.ts)
2 Met on all seven criteria 31 findings: 23 confirmed (3 in part), 5 deferred to 1.2 (ROADMAP), 1 rejected, 2 rejected in part; 3 majors — --overwrite=flase was ON, the published schemas rejected real output, --timeout did not fire on a stalled pipe — plus a bounded batch --task verify fixed in aba500a → 0536020, one regression test per finding (tests/integration/review2-1-1.test.ts, tests/commands/schema-conformance.test.ts); the 11 last engine exports exposed (canonicalize, merge, --range, manifest raw)
2b every finding closed, no regression 3 minors: canonicalize carried hostile names silently, a raw DEFLATE payload could hide trailing bytes, docs gaps; nits: ZIPNATIVE_* values strict fixed in 887254f
3 GO /release-audit at 99bf8c3: 238 rows over three reports (A 129, B 63, D 46); verifier 1 over A + B: 13 confirmed, 1 rejected, 2 duplicate, 2 raised; verifier 2 over D: 11 confirmed, 2 downgraded, 1 duplicate, 2 raised — 0 blockers, 5 majors, 23 minors, 2 notes. Majors: verify reported an intact entry over a --max-* bound as a CRC / size failure on every path (the engine's verifiers swallow the refusal — drafted upstream), SECURITY.md denied the ninth bound its flag, the docs said streamed entries > 4 GiB were refused (they take --zip64), a missing file was E_RUNTIME on two paths all 29 actionable rows fixed in b04f15f, one regression test per code finding (tests/integration/audit-1-1.test.ts, core-surfaces-1-1, the built-binary smoke test); the one note needs no action; 0 waived
4 (release day) GO the round-3 fixes re-audited at 9eaecd9: closure 29 / 29 (one observation), the diff 99bf8c3..HEAD re-read hunk by hunk, release-day bookkeeping (versions, dates, no tag, 1.1.0 unpublished, npm audit 0, 10 tarball files, the five engine drafts), the triage re-read through gh api, and a 96-row 2026 standards sweep (Scorecard, OSPS Baseline, SLSA, Keep a Changelog, Conventional Commits, clig.dev, llms.txt, REUSE, RFC 9116, CITATION.cff, npm metadata, the ten native proofs); verifier 3: 13 confirmed, 2 downgraded, 0 rejected, 1 raised — 0 blockers, 2 majors (a b04f15f regression: a malformed --max-* on batch --task verify became a per-row E_USAGE with exit 1 instead of the exit-2 refusal, with and without --dry-run), 7 minors (the raw ZIPNATIVE_JSON shadowed by --json, command: null in that envelope, the instruction files' "15 commands", the inbound = outbound sentence, ADR 0009 "four jobs", the batch --dry-run wording, the triage delta), 7 notes the 9 actionable rows fixed in 64089ac with their regression tests (audit-1-1.test.ts, the built-binary smoke test, COUNTED_DOCS now counts the .github/ instruction files); the notes are ROADMAP 1.2 items or pre-existing; 0 waived

The seven criteria after round 2b:

# Criterion Verdict Evidence
1 2026 state of the art, native philosophy met; family hygiene deferred attested five-job release, SBOMs, harden-runner, CodeQL, zizmor, actionlint, reuse, OSV, rulesets as code, SECURITY-INSIGHTS, security.txt, a Node security floor, an offline gate step; docs.yml, a Deno/Bun job, THREAT_MODEL.md are 1.2
2 No breaking change unless justified met no undocumented break in the surface diff; every narrowing under CHANGELOG ### Security with its migration
3 Full core coverage met 106 / 106 exports reachable; docs/data/core-exports.json grades none bridged
4 AI instructions current and economical met; size budget deferred CLAUDE.md, copilot-instructions, AGENTS.md corrected; the 94 KB budget is a 1.2 target
5 Hardening at pkinative-cli level met strict switch values, every read bounded, the deadline on stalled inputs, per-field escaping, no registry code next to the signing identity
6 Documentation factual and complete met every count pinned by a test; schemas checked against real output
7 Autonomous agent driving, token savings met schema manifest publishes arity, repeatability, path flags and operands; errors and summaries carry the facts a second call used to fetch; bytes measured per task

Ledger: .audit/1.1.0/ (local, git-ignored) — review-v1.1.0.md (round 2, every finding with
its verdict and commit), corpus-1.0.0.sha256, then the round-3 files brief.md,
auditor-a.md, auditor-b.md, verifier-1.md, auditor-d.md, verifier-2.md, ledger.md,
verdict.md; the release-day round under round-4/ (brief.md, auditor-c.md, auditor-e.md,
verifier-3.md, ledger.md, verdict.md).

/release-audit release-notes/v1.1.0.md — GO on 2026-10-20 (round-4/verdict.md: blockers
open 0, majors open 0, waived 0; gate 15 / 15 at 64089ac; next: the maintainer's review of this
branch). The agent-autonomy pass re-measured the
token table: --summary / --fields cut stdout by 75–94 % against text (88–98 % against the full
--json report); bare --json is text-sized on inspect and 2.8–2.9× on list / verify —
the gain is the projection levers', as AGENTS.md §3 says.

Validation (what actually ran, on Windows 11, Node 22.23.2)

  • npm run typecheck:all clean; npm run lint 0 errors
  • npm run test:coverage — 1387 passed, 9 skipped, 76 files; 95.91 / 91.48 / 97.81 / 96.77
    (thresholds 95 / 91 / 97 / 96)
  • npm run validate:zip — 37 PASS + 4 XFAIL; the 37 inherited archives are byte-identical
    to the 1.0.0 build (SHA-256 compared)
  • node scripts/gate.mjs --publish --require-all — every step green (reproducible build,
    bundle probe, 10-file tarball, install smoke test)
  • node samples/run-all.js --clean — 96 / 96
  • Built-binary smoke tests:
    • create --parallel, doctor (deflate tier node-zlib, maxEntryCompressedSize in the
      limits), schema diagnostics (12 codes), create --stdin-name --zip64,
      list --name-encoding shift_jis;
    • inspect --check canonical-form (pass and fail), --timeout 1ms → E_LIMIT, --progress;
    • --timeout 60ms mid-entry on a 64 MiB entry: stream -d, stream --cat, extract → E_LIMIT,
      exit 1, no partial file (1.1.0-dev exited 0 with a truncated file);
    • refusals: --overwite / --constructor x → E_USAGE, --max-entries none → E_USAGE,
      a planted config → E_USAGE, --overwrite over a symlink replaces the link.
  • CI green on the PR (all required contexts in .github/rulesets/main.json)

Human follow-ups (not performed by any agent)

Checklist

  • Tests pass (npm run test)
  • Type check passes (npm run typecheck:all)
  • Lint passes (npm run lint)
  • New code has tests (coverage thresholds raised, not lowered)
  • CHANGELOG.md updated
  • No breaking changes to the agent contract; the three narrowed inputs are documented with
    their migration
  • Binary smoke test passes (node dist/cli.cjs --help outputs all 17 commands)
  • veraZIP gate passes (npm run validate:zip locally; CI on Linux + Windows)
  • No ZIP parsing logic in the CLI
  • No security default loosened silently
  • Docs counts updated (tests/docs/consistency.test.ts green)
  • The audit ledger is summarised above with a fix commit for every confirmed blocker and
    major (b04f15f, 64089ac; verdict GO on 2026-10-20, 0 waived)

AI assistance

Drafted with AI assistance under .github/AGENT_RULES.md and
CLAUDE.md. The agent worked on a local branch only and made no GitHub writes; it read alerts
and PRs with read-only gh api calls. The quality gates above were run locally. Every
deviation from the plan is recorded in an ADR or in ROADMAP:

  • dosTimeMode not adopted (ADR 0008);
  • the Node floor is ^22.17.1 || >=24.4.1 (Windows path CVEs; second review);
  • no stream --codec sample: the CLI cannot write a custom-method archive, so the case is
    covered by tests;
  • runner labels stay -latest, as in the sibling repositories.

Before this draft, two independent review agents (correctness and security) read the branch
diff. Every finding was reproduced on the built binary, fixed, and pinned by a test
(tests/integration/review-fixes-1-1.test.ts, built-binary-smoke.test.ts). The engine-side
cause of the stream --timeout hang is a local draft for zipnative
(.github/drafts/upstream-forward-reader-abort-hang.md).

Human review gate applies.

Nizoka added 21 commits October 18, 2026 11:45
…on to v2.4.4

All four github/codeql-action steps (init, autobuild, analyze in codeql.yml,
upload-sarif in scorecard.yml) must run the same release: Dependabot opened
one PR per step (#3, #4, #5, #7), and #7 failed because CodeQL refuses a
mixed-version run. One commit moves them together; scorecard-action follows
#1. SHAs verified against the peeled upstream tags (git ls-remote).

Supersedes #1, #3, #4, #5 and #7.
…-encoding, --timeout, --progress

Pin zipnative ^1.1.0 (the sole runtime dependency, bumped by hand: the
Dependabot group #11 had moved it under a "dev" label and broke the build)
and absorb every 1.1 change:

- core bridge: the 29 new exports re-exported; docs/data/core-exports.json
  is the 106-row ledger (101 + 5 worker) with a third reach grade,
  "bridged", for exports no 1.1.0 command reaches yet (range reader,
  canonicalize, raw copy — each named in ROADMAP "1.2 candidates")
- scripts/sync-engine-data.mjs: re-derives the engine half of
  docs/data/{errors,core-exports}.json from a zipnative checkout and the
  CLI half (cli mapping) from the built CLI; --check for CI
- 12th diagnostic ZIP_TIMESTAMP_CLAMPED (typed satisfies
  Record<ZipDiagnosticCode, ...> so a new engine code fails tsc); the
  CLI's own DOS-range warning is gone (the engine reports it)
- ZipLimits.maxEntryCompressedSize: reported by doctor / schema limits,
  deliberately without a flag (only openZipRange consults it)
- removed compensations: ZLIB_TO_ZIP (every tier raises ZIP_DEFLATE_*),
  the hand-rolled skipped re-derivation in verify --entry and modify
  (verifyEntry().skipped + entryPasses(), the engine's archive rule), the
  Unix-mode encoder (externalAttributesFromUnixMode, still masked to 0o777)
- inspect: determinism measured by analyzeDeterminism(); deterministic
  keeps its 1.0 meaning, plus canonicalExtras / canonicalVersionMadeBy /
  canonicalForm / offenders and --check canonical-form
- create --stream|--stdin-name --zip64: streamed entries past 4 GiB
- --name-encoding <label> (list, inspect, cat, extract, stream): a
  TextDecoder nameDecoder, applied by the engine before sanitising
- --long rows: timestamps (UT / NTFS / DOS) and unixIds (0x7875)
- --timeout <duration>: AbortSignal deadline -> E_LIMIT detail.limit
  "timeout", output removed; --progress: onProgress on stderr (NDJSON
  under --json)
- stream: --codec methods decode through createDecompressor; codecs
  without it are refused before the first byte (--skip-unsupported skips)
- modify: an encrypted survivor's local header is now cross-checked
  (mismatch -> ZIP_CD_LFH_MISMATCH); an unregistered method stays
  E_UNSUPPORTED

dosTimeMode is deliberately not adopted: the CLI's date convention is per
entry (explicit dates store UTC fields, --date now / --mtime the local
wall-clock), which one archive-wide mode cannot express. The 37-archive
veraZIP corpus is byte-identical to the 1.0.0 build.

npm audit: the five vulnerable dev-only packages (brace-expansion,
source-map-js, fast-uri, http-cache-semantics, ip-address) resolved by
npm audit fix; 0 vulnerabilities.
…unds, symlink-safe writes, terminal-safe text

Runtime hardening to the level of pkinative-cli (ADRs 0001-0008 under
docs/adr/). Three items narrow what 1.0 accepted; each is a security fix
with its migration in the CHANGELOG:

- config (ADR 0001): .zipnativerc.json is presentation only (format,
  fields, json, pretty, quiet, no-color, progress, strict, long, summary).
  Every other key is E_USAGE naming the file (1.0 refused only codec, so
  a planted file could disable the limits or turn on --overwrite /
  --skip-unsafe / --allow-symlinks); prototype keys refused; regular file
  read through one bounded descriptor; a key applies only where the
  command declares it; the applying file is announced on stderr and
  carried as config { path, applied } in the envelopes
- strict invocation (ADR 0003): prototype-free parser, aliases
  canonicalised, repeated switches refused; assertInvocation() refuses
  undeclared flags, repeated single-value flags and surplus operands
  before any input is read — on argv and for every batch manifest task
  before the first one runs (--constructor no longer crashes E_RUNTIME)
- finite bounds (ADR 0002): none / inf refused on every --max-*,
  --max-input-size and inflate --max-output, with an explicit-value remedy
- reads (ADR 0005): one descriptor; regular files size-checked on it and
  read to that size; pipes / FIFOs / devices byte-counted (/dev/zero is
  E_LIMIT, process substitution still works); directories E_IO
- writes (ADR 0005): lstat refusal (dangling links count) + wx; under
  --overwrite a temp sibling renamed into place, so a planted symlink is
  replaced, never followed; the write layer removes only what it created
  — fixes a 1.0 race where an engine error before the deferred exclusive
  open made cat / inflate / create unlink a pre-existing output
- terminal-safe text (ADR 0004): escapeTerminal() on every text sink
  (reports, progress, diagnostics, errors); JSON exact;
  tests/docs/source-hygiene.test.ts forbids raw control / bidi characters
  in tracked files (diagnostics.ts carried a literal NUL)
- text errors lead with the class and cause: error E_X (ZIP_Y): ...;
  exits after a stdout / stderr write wait for the flush (async pipes on
  macOS / Windows); --json is honoured for refusals raised by the parse
- doctor reads the supported Node range from package.json engines.node
  (kept at >=22: pkinative-cli's patch floor answers a Web Crypto CVE that
  does not apply here)
- samples/config rewritten for the presentation-only contract, with a
  refused planted config demo; run-all.js 74/74
…esets as code

Supply chain and CI to the level of pkinative-cli and zipnative 1.1
(docs/adr/0009-release-integrity.md):

- scripts/gate.mjs: the single definition of green (--fast / --ci /
  --publish, --require-all, logs in test-output/.gate/), run by ci.yml,
  node-current.yml, sample-regression.yml and publish.yml instead of
  hand-copied steps; plain ESM with @ts-check (no tsx), typed by the new
  tsconfig.scripts.json
- publish.yml: guard -> build -> publish -> attest. The tag must equal
  v<version>; build holds no id-token and no cache and runs the publish
  gate; publish runs behind the npm-publish environment with .nvmrc only,
  re-checks both digests and publishes the handed-on tarball with an npm
  client pinned by SHA-512; attest fetches it back from the registry,
  runs npm audit signatures, writes CycloneDX + SPDX + toolchain SBOMs
  (npm sbom — @cyclonedx/cyclonedx-npm dropped) and attests them
- every job: harden-runner first (block with an allow-list, or audit
  with a dated reason), npm ci --ignore-scripts, read-only top-level
  permissions, persist-credentials: false, one SHA per action (verified
  against the peeled upstream tags); no path filter anywhere; verazip.yml
  no longer interpolates step outputs into run: scripts
- codeql.yml: actions + javascript-typescript, build-mode none,
  security-extended, job-level permissions
- new: audit.yml (weekly npm audit + OSV-Scanner), dependency-review.yml
  (high severity, licence allow-list), node-current.yml (Node 26,
  advisory), sample-regression.yml (run-all.js on Linux + Windows), and a
  workflow-lint job (zizmor, checksum-verified actionlint, reuse lint)
- .github/rulesets/{main,tags}.json, CODEOWNERS widened to the trust
  boundary, SECURITY-INSIGHTS.yml (OpenSSF 2.0), docs/.well-known/
  security.txt (RFC 9116), REUSE.toml + LICENSES/MIT.txt
- dependabot: seven-day cooldown, actions grouped (the four codeql-action
  steps move together), zipnative excluded from the dev group, @types/node
  majors ignored (engines floor 22)
- package: tracked .npmrc (ignore-scripts, engine-strict), .node-version,
  packageManager, exact overrides, @eslint/js declared, no main entry; the
  tarball is 10 files (+ CHANGELOG, SECURITY, THIRD-PARTY-NOTICES) pinned
  by docs/data/package-files.json; bundle-probe (engine external, no
  inlined engine code, no console.log, no attribution trailer) and an
  install smoke test of the packed tarball
- tests/tools/workflows.test.ts pins the workflow invariants;
  tests/scripts/release-tools.test.ts the gate and its checks; opt-in git
  hooks (.githooks/, npm run hooks:install)
- ai-governance.json 1.1.0: ai_attribution_trailers_allowed: false,
  configuration_relaxes_checks: false
…c, docs integrity

- tests/fuzz/parsers.test.ts: seeded campaigns (no dependency) over argv,
  sizes / counts / durations, globs, --fields, batch manifests,
  .zipnativerc.json, --name-encoding and the terminal escaper; invariant:
  return or CliError, never E_RUNTIME (ADR 0010, ROADMAP A-25)
- fix(glob): a character class with a range out of order ('[h-%]') is a
  usage error (exit 2) instead of a RegExp SyntaxError surfacing as
  E_RUNTIME (fuzz finding)
- tsconfig: exactOptionalPropertyTypes, noImplicitOverride,
  noPropertyAccessFromIndexSignature, lib ES2022 without dom
- eslint: no-non-null-assertion, no-console, no zipnative import outside
  the core bridge
- veraZIP: re-port of zipnative 1.1.0 validate-zip.ts (blob 604321f,
  commit 1e2ab42): WF/DESCRIPTOR-WIDTH keys the descriptor width on the
  local header's Zip64 extra; 23-id vocabulary; the vendor test compares
  the pins with the engine checkout at the installed tag when present
- corpus: create --stream --zip64 and a clamped pre-1980 --date
  (39 archives: 35 PASS, 4 XFAIL); the 37 earlier archives stay
  byte-identical to the 1.0.0 build
- tests/docs/links.test.ts: every relative link and anchor resolves, no
  CRLF in the index; the fixture ledger records and checks SHA-256
- coverage thresholds ratcheted to 95 / 91 / 97 / 96
- version 1.1.0 (package.json, lockfile, CITATION.cff with the engine
  reference at 1.1.0)
- CHANGELOG [1.1.0]: security and behaviour changes with their migration,
  added, changed, fixed, supply chain, documentation; the entries listed
  under [Unreleased] after 1.0.0 shipped in the 1.0.0 tarball (the
  published dist/cli.cjs is byte-identical to the v1.0.0 tag build) and
  now sit under [1.0.0] "Release-branch review"
- ROADMAP: v1.1.0 released section; the v1.0.0 entry keeps its own
  37-archive figures; the type-aware lint item is checked
- release-notes/v1.1.0.md; release-notes/draft/PR-v1.1.0.md and
  triage-v1.1.0.md (every open PR and alert, read with read-only gh api
  calls, with a verdict and a ready-to-paste comment)
- docs: README what's-new box and figures, AGENTS.md (zip64 in the create
  envelope, the timeout envelope, timestamps / unixIds / nameEncoding
  rows, canonicalForm and offenders, decoded names stay guarded),
  llms.txt, knowledge base, CONTRIBUTING, copilot-instructions and
  CLAUDE.md figures (106 exports, 12 diagnostics, 69 files, thresholds)
- samples: create/08-zip64-stream, list/04-name-encoding (with a foreign
  Shift_JIS archive from .NET System.IO.Compression, SHA-256 recorded and
  tested), inspect/04-canonical-form, agent/04-timeout-and-progress;
  45 demos, run-all.js 83 jobs; the samples README config rows follow the
  presentation-only contract
- typescript-eslint 8.69.0 -> 8.71.1 (the dev half of Dependabot #11)
Each finding was reproduced on the built binary before the fix and is
pinned by a test (tests/integration/review-fixes-1-1.test.ts,
built-binary-smoke.test.ts):

- stream --timeout no longer exits 0 with a truncated file: the engine's
  forward reader is not given the signal (its pending entry read did not
  settle on abort; upstream draft written), the CLI's input and write
  loops enforce the deadline; the dispatcher fails any run whose event
  loop drains with the command pending and removes the in-flight files
- a timeout that reaches a decompressor is E_LIMIT, not
  ZIP_DECOMPRESSION_FAILED (mapZipError returns the deadline's reason)
- false in .zipnativerc.json never turns a switch on
- batch directory mode declares every create flag it forwards
- one deadline per process: a manifest task cannot carry timeout
- the file writer's backpressure wait no longer leaks close listeners
  (MaxListenersExceededWarning on stderr, a 1.0.0 bug)
- --json after -- is an operand
- pipe / FIFO / device reads fill 64 KiB blocks: memory is bounded with
  the bytes, not 64 KiB per read
- terminal escaping per field: escapeLine() also escapes TAB and LF in
  names, messages, remedies and the config info line, and the escaped set
  covers the invisible look-alike characters (U+200B, U+2028/9, U+FEFF,
  U+00AD, Hangul fillers, tag characters)
- publish.yml: the consumer install check runs in a fifth, read-only job,
  never next to the attestation identity
- a manifest comment-file is anchored to the manifest directory
…t batch

Second 1.1.0 review (reviewers A and B, verifier), group 1:

- M-01: a switch takes only true|1|yes|on|false|0|no|off (case-
  insensitive); any other value, including an empty one, is E_USAGE before
  any input is read, on argv and in every batch manifest task. 1.1.0-dev
  read `--overwrite=flase` as ON (pkinative-cli parity: hasFlag delegates
  to the strict switch reader; the value is never echoed)
- N-1: batch --task verify reads each archive through the bounded
  one-descriptor reader (--max-input-size), like verify
- M-23: batch refuses the flags its mode ignores (create flags with
  --task verify, directory flags with --manifest); a manifest task cannot
  set a process-wide flag (json, quiet, no-color, config, no-config,
  progress, timeout, help, version); batch verify rows carry zipCode
- M-24: --workers is bounded 0-64
- M-26: the alias table is prototype-free; an undeclared alias is named
  as typed (--output-dir (-d)); the combined-short-flag message lists the
  real short flags
- M-27: extract refuses a file entry that is also a parent directory with
  E_IO naming the entry, before anything is written (dry-run agrees); sink
  mkdir failures map to E_IO, never E_RUNTIME
- M-28: extract --entry naming no entry is E_NOT_FOUND
  (ZIP_ENTRY_NOT_FOUND), like cat and verify
- the --json prescan also honours --json=true|1|yes|on
Second 1.1.0 review, M-03: the deadline was only checked when a chunk
arrived, so a stalled pipe (list -, crc32, create --stdin-name, stream,
extract -) waited forever. The input streams now carry the run signal
(addAbortSignal), the buffered stdin reader rejects on abort, an
AbortError past the deadline is reported as the deadline, and the
dispatcher fails a run still pending 500 ms after the abort (a FIFO read
the CLI cannot cancel), removing the files being written. Spawn test on
the built binary; ADR 0006 updated.
…fety in the summary

Second 1.1.0 review, group 3:

- M-02: inspect-summary declares canonicalForm; the error and status
  schemas declare config { path, applied } and the command-specific status
  fields (zip64, method, level, order, stream, parallel, ...).
  tests/commands/schema-conformance.test.ts validates the real output of
  every JSON-emitting command against its schema subject (test-only
  validator, no dependency)
- M-04: inspect --summary carries unsafeNames, symlinks and duplicateNames
  (the "safe to extract?" probe)
- M-05: schema manifest adds per-command switches, repeatable, pathFlags
  and operands, plus globalSwitches / globalRepeatable; exitCodes list
  130 and 143. flags stays a string list
- M-06: a verify failure names the failing entries (detail.failedEntries,
  failedCount; entryName when exactly one)
- M-08: verify --name-encoding decodes the --entry lookups (refused
  without --entry: verifyZip takes no decoder)
- M-10: schema output is compact under --json (--pretty opts out)
- M-21: text-mode inspect --check, verify and batch failures print their
  one-line reason; a parse-time envelope names the command
- M-07: --summary / --fields given to a text run print a warning (a
  config-supplied default stays silent)
- KB: the mapZipError paragraph no longer describes the removed zlib
  translation
…keywords

Second 1.1.0 review, group 4:

- M-29: engines.node is ^22.17.1 || >=24.4.1 (was >=22): the patch floor
  of the Windows path-normalisation fixes CVE-2025-23084 and
  CVE-2025-27210 (device names in path.join / normalize), which the sink's
  containment relies on. doctor reports error below it, and the new gate
  step node-floor (ci, publish) enforces it through the built CLI
- M-30: the bundle probe refuses a network module (net, http, https,
  http2, tls, dgram, dns), with or without the node: prefix, so 'offline,
  always' is enforced by the gate, not only stated
- M-22: the mcp keyword is gone (no MCP server ships with this package)
…I instructions

- M-25: the migration notes are exact: 1.0.0 printed the bare message (an
  Error: prefix only for unexpected runtime errors); the switch rule is
  true|1|yes|on|false|0|no|off, an unrecognised value is E_USAGE
- M-19: CHANGELOG [1.1.0] opens with a Keep a Changelog ### Security
  section (the migration lives there); links follow the new anchor
- M-11: 45 demos (README, llms.txt), 12 diagnostic codes (README), 32 + 3
  conformant archives (AGENTS), the 37 inherited archives (ADR 0008); the
  consistency test now pins the demo and diagnostic counts
- M-12: AGENTS §7 step 5 reads as one instruction; step 2 gates on the new
  safety counts; §3 documents the inspect summary fields
- M-13: CLAUDE.md names REPEATABLE_FLAGS / OPERANDS in the checklist and
  lists every utils module and script; copilot-instructions lists
  control, engines and sanitize and the --progress env mapping
- M-09: AGENTS and ADR 0008 say JSON lastModified is read in the host's
  time zone (TZ=UTC or dosDate/dosTime across hosts)
- ADR 0003 records the switch-value rule and the batch mode refusals
- ROADMAP: the deferred findings (token budget, contract/guide split,
  family hygiene, modify --name-encoding, diagnostic remedies) are 1.2
  targets
…-range, raw entries

Second 1.1.0 review, M-17 (the user's decision: expose the bridged 1.1
capabilities in 1.1.0). The 11 exports graded "bridged" are now reached:

- canonicalize (canonicalizeZip, CanonicalizeOptions, CanonicalOptions):
  rewrite any archive into the canonical deterministic form without
  recompression; --date (now refused), --keep-comments, --normalize-modes;
  the envelope carries deterministic / canonicalForm measured on the
  OUTPUT; idempotent. modify --compact --canonical (CompactOptions)
- merge (addFromReader, AddFromReaderOptions, RawEntryReader): merge
  archives or split one with filters; sources opened eagerly, every entry
  verified (verify: true, no opt-out); duplicate names refused unless
  --on-duplicate first|last; encrypted or unregistered-method entries
  refused (E_UNSUPPORTED) unless --skip-unsupported
- --range on list, cat, verify (openZipRange, ByteRangeSource,
  ZipRangeReader, rangeSourceFromBytes): fileRangeSource() reads one
  descriptor of a regular file by ranges, so an archive larger than
  --max-input-size is listed, read and verified; the ninth bound
  --max-entry-compressed-size bounds each whole-entry fetch (ADR 0011,
  superseding ADR 0007); inspect keeps reading whole (its determinism
  verdict needs the bytes)
- create-manifest raw entries (addRaw, RawEntryMeta): the payload is
  decoded once and its size and CRC-32 checked before it is written
  (ADR 0012)

17 commands; batch manifests whitelist canonicalize and merge; schema
manifest, completions, USAGE, README, AGENTS, llms.txt, the knowledge
base (§2, §4, §8), SECURITY, CONTRIBUTING, CLAUDE.md, copilot-instructions,
ROADMAP and the release notes follow; docs/data/core-exports.json grades
no export "bridged". Samples canonicalize/01, merge/01, list/05-range
(48 demos, 96 run-all jobs); veraZIP corpus + canonicalized.zip and
merged.zip (41 archives, 37 PASS, 4 XFAIL; the 37 inherited archives
byte-identical). The gate's smoke step reads the command count from
--help.
- canonicalize counts hostile names (unsafeNames) and encrypted entries in
  its envelope and warns: reproducible is not safe to extract
- manifest raw entries refuse bytes trailing a DEFLATE stream; a stored
  payload must be exactly the declared size
- ZIPNATIVE_* environment switches accept the same eight spellings as the
  flags (ZIPNATIVE_DRY_RUN=true was silently off); anything else is E_USAGE
- docs: raw, --range, --max-entry-compressed-size, the canonicalize
  envelope, nine --max-* bounds, Keep a Changelog headings, final figures
- release-notes/draft/review-v1.1.0.md: findings, verdicts, commits,
  token table and the counter-verification outcome
…de settings

- .claude/skills/release-audit/: the family's pre-release audit ported to
  zipnative-cli (two auditors, an adversarial verifier, an autonomy pass,
  a GO/NO-GO ledger under .audit/<version>/); the negative-space checks are
  the CLAUDE.md non-negotiables, each as a command on the built binary
- .claude/settings.json tracked: attribution off, npm publish / git push /
  git tag / gh writes denied for both shell tools; per-machine allowances
  move to .claude/settings.local.json (git-ignored, with .audit/)
- tests/tools/agent-settings.test.ts pins attribution, the deny list and
  the ignore rules
- CONTRIBUTING §Release, AGENTS §6, CLAUDE.md, the knowledge base, the
  CHANGELOG and the release note name the audit step
release-notes/draft/ holds the PR bodies only, as in the sibling
repositories: the second-review report now lives under .audit/1.1.0/ and
the HITL triage under .github/drafts/ (both git-ignored). The PR draft
carries their committed summary (§Independent audit with the rounds and
the seven criteria, the triage table under §Human follow-ups) and the
figures it still quoted from 1.0.0 (17 commands, 12 ADRs, 96 sample jobs).
verify: a bound is never a verdict. The engine's verifiers (verifyZip,
verifyEntry on both readers) report a fetch their bound stopped as a CRC /
size mismatch, which read as corruption; the declared sizes are checked
against --max-entry-size / --max-entry-compressed-size first and the
refusal is E_LIMIT with entryName, detail and the flag as remedy, on the
buffered, --entry, --range and batch --task verify paths; a limit refusal
at open keeps E_LIMIT and its detail; --strict names ZIP_STRICT_DIAGNOSTIC.
Engine draft: .github/drafts/upstream-range-verify-swallows-limit.md.

io: a path that cannot be opened is E_IO on every path (govern
verify-issue and a manifest raw path were E_RUNTIME with the raw ENOENT).

cli: a misspelt ZIPNATIVE_* value under --json is refused as an envelope;
list / inspect --summary carry zip64Entries next to zip64 (a streamed
--zip64 archive has entries and no Zip64 EOCD) and the schemas declare
both; the usage refusals whose message named the lifting flag carry it as
remedy; CRC details are numbers on every path; list --format ndjson warns
when a projection flag is ignored; batch --task verify rows carry
entryName, detail and remedy, declared in schema batch; the --timeout
message no longer claims a written file on a read.

docs: create --stream --zip64 is how a streamed entry passes 4 GiB (the
README feature table, llms.txt, AGENTS.md and the knowledge base said it
was refused); SECURITY.md describes the ninth bound and its flag; the
createDecompressor() contract; batch --manifest --dry-run scope; the
config announcement is text-mode only; verify --name-encoding; the
doctor Node check; raisedBy of ZIP_TIMESTAMP_CLAMPED; the raw manifest
keys in create --help; the sample note on --summary --fields; twelve
ADRs; the measured figures (76 files, 1385 tests, 95.91 / 91.48 / 97.81 /
96.77, 186.1 kB); the audit's deferred items in the ROADMAP.

tests: tests/integration/audit-1-1.test.ts, the range / zip64 / parity
cases in core-surfaces-1-1, the env-switch envelope in the built-binary
smoke test; the suites that pinned the old shapes follow the fix.
…draft

/release-audit at 99bf8c3: 238 rows over three reports, two verifiers,
0 blockers, 5 majors, 23 minors, 2 notes — GO, every actionable row fixed
in b04f15f, 0 waived. The ledger stays local under .audit/1.1.0/.
The release ships on 2026-10-20: CHANGELOG, the release note, CITATION.cff,
the ROADMAP, SECURITY-INSIGHTS; the derived dates follow (1.0.x security
fixes until 2027-04-20, security.txt Expires 2027-10-20).
batch --task verify parses every --max-* value once, before the pool and
before any read: a malformed bound is one E_USAGE (exit 2) again, never
a row per archive and never a plan that says ok (the b04f15f regression).

The ZIPNATIVE_* switches are judged after the --json token decides the
mode, and the raw ZIPNATIVE_JSON that token shadows is still judged;
the refusal envelope names the command.

The AI-instruction files say 17 commands and nine bounds, and the docs
consistency test counts them; CONTRIBUTING states the inbound = outbound
licensing rule; ADR 0009 names its five jobs; the batch --dry-run help
and AGENTS.md say what the plan checks; the --timeout help and README
match the message; the release-day items of the audit in the ROADMAP;
the coverage figures dated 2026-10-20 (76 files, 1387 tests).
…gures in the PR draft

Round 4 of /release-audit at 9eaecd9: closure 29 / 29 of the round-3
ledger, the fix diff re-read, release-day bookkeeping, a 96-row standards
sweep, the triage re-read through gh api (PR #12, #13, Dependabot #4 —
all resolved on the branch); 0 blockers, 2 majors + 7 minors fixed in
64089ac, 0 waived — GO on 2026-10-20. The ledger stays local under
.audit/1.1.0/round-4/.
@Nizoka Nizoka self-assigned this Oct 19, 2026
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread src/utils/io.ts Fixed
@Nizoka Nizoka added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request labels Oct 19, 2026
…mpdir root

CodeQL js/insecure-temporary-file flagged src/utils/io.ts:122 on the
release PR: the directory-refusal unit test passed os.tmpdir() itself
to readFileOrStdin, so a path in the shared, world-writable temp root
flowed into open(). The test now creates its own directory with
mkdtempSync, removes it afterwards, and asserts the E_IO refusal its
name promises. No src/ change: readFileOrStdin only reads.
@Nizoka
Nizoka merged commit 85efa79 into main Oct 19, 2026
10 of 16 checks passed
@Nizoka
Nizoka deleted the release/v1.1.0 branch October 19, 2026 11:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants