Skip to content

Commit 98cc059

Browse files
committed
fix: align the command bans across the two agent policy files
The two files carry the same block list in two languages and had drifted: expanding every prefix_rule into its concrete argv prefixes and matching both directions turned up 24 asymmetries — 22 commands Codex forbids that Claude Code only prompted for (git filter-repo, the rm flag spellings, fdisk/parted, redis-cli flush*, the npm/yarn/bun db: scripts, the pnpm/bunx prisma launchers, aws ec2 delete-volume/delete-snapshot, aws rds delete-db-cluster), and 2 the other way (prisma migrate reset, prisma db push --force-reset). Bare "prisma migrate reset" never matched: folding the bare form into the launcher alternation demanded four argv tokens where the command has three. It is now its own rule, as is "prisma db push --force-reset". Nothing is loosened — the allow list is untouched and every change is an addition or a widening. The one remaining difference is structural and now says so in the Codex header: Claude Code matches a command-string prefix, so "aws iam delete-:*" covers verbs AWS has not shipped yet, while a prefix_rule matches whole argv tokens and can only enumerate today's. Refs #38
1 parent d6d8201 commit 98cc059

2 files changed

Lines changed: 107 additions & 6 deletions

File tree

.claude/settings.json

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"Bash(git checkout --:*)",
2929
"Bash(git branch -D:*)",
3030
"Bash(git filter-branch:*)",
31+
"Bash(git filter-repo:*)",
3132
"Bash(git reflog expire:*)",
3233
"Bash(git gc --prune=now:*)",
3334
"Bash(git stash clear:*)",
@@ -38,20 +39,40 @@
3839
"Bash(gh release delete:*)",
3940
"Bash(gh secret delete:*)",
4041
"Bash(rm -rf /:*)",
42+
"Bash(rm -fr /:*)",
43+
"Bash(rm -Rf /:*)",
44+
"Bash(rm -fR /:*)",
4145
"Bash(sudo rm:*)",
4246
"Bash(chmod -R:*)",
4347
"Bash(chown -R:*)",
4448
"Bash(dd:*)",
4549
"Bash(mkfs:*)",
4650
"Bash(shred:*)",
51+
"Bash(fdisk:*)",
52+
"Bash(parted:*)",
4753
"Bash(php artisan migrate:fresh:*)",
4854
"Bash(php artisan migrate:reset:*)",
4955
"Bash(php artisan db:wipe:*)",
5056
"Bash(prisma migrate reset:*)",
5157
"Bash(npx prisma migrate reset:*)",
58+
"Bash(pnpm prisma migrate reset:*)",
59+
"Bash(bunx prisma migrate reset:*)",
5260
"Bash(prisma db push --force-reset:*)",
53-
"Bash(pnpm db:fresh)",
54-
"Bash(pnpm db:reset)",
61+
"Bash(npx prisma db push --force-reset:*)",
62+
"Bash(pnpm prisma db push --force-reset:*)",
63+
"Bash(bunx prisma db push --force-reset:*)",
64+
"Bash(pnpm db:fresh:*)",
65+
"Bash(pnpm db:reset:*)",
66+
"Bash(npm db:fresh:*)",
67+
"Bash(npm db:reset:*)",
68+
"Bash(yarn db:fresh:*)",
69+
"Bash(yarn db:reset:*)",
70+
"Bash(bun db:fresh:*)",
71+
"Bash(bun db:reset:*)",
72+
"Bash(redis-cli flushall:*)",
73+
"Bash(redis-cli flushdb:*)",
74+
"Bash(redis-cli FLUSHALL:*)",
75+
"Bash(redis-cli FLUSHDB:*)",
5576
"Bash(terraform destroy:*)",
5677
"Bash(terraform apply -auto-approve:*)",
5778
"Bash(terraform state rm:*)",
@@ -67,7 +88,10 @@
6788
"Bash(aws s3 rb:*)",
6889
"Bash(aws s3 rm:*)",
6990
"Bash(aws ec2 terminate-instances:*)",
91+
"Bash(aws ec2 delete-volume:*)",
92+
"Bash(aws ec2 delete-snapshot:*)",
7093
"Bash(aws rds delete-db-instance:*)",
94+
"Bash(aws rds delete-db-cluster:*)",
7195
"Bash(aws cloudformation delete-stack:*)",
7296
"Bash(aws iam delete-:*)"
7397
]

