Diff
1diff --git a/src/core/rules.ts b/src/core/rules.ts
2index ddc5f2dc2092efb6dc7e62631a43a980fd5fee88..e9dcdd4291e3e57e2779adb15b14f2304827ac56 100644
3--- a/src/core/rules.ts
4+++ b/src/core/rules.ts
5@@ -1,7 +1,7 @@
6 import { DECISION_CATEGORIES } from "./types";
7
8 // Bump to invalidate all cached decisions when rules change.
9-export const POLICY_VERSION = 32;
10+export const POLICY_VERSION = 33;
11
12 // Trailing stderr redirections that are safe to strip before pattern matching.
13 // `2>&1` and `2>/dev/null` have no security implication but would otherwise
14@@ -79,7 +79,7 @@ export const HARD_ALLOW_PATTERNS: RegExp[] = [
15 /^sleep\s+[\d.]+\s*$/,
16
17 // `-n` checks syntax only; it never executes the script.
18- /^(ba)?sh\s+-n(?:\s+(?!.*\/\.)(?!.*\.\.)\S+)+\s*$/,
19+ /^(ba)?sh\s+-n(?:\s+\S+)*\s*$/,
20 ];
21
22 // Broader prefix patterns ported from the OpenCode config.
23diff --git a/test/core/deterministic.test.ts b/test/core/deterministic.test.ts
24new file mode 100644
25index 0000000000000000000000000000000000000000..08687870ac2ecacc901bba2f7c813aae483c7a07
26--- /dev/null
27+++ b/test/core/deterministic.test.ts
28@@ -0,0 +1,18 @@
29+import { expect, test } from "bun:test"
30+import { checkDeterministic } from "../../src/core/deterministic"
31+
32+test("allows sh syntax checks without restricting the source path", () => {
33+ for (const command of [
34+ "sh -n script.sh",
35+ "sh -n ./script.sh",
36+ "bash -n /tmp/script.sh",
37+ "bash -n ../script.sh",
38+ "bash -n",
39+ ]) {
40+ expect(checkDeterministic("bash", { command })).toMatchObject({ decision: "allow" })
41+ }
42+})
43+
44+test("keeps shell execution subject to confirmation", () => {
45+ expect(checkDeterministic("bash", { command: "bash script.sh -n" })).toMatchObject({ decision: "ask" })
46+})