On this page
Changelog
All notable changes to agentcurfew are written here for its users.
Unreleased
Nothing yet.
1.2.0
- Settings that redirect the agent or bring code into it are read, as components of the new kind
setting: each variable of Claude Code'senvblock, each pluginenabledPluginsturns on or off, each marketplace ofextraKnownMarketplaces(also writtenadditionalMarketplaces), and Gemini CLI'scontext.fileName. - New rule
env-override(high): anenvvariable in a project file that sends Claude Code's requests to another host (ANTHROPIC_BASE_URLand the Bedrock, Vertex, Foundry and Claude Platform on AWS endpoints,HTTP_PROXY,HTTPS_PROXY), adds headers to them (ANTHROPIC_CUSTOM_HEADERS) or wraps every command (CLAUDE_CODE_SHELL_PREFIX). Low in user files and for variables current versions ignore in a project file. - New rule
plugin-marketplace(medium): a marketplace registered from GitHub, git or an address, high withautoUpdateor over plainhttp://; low for a local path and for a plugin a project file turns on. - New rule
unpinned-launcher(medium): hooks, automatic tasks, settings that run a command and the scripts they run that start a package throughnpx,uvx,docker runand the like without an exact version or digest.npx --no-installandnpm exec --noare not reported. headersHelperof a remote MCP server and of a marketplaceurlsource, andCLAUDE_CODE_SHELL_PREFIX, are settings that run a command (command-setting), sodangerous-hook-command,secret-in-configandunpinned-launchercheck them, and the scripts they run are read and locked.- Gemini CLI's
context.fileNamenames context files that are now read whereGEMINI.mdis, besidesGEMINI.mditself; a name that is absolute, from home, with..or wildcards is not followed, and the settings file is reported asunparsable. secret-in-configchecksenvvalues and marketplace headers by their names.- The hook asks before a write to a script that a hook, a task or a setting runs, or to a context file a setting names, and denies it on the same terms as a write to an agent file, so a hook script that starts to download and run code is refused; a shell command that names such a file and does not only read it is asked about.
- The hook denies a write to the project's Claude Code settings (
.claude/settings.jsonor.claude/settings.local.json) that setsdisableAllHooks, removes the depcurfew hook from a project withdepcurfew.json, or removes the agentcurfew hook, since the agent would switch off the checks of its own writes and installs; such a change is made by hand.disableAllHooksis inventoried as a setting. - New
doctorcheckhooks-disabled:disableAllHookstrue in a project settings file turns off every hook of the user and the project for everyone who opens the repository; an error when the same file holds the depcurfew or agentcurfew hook. - Fixed, found by a review of 1.1.0 (each also in 1.1.0 unless noted):
- A hook command with a very long script path made the
.gitignorematch take quadratic time: a 7 KB settings file took 10 s and 160 KB took 50 s. Script paths are now read only up to the limits of common file systems (4096 characters, names of 255, 64 folders), and a path is matched once per pattern. - Thousands of nested here-documents (
bash <<Arepeated) overflowed the stack, soscanstopped with an error, and the hook gave no opinion on such a shell command, which let any command it would deny through. Here-documents are read to a depth of 8, past which their lines are read as plain commands, and the hook now asks when its own check fails, for shell commands too. unpinned-launcher(new, not in 1.1.0) andunpinned-mcp-servermissednpm x, global options before the subcommand (npm --yes exec,docker --context x run), every-pbut the last, packages added withuvx --with, and options of npm and docker whose value was taken for the package (--node-options,--cidfile,--volumes-fromand others).npx -y pkg --nono longer passes as--no-install: only options before the package count.- An MCP server named like its file (
.mcp.jsonin.mcp.json) was read as the whole file, so no rule checked its entry and the lock had noserverdetails for it. - The new guard of the hook counted a guard hook by its command string only, so a write that kept the string but stopped the hook from acting (a matcher that never fires, the hook moved to
PostToolUse, a decoy handler, a tiny timeout) was only asked about. A guard now counts only when it can act. (Not in 1.1.0, which had no such guard.) - Shell writes to an agent file spelled with a glob or braces (
cp x CLAUD?.md,tee .claude/settings.jso{n,x}), or named by another command (printf x > $(echo CLAUDE.md),xargs), passed the hook without a question. - A repository padded with tens of thousands of skill folders made the hook's two scans of a write take longer than the 30 s timeout of
init --hook, after which Claude Code lets the call through. The hook's scans now have a budget of 15 s, and it asks when the project is too large to check in time. - An own
__proto__key in an MCP server'senvorheadersbecame the prototype of the merged entry and hid the variables below it fromsecret-in-config.
- A hook command with a very long script path made the
- Fixed, found by a functional review of 1.1.0 (each also in 1.1.0):
- A server that a launcher fetches from git or an address (
npx -y github:org/repo, a tarball URL,uvx --from git+https://…) counted as a server started from a local command, somcp.allowServers: ["local"]allowed it, the hook asked aboutclaude mcp addof it instead of denying, andinitwrotelocalfor it. Such a server is now named by its spec. verifypassed when a file could not be read (too large, too deep, a link out of the project); it now fails asscandoes, withok: false.remote-instructionsmissedcurl -fsSL https://… | sh,wget -qO- https://… | bashandeval "$(curl -s https://…)"in skill scripts: options between the program and the address hid it.scansays in a note, and indepcurfewof its JSON, whenmcp-packagedid not check the packages of MCP servers because depcurfew is missing, unsupported or has no validdepcurfew.json.- The hook did not ask about
rm -rf .claude,cp -r x/. .claude/andcd .claude && echo x > settings.json.init --hooknow also matchesPowerShellandNotebookEdit(PowerShell commands are read like Bash commands), and a guard hook with anifcondition does not count as working. - Findings of
out-of-policyandmcp-packagecarry thecomponentIdof the component they name. - SARIF placed files of the home folder (
--user) under the repository; they are now relative toHOME, and machine files are absolute. - The action uploaded an empty SARIF file when the policy was invalid.
mcp --rootwith a folder that does not exist exits 2, as other wrong input does.
- A server that a launcher fetches from git or an address (
- Gemini CLI
context.fileName: a name with a leading slash is read in the folder, as Gemini CLI joins it (/.prompts/x.md), and a name agentcurfew does not follow is the new problemnot-followed, a warning, instead ofunparsable, whichdoctorreported as a file that could not be parsed. - Fewer false alarms and misses, found by a field test on 34 public repositories:
broad-shell-permissionreportsBash(npx*)and the like, a runner with a wildcard joined to it, and no longer reports a runner whose entry fixes the program it runs (Bash(pnpx vitest:*),Bash(xargs kill:*)).dangerous-hook-commandskips comment lines of scripts, and an assignment of a download's output (V=$(curl …)) is reported as a network connection, not as running the download.remote-instructionsleaves requests to this machine alone (localhost,127.0.0.1,::1).obfuscated-contentno longer takes a long paragraph of prose for a minified line.unpinned-mcp-serverleaves a server alone that npm runs only from what is installed (npm exec --no,npx --no-install), asunpinned-launcherdoes.- Scripts that hooks run through
${CLAUDE_PROJECT_DIR:-…}/…, and Gemini CLI hooks through$GEMINI_PROJECT_DIR, are read and locked.
- README: in a project with a release-age delay,
npx agentcurfew@<version>fails until the version is older than the delay; install it as a dev dependency or use the GitHub Action. verifyskips components of a kind it does not know, with a note andunknownKindsin its JSON, instead of refusing the whole lock. agentcurfew 1.1 refuses a lock that holdssettingcomponents, so upgrade every copy that verifies the same lock together.- After upgrading, run
agentcurfew lockonce: the lock gains the new components. NoschemaVersionorlockfileVersionchanged, and no dependency was added.
1.1.0
- Settings that run a command are read: Claude Code
statusLine,subagentStatusLine,fileSuggestion,apiKeyHelper,otelHeadersHelper,awsAuthRefresh,awsCredentialExportandgcpAuthRefresh, as components of the new kindcommand-setting;statusLineandotelHeadersHelper, which run when a session starts, are alsoauto-run. Codexnotifyis read from the user's config only, since Codex ignores it in a project.dangerous-hook-command,secret-in-configandhidden-unicodecheck them. - The scripts of the project that hooks, editor tasks and these settings run (
./scripts/x.sh,python3 "$CLAUDE_PROJECT_DIR"/.claude/hooks/x.py) are read with the same limits as other files, checked by the same rules, and locked with their own checksums, soverifyreportsscript changed. Installed packages (node_modules) and files the root.gitignoreignores are left out. - Every component has a stable
id. A hook's is its event, its matcher and a hash of its handler (with secrets redacted), so it no longer shifts when a hook is added before it:verifythen reports one added hook instead of changes to every later one, and the hook check does not take a moved finding for a new one. Locks of 1.0 without ids still work, compared by name, with a note. - The JSON output names its
producer(version and the date of the knowledge base), gives each component itsidand lock checksumhash, and ties findings to components withcomponentId;verify --jsonaddslockDigest. SARIF adds the fingerprintagentcurfew/v2by id and keepsagentcurfew/v1. NoschemaVersionchanged. - After upgrading, run
agentcurfew lockonce: the lock gains the new components and the scripts' checksums, and the hashes of existing entries do not change. - Node.js 20 or later (
engines>=20.0.0), instead of 22.23: nothing in agentcurfew needs a newer Node, and CI runs the packed CLI on Node 20.0.0, 20, 22, 24 and 26.
1.0.0
The first release. agentcurfew inventories, locks and checks the files that steer AI coding agents (Claude Code, Cursor, Codex CLI, Gemini CLI, Copilot in VS Code, Windsurf and Cline) without running any of them and without using the network.
scan: lists skills, MCP servers, hooks, automatic tasks, context files, permissions, subagents and commands from a sourced knowledge base, and checks them against 15 rules, each with an OWASP Agentic Skills Top 10 reference, a fix and an example. Text, JSON (with published schemas) and SARIF 2.1.0 output;--fail-on; a file that could not be read fails the scan;--userfor the home directory and machine settings.lockandverify:agentcurfew.lockholds a checksum of every component, with MCP servers' commands and addresses; secrets are redacted, also from the hashes, so a committed lock reveals none;verifyfails when anything was added, removed or changed. Skills are also compared withskills-lock.json.initandagentcurfew.policy.json: allowed skill sources and MCP servers, pinned versions, plain HTTP, network in hooks, auto-run on open, hidden Unicode, excluded paths, and exceptions with a reason and an end date. A JSON schema for editors.doctor: 13 checks for configuration that agents ignore or that does not work, each naming its source.mcp: a read-only MCP server (2026-07-28 and 2025-11-25) withscan_project,check_skill,check_mcp_configandexplain_rule.hookandinit --hook: a Claude CodePreToolUsehook that asks before writes to the files that steer agents and denies dangerous ones, and checksnpx skills addandclaude mcp addagainst the policy.- depcurfew as an optional peer (
>=1.5.2 <2): the rulemcp-packagechecks the packages that MCP servers start, offline;doctorchecks depcurfew's hook. - A GitHub Action (
scan,verifyordoctor, with SARIF upload). - Hostile input: size, depth and total-read limits, links not followed out of the project, secrets masked in every output and redacted in the lock, and patterns that read padded text in linear time.
- Node.js 22.23 or later; one runtime dependency each for YAML and TOML.