.codex/rules/default.rules

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@
77
#
88
# Verify after editing:
99
# codex execpolicy check --pretty --rules .codex/rules/default.rules -- <command>
10+
#
11+
# Verify the two files still ban the same set:
12+
# pnpm check:policy
13+
#
14+
# Two differences from .claude/settings.json are deliberate, because the rule
15+
# languages differ rather than the intent:
16+
#
17+
# Claude Code matches a prefix of the command *string*; a prefix_rule below
18+
# matches whole argv *tokens*. So an open-ended ban such as
19+
# "Bash(aws iam delete-:*)" — every delete verb, present and future — has no
20+
# equivalent here, and the aws iam rule enumerates the verbs AWS ships today
21+
# instead. That enumeration is a snapshot, and no rule closes the rest.
22+
#
23+
# Neither language normalises flag order or case, so a command is banned only
24+
# in the spellings written down. "rm -rf /" and "rm -fr /" are both here;
25+
# "rm -r -f /" and "redis-cli FlushAll" are not, and enumerating permutations
26+
# never ends. Both files list the same spellings — that parity is what
27+
# pnpm check:policy proves; it is not a claim that the set is complete.
1028

1129
# ─────────────────────────── git: work and history ───────────────────────────
1230

@@ -148,14 +166,41 @@ prefix_rule(
148166
not_match = ["php artisan migrate", "php artisan tinker"],
149167
)
150168

169+
# The bare and launcher-prefixed forms are separate rules: a pattern matches a
170+
# fixed number of argv tokens, so folding "prisma" into the launcher list would
171+
# demand four tokens and never match the three-token bare command.
172+
prefix_rule(
173+
pattern = ["prisma", "migrate", "reset"],
174+
decision = "forbidden",
175+
justification = "Drops and recreates the database",
176+
match = ["prisma migrate reset", "prisma migrate reset --force"],
177+
not_match = ["prisma migrate dev", "prisma generate"],
178+
)
179+
151180
prefix_rule(
152-
pattern = [["prisma", "npx", "pnpm", "bunx"], "prisma", "migrate", "reset"],
181+
pattern = [["npx", "pnpm", "bunx"], "prisma", "migrate", "reset"],
153182
decision = "forbidden",
154183
justification = "Drops and recreates the database",
155184
match = ["npx prisma migrate reset", "bunx prisma migrate reset"],
156185
not_match = ["npx prisma migrate dev", "npx prisma generate"],
157186
)
158187

188+
prefix_rule(
189+
pattern = ["prisma", "db", "push", "--force-reset"],
190+
decision = "forbidden",
191+
justification = "Resets the database before pushing the schema",
192+
match = ["prisma db push --force-reset"],
193+
not_match = ["prisma db push", "prisma db seed"],
194+
)
195+
196+
prefix_rule(
197+
pattern = [["npx", "pnpm", "bunx"], "prisma", "db", "push", "--force-reset"],
198+
decision = "forbidden",
199+
justification = "Resets the database before pushing the schema",
200+
match = ["npx prisma db push --force-reset"],
201+
not_match = ["npx prisma db push", "npx prisma db seed"],
202+
)
203+
159204
prefix_rule(
160205
pattern = [["pnpm", "npm", "yarn", "bun"], ["db:fresh", "db:reset"]],
161206
decision = "forbidden",
@@ -240,10 +285,42 @@ prefix_rule(
240285
not_match = ["aws cloudformation describe-stacks"],
241286
)
242287

288+
# .claude/settings.json bans "aws iam delete-" as an open string prefix, which
289+
# covers verbs AWS has not shipped yet. A pattern matches whole argv tokens, so
290+
# the closest form is this enumeration of today's delete surface — a snapshot,
291+
# and the deliberate structural difference named in the header.
243292
prefix_rule(
244-
pattern = ["aws", "iam", ["delete-user", "delete-role", "delete-policy", "delete-group"]],
293+
pattern = [
294+
"aws",
295+
"iam",
296+
[
297+
"delete-access-key",
298+
"delete-account-alias",
299+
"delete-account-password-policy",
300+
"delete-group",
301+
"delete-group-policy",
302+
"delete-instance-profile",
303+
"delete-login-profile",
304+
"delete-open-id-connect-provider",
305+
"delete-policy",
306+
"delete-policy-version",
307+
"delete-role",
308+
"delete-role-permissions-boundary",
309+
"delete-role-policy",
310+
"delete-saml-provider",
311+
"delete-server-certificate",
312+
"delete-service-linked-role",
313+
"delete-service-specific-credential",
314+
"delete-signing-certificate",
315+
"delete-ssh-public-key",
316+
"delete-user",
317+
"delete-user-permissions-boundary",
318+
"delete-user-policy",
319+
"delete-virtual-mfa-device",
320+
],
321+
],
245322
decision = "forbidden",
246-
justification = "Removes identities other systems depend on",
247-
match = ["aws iam delete-role --role-name deploy"],
323+
justification = "Removes identities and credentials other systems depend on",
324+
match = ["aws iam delete-role --role-name deploy", "aws iam delete-access-key --access-key-id AKIA"],
248325
not_match = ["aws iam list-roles", "aws iam get-role --role-name deploy"],
249326
)

0 commit comments

Comments
 (0)