83ddb3749e31c14d5f4127f26cabeabeb86a446b
- Author
- TheEdgeOfRage <git@theedgeofrage.com>
- Committer
- TheEdgeOfRage <git@theedgeofrage.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/src/core/audit.ts b/src/core/audit.ts
2index b1b8aa4778ca5a575b4f7a4760552105bdb5553b..ac8ed0fd79caf14c528cfc3b794a38be1aa7f117 100644
3--- a/src/core/audit.ts
4+++ b/src/core/audit.ts
5@@ -1,6 +1,6 @@
6 import { appendFile, mkdir } from "node:fs/promises";
7 import { dirname } from "node:path";
8-import { POLICY_VERSION } from "./rules";
9+import { POLICY_VERSION } from "./normalize";
10 import type { DecisionAudit } from "./types";
11
12 export function createJsonlDecisionAudit(file: string): DecisionAudit {
13diff --git a/src/core/cache.ts b/src/core/cache.ts
14index e85e9ed26d7d437de67e2b0b9644229ba540b473..1f937de0af4d5947dd34a463ca632032d2f30f26 100644
15--- a/src/core/cache.ts
16+++ b/src/core/cache.ts
17@@ -1,6 +1,6 @@
18 import { appendFile, mkdir, readFile } from "node:fs/promises";
19 import { dirname } from "node:path";
20-import { POLICY_VERSION } from "./rules";
21+import { POLICY_VERSION } from "./normalize";
22 import type { CacheEntry, Decision, DecisionCache } from "./types";
23
24 type StoredCacheEntry = CacheEntry & {
25diff --git a/src/core/deterministic.ts b/src/core/deterministic.ts
26index c8615ed1c43ab76b6f803e5c595f28a214b091bd..f07a33a64e51fc51fbfa361a531b569054da6d7c 100644
27--- a/src/core/deterministic.ts
28+++ b/src/core/deterministic.ts
29@@ -2,9 +2,99 @@ import { lstatSync, readlinkSync, statSync } from "node:fs";
30 import { dirname, isAbsolute, resolve } from "node:path";
31 import { parseBash, type BashCommand, type BashNode, type BashParseResult } from "./bash";
32 import { resolveBashGlob } from "./bash-glob";
33-import { ALLOW_COMMANDS } from "./rules";
34 import type { Decision } from "./types";
35
36+export const ALLOW_COMMANDS: ReadonlySet<string> = new Set([
37+ "./gradlew",
38+ "base64",
39+ "break",
40+ "bun",
41+ "bunx",
42+ "cargo",
43+ "cat",
44+ "cd",
45+ "chmod",
46+ "chown",
47+ "composer",
48+ "continue",
49+ "cp",
50+ "cut",
51+ "date",
52+ "deno",
53+ "dirname",
54+ "dotnet",
55+ "echo",
56+ "elixir",
57+ "exit",
58+ "file",
59+ "gofmt",
60+ "git",
61+ "getent",
62+ "go",
63+ "golangci-lint",
64+ "grep",
65+ "head",
66+ "id",
67+ "jar",
68+ "java",
69+ "javac",
70+ "jq",
71+ "kotlinc",
72+ "ln",
73+ "lsof",
74+ "ls",
75+ "make",
76+ "mix",
77+ "mkdir",
78+ "mvn",
79+ "mv",
80+ "netstat",
81+ "nl",
82+ "node",
83+ "npm",
84+ "npx",
85+ "php",
86+ "pip",
87+ "pip3",
88+ "pnpm",
89+ "poetry",
90+ "printf",
91+ "ps",
92+ "pwd",
93+ "python",
94+ "python3",
95+ "qmd",
96+ "readlink",
97+ "rg",
98+ "rmdir",
99+ "rm",
100+ "ruby",
101+ "rustc",
102+ "sbt",
103+ "scala",
104+ "sleep",
105+ "sort",
106+ "ss",
107+ "stat",
108+ "strings",
109+ "swift",
110+ "tail",
111+ "tc",
112+ "tee",
113+ "touch",
114+ "tree",
115+ "tr",
116+ "true",
117+ "type",
118+ "uniq",
119+ "uv",
120+ "wc",
121+ "whereis",
122+ "which",
123+ "yarn",
124+ "yarnpkg",
125+]);
126+
127 const SYSTEM_DIRECTORIES = [
128 "/bin",
129diff --git a/src/core/index.ts b/src/core/index.ts
130index ce4318e3b8dee42f787a3c2e8c61b52d5f1174a5..d6053377b4c5b502e24da9a765279d266468ba35 100644
131--- a/src/core/index.ts
132+++ b/src/core/index.ts
133@@ -7,5 +7,4 @@ export * from "./normalize";
134 export * from "./pipeline";
135 export * from "./providers";
136 export * from "./review";
137-export * from "./rules";
138 export * from "./types";
139diff --git a/src/core/normalize.ts b/src/core/normalize.ts
140index 646ad24bda2c3f316d872bfea2912f004f9d2929..2376ca8131727661d503e059b79865721953d7ef 100644
141--- a/src/core/normalize.ts
142+++ b/src/core/normalize.ts
143@@ -1,7 +1,9 @@
144 import { createHash } from "node:crypto";
145-import { POLICY_VERSION } from "./rules";
146 import type { PolicyRequest } from "./types";
147
148+// Bump to invalidate all cached decisions when rules change.
149+export const POLICY_VERSION = 39;
150+
151 const SENSITIVE_KEY_RE = /(?:api[-_]?key|auth(?:entication|orization)?|credential|cookie|password|secret|token)/i;
152 const SENSITIVE_ARGUMENT_RE =
153 /((?:^|\s)(?:-u|-H)(?:=|\s*)|(?:^|\s)--(?:api[-_]?key|authorization|cookie|password|secret|token|user|proxy-user|header)(?:=|\s+))(?:(?:"(?:[^"\\]|\\.)*")|(?:'(?:[^'\\]|\\.)*')|\S+)/gi;
154diff --git a/src/core/review.ts b/src/core/review.ts
155index f997b9676e7a177c01ea21df57779756934cf5d9..26726380db9964e724a89db599e3adabfcad19bb 100644
156--- a/src/core/review.ts
157+++ b/src/core/review.ts
158@@ -1,6 +1,6 @@
159 import { callLlamaReviewer } from "./providers";
160-import { llmPolicyPrompt } from "./rules";
161 import {
162+ DECISION_CATEGORIES,
163 isDecisionCategory,
164 type Decision,
165 type LLMEvaluationResult,
166@@ -22,6 +22,65 @@ export type PiReviewerCaller = (
167 context: PolicyContext,
168 ) => Promise<string>;
169
170+const EXTERNAL_DIRECTORIES_PLACEHOLDER = "<<EXTERNAL_DIRECTORIES>>";
171+
172+export const LLM_POLICY_PROMPT = `Classify the serialized tool call as allow or ask.
173+
174+Decision procedure:
175+1. Return ask only when the actual operation directly does a listed risk below.
176+2. Otherwise return allow.
177+
178+Ask only for:
179+- Privilege escalation: sudo, su, or doas.
180+- Wide destruction, such as rm -rf of /, /etc, /usr, or ~/.
181+- Direct reading or exposure of an identified secret: a private SSH key, cloud credential file, .env file, or file explicitly named credentials or token.
182+- Editing system (non-user) files, outside of the current working directory tree, or not on the list of allowed directories.
183+- Remote infrastructure, data, access, or deployment mutation: create, update, delete, grant, revoke, apply, or execute a write on production databases, Kubernetes, or cloud providers.
184+- Irreversibly destructive Git operations, such as reset --hard or checking out files.
185+- When a command attempts to search the entire (and only *THE ENTIRE*) home directory for something, since that's very slow and rarely needed.
186+- Stopping the Herdr server with \`herdr server stop\`.
187+
188diff --git a/src/core/rules.ts b/src/core/rules.ts
189deleted file mode 100644
190index 5478a044bd65ab5cf4bd140d4343fba7b21e8eba..0000000000000000000000000000000000000000
191--- a/src/core/rules.ts
192+++ /dev/null
193@@ -1,156 +0,0 @@
194-import { DECISION_CATEGORIES } from "./types";
195-
196-// Bump to invalidate all cached decisions when rules change.
197-export const POLICY_VERSION = 39;
198-
199-// Local commands that can run without LLM review. Secret paths, destructive
200-// Git operations, system-file changes, and shell control syntax are checked first.
201-export const ALLOW_COMMANDS: ReadonlySet<string> = new Set([
202- "./gradlew",
203- "base64",
204- "break",
205- "bun",
206- "bunx",
207- "cargo",
208- "cat",
209- "cd",
210- "chmod",
211- "chown",
212- "composer",
213- "continue",
214- "cp",
215- "cut",
216- "date",
217- "deno",
218- "dirname",
219- "dotnet",
220- "echo",
221- "elixir",
222- "exit",
223- "file",
224- "gofmt",
225- "git",
226- "getent",
227- "go",
228- "golangci-lint",
229- "grep",
230- "head",
231- "id",
232- "jar",
233- "java",
234- "javac",
235- "jq",
236- "kotlinc",
237- "ln",
238- "lsof",
239- "ls",
240- "make",
241- "mix",
242- "mkdir",
243- "mvn",
244- "mv",
245- "netstat",
246- "nl",
247- "node",
248- "npm",
249- "npx",
250- "php",
251- "pip",
252- "pip3",
253- "pnpm",
254- "poetry",
255- "printf",
256- "ps",
257- "pwd",
258- "python",
259- "python3",
260- "qmd",
261- "readlink",
262- "rg",
263- "rmdir",
264- "rm",
265- "ruby",
266- "rustc",
267- "sbt",
268- "scala",
269- "sleep",
270- "sort",
271- "ss",
272- "stat",
273- "strings",
274- "swift",
275- "tail",
276- "tc",
277- "tee",
278- "touch",
279- "tree",
280- "tr",
281- "true",
282- "type",
283- "uniq",
284- "uv",
285- "wc",
286- "whereis",
287- "which",
288- "yarn",
289- "yarnpkg",
290-]);
291-
292-const EXTERNAL_DIRECTORIES_PLACEHOLDER = "<<EXTERNAL_DIRECTORIES>>";
293diff --git a/test/core/deterministic.test.ts b/test/core/deterministic.test.ts
294index 031ba91c4ffce51db02780de2743a7ecfd714804..efcc888f75f0329cad5ab5c6c7799bd4fe2791df 100644
295--- a/test/core/deterministic.test.ts
296+++ b/test/core/deterministic.test.ts
297@@ -41,7 +41,7 @@ const allowed = [
298 "git commit -m 'sudo rm -rf /'",
299 "cp README.md README.copy",
300 "bun test",
301- "sed -n '10,20p' src/core/rules.ts",
302+ "sed -n '10,20p' src/core/deterministic.ts",
303 "find src -type f -name '*.ts'",
304 "docker compose ps --all",
305 "systemctl --user is-active pi.service",