diff --git a/packages/coding-agent/src/step/command-policy.ts b/packages/coding-agent/src/step/command-policy.ts index b5d743d..29c83df 100644 --- a/packages/coding-agent/src/step/command-policy.ts +++ b/packages/coding-agent/src/step/command-policy.ts @@ -94,12 +94,13 @@ const COMMAND_APPROVAL_RULES: readonly CommandApprovalRule[] = [ { id: "recursive-force-remove", kind: "shell", matches: isRecursiveForceRemove }, { id: "system-lifecycle", kind: "shell", matches: isLifecycleCommand }, { id: "format-filesystem", kind: "pattern", pattern: /\bmkfs(?:\.[\w.-]+)?\b/iu }, - { id: "copy-device", kind: "pattern", pattern: /\bdd\s+if=/iu }, - { id: "truncate-device", kind: "pattern", pattern: /\b:>\s*\/dev\//u }, + { id: "copy-device", kind: "pattern", pattern: /\bdd\b[^;&|\n]*\bif=/iu }, + { id: "truncate-device", kind: "pattern", pattern: /(?:^|[;&|]\s*):\s*>\s*\/dev\//imu }, { id: "destructive-git", kind: "pattern", - pattern: /\bgit\s+(?:reset\s+--hard|clean\s+-[^\n]*f|push\s+[^\n]*--force(?:-with-lease)?)/iu, + pattern: + /\bgit\s+(?:reset\s+--hard|clean\s+-[^\n]*f|push\s+[^\n]*(?:--force(?:-with-lease(?:=\S*)?)?|-f)(?=\s|$))/iu, }, { id: "destructive-sql", kind: "pattern", pattern: /\b(?:drop\s+database|truncate\s+table)\b/iu }, ]; diff --git a/packages/coding-agent/test/step-command-policy.test.ts b/packages/coding-agent/test/step-command-policy.test.ts index a022a0b..ef6e64d 100644 --- a/packages/coding-agent/test/step-command-policy.test.ts +++ b/packages/coding-agent/test/step-command-policy.test.ts @@ -112,7 +112,13 @@ describe("mandatory command approval", () => { it.each([ "mkfs.ext4 /dev/test", "dd if=image of=/dev/test", + "dd of=/dev/test if=image", "git reset --hard", + "git push -f origin main", + "git push origin main -f", + "git push --force-with-lease=main:expected origin main", + ":> /dev/test", + ": >/dev/test", "sudo reboot", "sh -c 'shutdown now'", ])("preserves existing hazardous commands: %s", (command) => {