What's new in zn-gate 1.12.0: zn-gate reach, signed releases and a security fix
zn-gate 1.12.0 adds zn-gate reach, a read-only report of what the coding agents on your machine can reach, with the command that fixes each finding. It also moves release building and signing to a separate workflow, makes zn audit hide more secret values, and fixes a security issue in the agent hooks that affects versions 1.5.0 to 1.11.0. Update with npm install -g zn-gate@1.12.0 and pip install -U zn-gate.
What does zn-gate reach show?
It answers one question: what can the coding agents on this machine reach? Run it in the folder you work in:
npx -y zn-gate reach
In one screen it lists:
- which agents it finds (Claude Code, Codex CLI, Cursor, OpenCode) and whether zn protects them;
- the
.envfiles of the folder, with the names of their secrets and never the values; - the credential files in your home folder, such as
~/.aws/credentials, SSH keys and~/.npmrc. It only checks that they exist and never opens them; - the names of environment variables that hold a secret;
- what
zn auditfinds in the MCP servers and agent settings of the folder and of your user.
Each finding comes with the command that fixes it. The report is read only: it shows no value, sends nothing, starts no hook and writes nothing unless you ask for it with --remember, which keeps the counts of the run (high, medium, low and the date) in ~/.zn/reach-last.json so the next run can show what they were. --json prints the same report as JSON. The full description is in the reach documentation.
The README and the npm page now show a four-step path: zn-gate reach, zn-gate init, zn-gate check and zn-gate env shadow.
Can you run reach in CI and send it to code scanning?
Yes. zn-gate reach --sarif writes the report as SARIF 2.1.0. Findings about a file of the checked folder are results: the zn audit findings keep their rules, and a .env with secrets an agent can read is ZNR002. What belongs to the machine (agents that are not protected, credential files, environment variables with secrets) and files in your home folder are notifications of the run.
By default reach exits 0, so it stays a report. --fail-on high|medium|low exits 1 when a finding at or above that level is found, and a usage error or a failure exits 2.
What changed in how releases are signed?
Two things, both for people who verify what they install.
Releases are now built, attested and signed by a separate reusable workflow, build-release.yml. The release workflow calls its build and signing jobs but cannot reach them. That makes the build provenance of the npm and Python packages SLSA Build Level 3.
Release tags are created and signed by a workflow, tag-release.yml, with gitsign (Sigstore keyless signing), so the signature names that workflow and no person's identity. It runs only from main, checks that the commit is the head of its release branch, that every version matches and that every check passed, and waits for approval before it signs. From 1.12.0 on, the release workflow refuses to publish a tag unless it carries a valid signature from tag-release.yml.
If you verify releases, note that the signer workflow of the GitHub attestations and Sigstore signatures is usezn/zn/.github/workflows/build-release.yml from 1.12.0 on (earlier releases: release.yml). npm provenance and PyPI attestations still name release.yml. The verification page in the repository has both, and how to check a tag.
What does zn audit hide now?
zn audit quotes the line of a finding as evidence. It already replaced secrets in known formats. In 1.12.0 it also hides:
- a secret value in a format it does not know when it follows a credential flag (
--token,--api-key,--password), an auth header (Authorization: Bearer ...,x-api-key: ...) or a name such asAPI_TOKEN=; - a secret given as the next item of a JSON array after a credential flag, as in the arguments of an MCP server.
References such as $TOKEN or ${API_KEY} are kept. Earlier versions could quote such a value in the evidence of a finding. If a finding ever showed you one, rotate that credential.
What else is fixed?
- A long patch sent as a shell command (
apply_patchin Codex) is refused in a fraction of a second again. To name a secret file in the message, zn now checks only the first 32 files of the patch instead of every one on disk, which took several seconds for a patch near the 100 KB limit and could reach the agent's time limit for the hook on a slow machine. The decision does not change. - On Windows, a PowerShell command that already runs through
zn-gate run -- ...with a secret placeholder is no longer refused by mistake. zn-gate checkandzn hooks install codexnow say when Codex will not run zn's hooks, because they are turned off inrequirements.toml,managed_config.tomlor your ownconfig.toml, orallow_managed_hooks_onlyinrequirements.tomlwithout zn among the managed hooks. Codex gives no sign of it.
What is the security fix?
GHSA-rccq-h3xc-j9qj is rated medium and affects zn-gate 1.5.0 to 1.11.0. In those versions the read guard of the agent hooks could let a protected file, such as a .env file with values or a credential file, be read without being refused when a command reached it indirectly, or when the command was very long or slow to check. 1.12.0 resolves these forms before deciding, checks every command of a chain, and refuses a command it cannot check within a time limit well below the agent's hook timeout. It also refuses private keys kept outside ~/.ssh, for example in a project.
The zn team found it during an internal review. We describe the classes of routes and the design of the fix in a separate article: how a coding agent could read your .env file indirectly, and how 1.12.0 closes those routes.
What to do:
-
Update the packages and reinstall the hooks:
npm install -g zn-gate@1.12.0 pip install -U zn-gate npx -y zn-gate@1.12.0 init -
In every folder that has a
.envfile with values, runzn env shadow .env. A shadowed file holds only markers, so reading it by any route reveals no value. -
Run
zn-gate checkto see which agents have the hooks installed.
If you use the Claude Code plugin, update it from the plugin directory once 1.12.0 is listed there. The long-chain and timeout forms have no workaround in older versions, so updating is the only fix for those.
Frequently asked questions
What is new in zn-gate 1.12.0?
zn-gate reach, a read-only report of what the coding agents on your machine can reach, also as SARIF for code scanning. Releases are built and signed in a separate workflow (SLSA Build Level 3) and release tags are signed with gitsign. zn audit hides more secret values, the agent hooks refuse long shell patches quickly again, and a security fix closes GHSA-rccq-h3xc-j9qj.
How do I update to zn-gate 1.12.0?
Run npm install -g zn-gate@1.12.0 for the Node package and pip install -U zn-gate for the Python package. To reinstall the agent hooks, run npx -y zn-gate@1.12.0 init. If you use the Claude Code plugin, update it from the plugin directory once 1.12.0 is listed there.
Does zn-gate reach send my secrets anywhere?
No. It is read only: it never shows a value, sends nothing, starts no hook and writes nothing unless you pass --remember. It lists the names of the secrets in your .env files, checks whether credential files such as ~/.aws/credentials exist without opening them, and shows what zn audit finds in your MCP servers and agent settings.
Which versions does GHSA-rccq-h3xc-j9qj affect?
zn-gate 1.5.0 to 1.11.0. It is fixed in 1.12.0. The advisory is rated medium.
What changes if I verify zn-gate releases?
From 1.12.0 on, the signer workflow of the GitHub attestations and Sigstore signatures is build-release.yml, not release.yml. The verification page in the repository has both, and the way to check a signed tag.
The full list of changes is in the 1.12.0 release notes. zn-gate is MIT, runs locally and needs no account: start with npx -y zn-gate reach, or see Install for each agent.