Start Debugging

Claude Code 2.1.251 Closes Four Ways Around the Permission Check

A symlink swapped after the check, deny rules that stopped applying through a symlinked search path, a marketplace command pointing outside its plugin, and a workflow script read before approval. Four fixes in one release, all the same bug.

Claude Code 2.1.251 shipped on August 28, 2026 with a changelog long enough to bury the interesting part. Four of its fixes share one shape: something reached a file that the permission check had not approved. Read them together and they stop looking like four bugs and start looking like one class.

The check passed, then the path changed

The headline fix is a textbook time-of-check-to-time-of-use race. Per the changelog, file tools “following a symlink swapped inside the working directory after the permission check” could “read or write outside the approved location.” You approve an edit to src/config.ts, the path resolves, the check says yes — and between that yes and the write, the entry becomes a symlink pointing somewhere else.

The part worth internalizing is who gets to do the swapping. A postinstall script, a file watcher, a dev server, a test harness, or the agent’s own previous Bash command all run while the session is open. The working directory is not a quiet place, and it was never a trusted one.

Grep and Glob had the read-side version of the same hole: Read(...) deny rules were not applied to files reached through a symlinked search path. A deny rule on secrets/** held for a direct read and quietly stopped holding when the same file was matched through a symlink pointing into it.

Two paths that came from config, not from you

The other two came in through files that ship with a repository. Plugin commands declared in a marketplace entry could point outside the plugin directory; those paths are now rejected with an explicit path-traversal error. And the Workflow tool read a scriptPath outside what the session was allowed to read before the permission check ran — then quoted the contents back in its error message, which turns a blocked read into a successful one.

The same release keeps tightening settings

Half a dozen other changes in 2.1.251 point the same direction, all treating a cloned repository as untrusted input:

None of these is a dramatic exploit on its own. Together they close the gap between “the permission system said no” and “the file stayed unread.”

Upgrading

claude update, or reinstall from npm. Two same-week notes: 2.1.250 landed the same day and is bug fixes only, and 2.1.248 (August 27) added --restricted — equivalently CLAUDE_CODE_RESTRICTED=1 — which strips the tools that run commands or code, drops WebFetch unless you name it in --tools, keeps file tools inside the working directory, refuses bypassPermissions, and ignores user, project, and local settings files entirely. That flag and this week’s fixes are the same argument from two directions: the settings and paths a repository hands you are input, not configuration.

The marketplace fix in particular lands one week after 2.1.238 gave catalogs real reach, letting a plugin marketplace mint its own auth headers — the more a marketplace entry can do, the more the directory boundary around it has to hold.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back