forked from bchanot/claude
fix(settings): Write() deny rules inert — convert to Edit(), close write gaps
Startup emitted 15 warnings: "Write(**/.env) is not matched by file
permission checks — only Edit(path) rules are."
Write(path) rules never matched. The 5 secret-file write bans were dead
config — .env, secrets/**, *.pem, *.key were freely writable. Converting
to Edit() makes them enforced: permissions.md:242 "Edit rules apply to all
built-in tools that edit files", and :244 prescribes exactly this ("add an
Edit deny rule for paths no tool may change").
- settings.json: Write(...) -> Edit(...) on the 5 patterns.
- Mirror the 9 secret patterns Read denied but Edit did not: *.p12, *.pfx,
id_rsa*, id_ed25519*, .ssh/**, credentials, credentials.json,
.aws/credentials, .azure/**. Read/Edit parity now 14/14. Claude could
previously overwrite an SSH private key or ~/.aws/credentials.
- New read-allowed/write-denied class: lockfiles (*.lock,
package-lock.json, pnpm-lock.yaml, go.sum) + node_modules/**. Reading
aids diagnosis; hand-editing is always wrong — the package manager
regenerates them via Bash, which Edit deny does not block.
- templates/settings/SETTINGS.md taught the broken Write() pattern; fixed
at the source so /onboard stops propagating it.
Rule syntax has no negation and deny beats allow, so deny globs cannot
carry exceptions — see the .env.example conflict noted in the follow-up.
This commit is contained in:
+20
-6
@@ -134,11 +134,25 @@
|
||||
"Read(**/credentials.json)",
|
||||
"Read(**/.aws/credentials)",
|
||||
"Read(**/.azure/**)",
|
||||
"Write(**/.env)",
|
||||
"Write(**/.env.*)",
|
||||
"Write(**/secrets/**)",
|
||||
"Write(**/*.pem)",
|
||||
"Write(**/*.key)",
|
||||
"Edit(**/.env)",
|
||||
"Edit(**/.env.*)",
|
||||
"Edit(**/secrets/**)",
|
||||
"Edit(**/*.pem)",
|
||||
"Edit(**/*.key)",
|
||||
"Edit(**/*.p12)",
|
||||
"Edit(**/*.pfx)",
|
||||
"Edit(**/id_rsa*)",
|
||||
"Edit(**/id_ed25519*)",
|
||||
"Edit(**/.ssh/**)",
|
||||
"Edit(**/credentials)",
|
||||
"Edit(**/credentials.json)",
|
||||
"Edit(**/.aws/credentials)",
|
||||
"Edit(**/.azure/**)",
|
||||
"Edit(**/*.lock)",
|
||||
"Edit(**/package-lock.json)",
|
||||
"Edit(**/pnpm-lock.yaml)",
|
||||
"Edit(**/go.sum)",
|
||||
"Edit(**/node_modules/**)",
|
||||
"Bash(eval *)",
|
||||
"Bash(exec *)",
|
||||
"Bash(find * -delete*)",
|
||||
@@ -236,7 +250,7 @@
|
||||
"disableBypassPermissionsMode": "disable",
|
||||
"additionalDirectories": []
|
||||
},
|
||||
"model": "claude-fable-5[1m]",
|
||||
"model": "opus[1m]",
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
|
||||
@@ -10,13 +10,17 @@
|
||||
"Bash(curl * | bash)" // pipe pattern — block code injection
|
||||
```
|
||||
|
||||
### Read / Write / Edit — gitignore syntax
|
||||
### Read / Edit — gitignore syntax
|
||||
```json
|
||||
"Read(**/.env)" // any .env in any subdirectory
|
||||
"Read(**/secrets/**)" // anything inside secrets/
|
||||
"Read(src/**/*.ts)" // all .ts under src/
|
||||
"Write(**/*.key)" // deny writing any .key file
|
||||
"Edit(**/*.key)" // deny writing any .key file — Edit covers
|
||||
// Write/Edit/MultiEdit/NotebookEdit
|
||||
```
|
||||
`Write(path)` rules are **inert**: file permission checks only match
|
||||
`Edit(path)`. Claude Code warns at startup for every `Write(glob)` rule.
|
||||
Always write the file-write ban as `Edit(...)`.
|
||||
|
||||
### WebFetch / WebSearch
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user