@pentoshi/clai 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +9 -17
  2. package/dist/agent/context-manager.d.ts +27 -0
  3. package/dist/agent/context-manager.js +75 -0
  4. package/dist/agent/context-manager.js.map +1 -0
  5. package/dist/agent/runner.d.ts +21 -1
  6. package/dist/agent/runner.js +176 -73
  7. package/dist/agent/runner.js.map +1 -1
  8. package/dist/commands/doctor.js +20 -2
  9. package/dist/commands/doctor.js.map +1 -1
  10. package/dist/commands/update.js +11 -2
  11. package/dist/commands/update.js.map +1 -1
  12. package/dist/index.js +156 -5
  13. package/dist/index.js.map +1 -1
  14. package/dist/llm/anthropic.js +29 -38
  15. package/dist/llm/anthropic.js.map +1 -1
  16. package/dist/llm/gemini.js +31 -40
  17. package/dist/llm/gemini.js.map +1 -1
  18. package/dist/llm/http.d.ts +21 -0
  19. package/dist/llm/http.js +140 -1
  20. package/dist/llm/http.js.map +1 -1
  21. package/dist/llm/ollama.js +18 -27
  22. package/dist/llm/ollama.js.map +1 -1
  23. package/dist/llm/router.d.ts +7 -0
  24. package/dist/llm/router.js +14 -23
  25. package/dist/llm/router.js.map +1 -1
  26. package/dist/modes/agent.d.ts +4 -2
  27. package/dist/modes/agent.js +2 -2
  28. package/dist/modes/agent.js.map +1 -1
  29. package/dist/modes/ask.js +3 -4
  30. package/dist/modes/ask.js.map +1 -1
  31. package/dist/os/pkgmgr.d.ts +7 -1
  32. package/dist/os/pkgmgr.js +97 -18
  33. package/dist/os/pkgmgr.js.map +1 -1
  34. package/dist/prompts/index.d.ts +7 -0
  35. package/dist/prompts/index.js +12 -4
  36. package/dist/prompts/index.js.map +1 -1
  37. package/dist/repl.d.ts +1 -0
  38. package/dist/repl.js +283 -43
  39. package/dist/repl.js.map +1 -1
  40. package/dist/safety/classifier.d.ts +5 -1
  41. package/dist/safety/classifier.js +244 -88
  42. package/dist/safety/classifier.js.map +1 -1
  43. package/dist/safety/patterns.d.ts +48 -1
  44. package/dist/safety/patterns.js +140 -7
  45. package/dist/safety/patterns.js.map +1 -1
  46. package/dist/store/config.d.ts +21 -3
  47. package/dist/store/config.js +28 -9
  48. package/dist/store/config.js.map +1 -1
  49. package/dist/store/history.d.ts +9 -0
  50. package/dist/store/history.js +58 -1
  51. package/dist/store/history.js.map +1 -1
  52. package/dist/store/keys.d.ts +2 -1
  53. package/dist/store/keys.js +7 -3
  54. package/dist/store/keys.js.map +1 -1
  55. package/dist/store/logs.d.ts +7 -0
  56. package/dist/store/logs.js +39 -1
  57. package/dist/store/logs.js.map +1 -1
  58. package/dist/store/project.d.ts +1 -0
  59. package/dist/store/project.js +34 -9
  60. package/dist/store/project.js.map +1 -1
  61. package/dist/store/scope.d.ts +29 -0
  62. package/dist/store/scope.js +113 -0
  63. package/dist/store/scope.js.map +1 -0
  64. package/dist/tools/fs.d.ts +6 -2
  65. package/dist/tools/fs.js +99 -87
  66. package/dist/tools/fs.js.map +1 -1
  67. package/dist/tools/http.d.ts +5 -3
  68. package/dist/tools/http.js +170 -31
  69. package/dist/tools/http.js.map +1 -1
  70. package/dist/tools/policies/output-policy.d.ts +13 -0
  71. package/dist/tools/policies/output-policy.js +56 -0
  72. package/dist/tools/policies/output-policy.js.map +1 -0
  73. package/dist/tools/reducers/ffuf.d.ts +6 -0
  74. package/dist/tools/reducers/ffuf.js +74 -0
  75. package/dist/tools/reducers/ffuf.js.map +1 -0
  76. package/dist/tools/reducers/generic.d.ts +2 -0
  77. package/dist/tools/reducers/generic.js +60 -0
  78. package/dist/tools/reducers/generic.js.map +1 -0
  79. package/dist/tools/reducers/gobuster.d.ts +2 -0
  80. package/dist/tools/reducers/gobuster.js +36 -0
  81. package/dist/tools/reducers/gobuster.js.map +1 -0
  82. package/dist/tools/reducers/httpx.d.ts +2 -0
  83. package/dist/tools/reducers/httpx.js +38 -0
  84. package/dist/tools/reducers/httpx.js.map +1 -0
  85. package/dist/tools/reducers/nmap.d.ts +7 -0
  86. package/dist/tools/reducers/nmap.js +82 -0
  87. package/dist/tools/reducers/nmap.js.map +1 -0
  88. package/dist/tools/reducers/nuclei.d.ts +2 -0
  89. package/dist/tools/reducers/nuclei.js +51 -0
  90. package/dist/tools/reducers/nuclei.js.map +1 -0
  91. package/dist/tools/reducers/sqlmap.d.ts +2 -0
  92. package/dist/tools/reducers/sqlmap.js +39 -0
  93. package/dist/tools/reducers/sqlmap.js.map +1 -0
  94. package/dist/tools/reducers/subdomains.d.ts +6 -0
  95. package/dist/tools/reducers/subdomains.js +31 -0
  96. package/dist/tools/reducers/subdomains.js.map +1 -0
  97. package/dist/tools/reducers/types.d.ts +14 -0
  98. package/dist/tools/reducers/types.js +2 -0
  99. package/dist/tools/reducers/types.js.map +1 -0
  100. package/dist/tools/registry.d.ts +1 -1
  101. package/dist/tools/registry.js +223 -79
  102. package/dist/tools/registry.js.map +1 -1
  103. package/dist/tools/shell.d.ts +45 -4
  104. package/dist/tools/shell.js +419 -88
  105. package/dist/tools/shell.js.map +1 -1
  106. package/dist/tools/validate.d.ts +37 -0
  107. package/dist/tools/validate.js +144 -0
  108. package/dist/tools/validate.js.map +1 -0
  109. package/dist/types.d.ts +7 -15
  110. package/dist/ui/keys.d.ts +21 -0
  111. package/dist/ui/keys.js +13 -0
  112. package/dist/ui/keys.js.map +1 -0
  113. package/dist/ui/output-pane.d.ts +31 -0
  114. package/dist/ui/output-pane.js +81 -0
  115. package/dist/ui/output-pane.js.map +1 -0
  116. package/package.json +1 -1
@@ -1,8 +1,12 @@
1
1
  import type { RiskLevel, ToolCall } from "../types.js";
2
+ import { type EngagementScope } from "../store/scope.js";
2
3
  export interface RiskDecision {
3
4
  level: RiskLevel;
4
5
  reason: string;
5
6
  }
6
7
  export declare function isPrivateIpv4(value: string): boolean;
7
8
  export declare function isPentestToolCall(call: ToolCall): boolean;
8
- export declare function classifyToolCall(call: ToolCall): RiskDecision;
9
+ export interface ClassifyOptions {
10
+ scope?: EngagementScope | undefined;
11
+ }
12
+ export declare function classifyToolCall(call: ToolCall, options?: ClassifyOptions): RiskDecision;
@@ -1,9 +1,23 @@
1
1
  import net from "node:net";
2
- import { destructiveCommandPatterns, exfiltrationPatterns, networkScanTools, readOnlyShellCommands, } from "./patterns.js";
2
+ import { homedir } from "node:os";
3
+ import { resolve } from "node:path";
4
+ import { containsShellMetacharacter, destructiveCommandPatterns, exfiltrationPatterns, isSecretPath, networkScanTools, readOnlyShellCommands, subcommandSafeMap, commandHasMutatingArg, } from "./patterns.js";
5
+ import { isScopeActive, targetInScope, } from "../store/scope.js";
3
6
  function stringArg(args, key) {
4
7
  const value = args[key];
5
8
  return typeof value === "string" ? value : undefined;
6
9
  }
10
+ function expandTilde(path) {
11
+ if (path === "~")
12
+ return homedir();
13
+ if (path.startsWith("~/") || path.startsWith("~\\")) {
14
+ return resolve(homedir(), path.slice(2));
15
+ }
16
+ return path;
17
+ }
18
+ function resolveForSecretCheck(path) {
19
+ return resolve(expandTilde(path));
20
+ }
7
21
  export function isPrivateIpv4(value) {
8
22
  const candidate = value.split("/")[0] ?? value;
9
23
  // Handle hostnames — if it's not an IP, treat it as non-private (domain)
@@ -12,7 +26,10 @@ export function isPrivateIpv4(value) {
12
26
  if (net.isIP(candidate) === 6) {
13
27
  // IPv6 link-local (fe80::), loopback (::1), ULA (fc00::/7)
14
28
  const lower = candidate.toLowerCase();
15
- return lower === "::1" || lower.startsWith("fe80:") || lower.startsWith("fc") || lower.startsWith("fd");
29
+ return (lower === "::1" ||
30
+ lower.startsWith("fe80:") ||
31
+ lower.startsWith("fc") ||
32
+ lower.startsWith("fd"));
16
33
  }
17
34
  const parts = candidate.split(".").map((part) => Number(part));
18
35
  const [a, b] = parts;
@@ -31,50 +48,58 @@ export function isPrivateIpv4(value) {
31
48
  function commandContainsNetworkScanner(command) {
32
49
  return networkScanTools.some((tool) => new RegExp(`(^|\\s)${tool}(\\s|$)`, "i").test(command));
33
50
  }
34
- function isPrivateTarget(value) {
35
- const trimmed = value.trim().replace(/^https?:\/\//i, "");
36
- const host = trimmed.split(/[/:?#]/)[0] ?? trimmed;
37
- if (!host || host === "localhost" || host.endsWith(".localhost"))
38
- return true;
39
- if (host === "0.0.0.0")
40
- return true;
41
- return isPrivateIpv4(host);
42
- }
43
- function shellTokens(command) {
44
- return command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((token) => token.replace(/^["']|["']$/g, "")) ?? [];
45
- }
46
- function commandHasOwnershipFlag(command) {
47
- return shellTokens(command).includes("--i-own-this");
48
- }
49
- function extractNetworkTargets(command) {
50
- const targets = new Set();
51
- for (const match of command.matchAll(/https?:\/\/[^\s"'`<>]+/gi)) {
52
- targets.add(match[0]);
53
- }
54
- for (const match of command.matchAll(/\b(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?\b/g)) {
55
- targets.add(match[0]);
51
+ const PRIVATE_TLD_RE = /\.(?:local|internal|lan|home|corp|intranet|test|localdomain)$/i;
52
+ const URL_HOSTNAME_RE = /\bhttps?:\/\/([^\/\s:?#]+)/gi;
53
+ // A bareword domain anchored at a whitespace boundary on the left so we
54
+ // don't pick up file paths like `wordlists/common.txt`. The right side
55
+ // stays at \b so trailing punctuation doesn't trip us up.
56
+ const BARE_HOSTNAME_RE = /(?:^|\s)((?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63})\b/g;
57
+ function extractHostnameTokens(command) {
58
+ const tokens = [];
59
+ // First pull host parts out of any URL so https://example.com/FUZZ contributes "example.com"
60
+ let match;
61
+ URL_HOSTNAME_RE.lastIndex = 0;
62
+ while ((match = URL_HOSTNAME_RE.exec(command)) !== null) {
63
+ if (match[1])
64
+ tokens.push(match[1].replace(/\[|\]/g, "").split(":")[0]);
56
65
  }
57
- for (const token of shellTokens(command)) {
58
- if (token.startsWith("-") || token.includes("/") || token.includes("="))
59
- continue;
60
- if (/\.(?:txt|lst|list|json|xml|csv|log|html)$/i.test(token))
61
- continue;
62
- if (/^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(token))
63
- targets.add(token);
66
+ // Then capture bare-hostname tokens (eg `nmap example.com`). The leading
67
+ // boundary stops us picking up `path/to/common.txt` (which would
68
+ // otherwise look like a domain because of the `.txt` suffix).
69
+ BARE_HOSTNAME_RE.lastIndex = 0;
70
+ while ((match = BARE_HOSTNAME_RE.exec(command)) !== null) {
71
+ if (match[1])
72
+ tokens.push(match[1]);
64
73
  }
65
- return [...targets];
74
+ return tokens;
66
75
  }
67
- function containsPublicTarget(command) {
68
- const targets = extractNetworkTargets(command);
69
- if (targets.length === 0)
76
+ const FILEY_TLDS = new Set([
77
+ "txt", "log", "json", "yaml", "yml", "md", "html", "htm", "xml", "csv",
78
+ "sh", "py", "rb", "rs", "go", "js", "ts", "tsx", "jsx", "css", "scss",
79
+ "tar", "gz", "zip", "tgz", "pdf", "png", "jpg", "jpeg", "gif", "svg",
80
+ "exe", "dll", "so", "dylib", "ini", "conf", "lock", "toml", "env",
81
+ ]);
82
+ function isPublicHostname(host) {
83
+ const lower = host.toLowerCase();
84
+ if (!lower.includes("."))
70
85
  return false;
71
- return targets.some((target) => !isPrivateTarget(target));
72
- }
73
- function hasShellControlSyntax(command) {
74
- return /(?:[;&|`<>]|\$\(|\${)/.test(command);
86
+ if (lower === "localhost" || lower === "localhost.localdomain")
87
+ return false;
88
+ if (PRIVATE_TLD_RE.test(lower))
89
+ return false;
90
+ // Reject things that look like filenames (common.txt, package.json, etc.)
91
+ // even when they syntactically resemble a domain.
92
+ const tld = lower.split(".").pop() ?? "";
93
+ if (FILEY_TLDS.has(tld))
94
+ return false;
95
+ // Must contain at least one alphabetic TLD-like segment to count as a domain
96
+ return /\.[a-z]{2,63}$/i.test(lower);
75
97
  }
76
- function referencesSecretPath(command) {
77
- return /(?:^|\s)(?:~\/)?(?:\.ssh|\.gnupg|\.aws|\.kube|\.docker|\.env\b|id_rsa|id_ed25519|\.npmrc|\.pypirc|\.clai\/keys\.json)/i.test(command);
98
+ function containsPublicTarget(command) {
99
+ const ips = command.match(/\b(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?\b/g) ?? [];
100
+ if (ips.some((ip) => !isPrivateIpv4(ip)))
101
+ return true;
102
+ return extractHostnameTokens(command).some((host) => isPublicHostname(host));
78
103
  }
79
104
  export function isPentestToolCall(call) {
80
105
  if (call.name === "net.scan" || call.name === "pentest.recon")
@@ -84,37 +109,117 @@ export function isPentestToolCall(call) {
84
109
  const command = stringArg(call.args, "command") ?? "";
85
110
  return commandContainsNetworkScanner(command);
86
111
  }
87
- export function classifyToolCall(call) {
88
- if (call.name === "sysinfo" ||
89
- call.name === "fs.read" ||
112
+ /**
113
+ * Pull rough "path-looking" tokens out of a command string. Used so we can
114
+ * refuse to auto-execute commands that touch known secret paths even when
115
+ * the base command (cat, head, tail, less, etc.) is otherwise safe.
116
+ */
117
+ function extractPathLikeTokens(command) {
118
+ // Match tilde paths, absolute paths, and dotted relative paths.
119
+ const matches = command.match(/(?:~|\.{1,2}|\/)?[\w./~-]+/g) ?? [];
120
+ return matches.filter((token) => /[\\/~]/.test(token));
121
+ }
122
+ function commandTouchesSecretPath(command) {
123
+ return extractPathLikeTokens(command).some((token) => {
124
+ try {
125
+ return isSecretPath(resolveForSecretCheck(token));
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ });
131
+ }
132
+ /**
133
+ * Split a command line into [base, subcommand] respecting quotes minimally.
134
+ * We only need the first two whitespace-delimited tokens.
135
+ */
136
+ function baseAndSub(command) {
137
+ const tokens = command.trim().split(/\s+/);
138
+ const baseRaw = tokens[0] ?? "";
139
+ const base = baseRaw.replace(/^.*\//, "");
140
+ const sub = tokens[1];
141
+ return { base, sub };
142
+ }
143
+ function isReadOnlyBase(base) {
144
+ return readOnlyShellCommands.has(base);
145
+ }
146
+ function isSafeSubcommand(base, sub) {
147
+ if (!sub)
148
+ return false;
149
+ const allow = subcommandSafeMap[base];
150
+ if (!allow)
151
+ return false;
152
+ // Strip leading `--` so `--list` and `list` both work.
153
+ return allow.has(sub) || allow.has(sub.replace(/^--/, ""));
154
+ }
155
+ /**
156
+ * Extract the apparent target from a shell command that contains a scanner.
157
+ * Used to decide whether the target is covered by the active engagement
158
+ * scope. Falls back to the trailing token of the command if no obvious
159
+ * target argument is found.
160
+ */
161
+ function extractScanTarget(command) {
162
+ // URL-style targets first (eg `ffuf -u https://example.com/FUZZ`,
163
+ // `nuclei -u https://example.com`). The hostname is what scope cares about.
164
+ const urlMatch = /\bhttps?:\/\/([^\/\s:?#]+)/i.exec(command);
165
+ if (urlMatch?.[1]) {
166
+ return urlMatch[1].replace(/[\[\]]/g, "").split(":")[0];
167
+ }
168
+ const tokens = command.trim().split(/\s+/).filter(Boolean);
169
+ // Drop the first token (binary) and any leading flags.
170
+ const args = tokens.slice(1).filter((token) => !token.startsWith("-"));
171
+ // Many scanners take target as the trailing positional.
172
+ for (let i = args.length - 1; i >= 0; i -= 1) {
173
+ const arg = args[i];
174
+ if (/^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/.test(arg) ||
175
+ net.isIP(arg) ||
176
+ /^[0-9./]+$/.test(arg)) {
177
+ return arg;
178
+ }
179
+ }
180
+ return undefined;
181
+ }
182
+ export function classifyToolCall(call, options = {}) {
183
+ if (call.name === "fs.read" ||
90
184
  call.name === "fs.list" ||
91
185
  call.name === "fs.search") {
186
+ const pathArg = stringArg(call.args, "path");
187
+ if (pathArg) {
188
+ try {
189
+ if (isSecretPath(resolveForSecretCheck(pathArg))) {
190
+ return {
191
+ level: "block",
192
+ reason: "Path is a known secret location and cannot be read by the agent",
193
+ };
194
+ }
195
+ }
196
+ catch {
197
+ // resolve failed — fall through to safe
198
+ }
199
+ }
200
+ return { level: "safe", reason: "Read-only operation" };
201
+ }
202
+ if (call.name === "sysinfo") {
92
203
  return { level: "safe", reason: "Read-only operation" };
93
204
  }
205
+ if (call.name === "tool.batch") {
206
+ // The batch handler enforces a hard allowlist of read-only tools and a
207
+ // capped concurrency. Treat it as safe so batched recon can run without
208
+ // a per-call confirmation. If the caller smuggles in a non-safe tool,
209
+ // the handler rejects it.
210
+ return { level: "safe", reason: "Read-only batch dispatch" };
211
+ }
94
212
  if (call.name === "http.fetch") {
95
213
  const method = (stringArg(call.args, "method") ?? "GET").toUpperCase();
96
- const url = stringArg(call.args, "url") ?? "";
97
214
  if (method !== "GET" && method !== "HEAD") {
98
215
  return {
99
216
  level: "confirm",
100
- reason: "Non-GET HTTP requests can mutate remote systems",
101
- };
102
- }
103
- if (/169\.254\.169\.254|metadata\.google\.internal/i.test(url)) {
104
- return {
105
- level: "block",
106
- reason: "Cloud metadata endpoints are blocked",
107
- };
108
- }
109
- if (url && isPrivateTarget(url)) {
110
- return {
111
- level: "confirm",
112
- reason: "Fetching local or private network URLs requires confirmation",
217
+ reason: `HTTP ${method} is mutating and requires confirmation`,
113
218
  };
114
219
  }
115
220
  return {
116
221
  level: "safe",
117
- reason: "HTTP fetch is read-only with response size limits",
222
+ reason: "HTTP GET/HEAD is read-only",
118
223
  };
119
224
  }
120
225
  if (call.name === "shell.exec") {
@@ -131,55 +236,90 @@ export function classifyToolCall(call) {
131
236
  reason: "Command resembles secret or data exfiltration",
132
237
  };
133
238
  }
134
- if (referencesSecretPath(command)) {
239
+ if (commandTouchesSecretPath(command)) {
135
240
  return {
136
- level: "confirm",
137
- reason: "Command references a path that may contain secrets",
241
+ level: "block",
242
+ reason: "Command references a known secret path (e.g. ~/.ssh, ~/.clai/keys.json, .env)",
138
243
  };
139
244
  }
140
245
  if (commandContainsNetworkScanner(command) &&
141
- containsPublicTarget(command) &&
142
- !commandHasOwnershipFlag(command)) {
143
- return {
144
- level: "block",
145
- reason: "Public target scanning requires explicit --i-own-this authorization flag",
146
- };
246
+ containsPublicTarget(command)) {
247
+ // The legacy `--i-own-this` substring bypass is gone: a malicious target
248
+ // string could include that flag and trick the agent into running a
249
+ // public scan. Now the agent must have a configured engagement scope
250
+ // that explicitly covers the apparent target.
251
+ const target = extractScanTarget(command);
252
+ const scopeOk = isScopeActive(options.scope) && target
253
+ ? targetInScope(target, options.scope)
254
+ : false;
255
+ if (!scopeOk) {
256
+ return {
257
+ level: "block",
258
+ reason: "Public target scanning requires a configured engagement scope. Run `clai scope new` to authorize specific domains/CIDRs.",
259
+ };
260
+ }
147
261
  }
148
262
  // Pentest scan tools always require confirmation even against private targets
149
263
  if (commandContainsNetworkScanner(command)) {
150
- return { level: "confirm", reason: "Security scan tool requires confirmation" };
264
+ return {
265
+ level: "confirm",
266
+ reason: "Security scan tool requires confirmation",
267
+ };
151
268
  }
152
- // Read-only / info commands are safe to auto-execute
153
- const base = command.trim().split(/\s+/)[0]?.replace(/^.*\//, "") ?? "";
154
- if (hasShellControlSyntax(command)) {
155
- return { level: "confirm", reason: "Shell control syntax requires confirmation" };
269
+ // Compound commands (pipes, redirects, &&, ||, sudo, command substitution)
270
+ // always require confirmation because the arguments can mutate state or
271
+ // exfiltrate data even when the base command is otherwise safe.
272
+ if (containsShellMetacharacter(command)) {
273
+ return {
274
+ level: "confirm",
275
+ reason: "Compound command (pipes, redirects, &&, ||, sudo, or command substitution) requires confirmation",
276
+ };
277
+ }
278
+ // Mutating-argument patterns (sed -i, awk system(...), find -exec/-delete,
279
+ // git config --global, npm config set, docker run, kubectl apply, ...).
280
+ // These bypass the read-only base check because their *arguments* mutate
281
+ // state or escape into another shell.
282
+ if (commandHasMutatingArg(command)) {
283
+ return {
284
+ level: "confirm",
285
+ reason: "Command argument mutates state or escapes into another shell (sed -i, awk system(), find -exec/-delete, git config --global, npm config set, docker/kubectl mutators)",
286
+ };
156
287
  }
157
- if (readOnlyShellCommands.has(base)) {
288
+ // Read-only / info commands are safe to auto-execute
289
+ const { base, sub } = baseAndSub(command);
290
+ if (isReadOnlyBase(base)) {
158
291
  return { level: "safe", reason: "Read-only command" };
159
292
  }
293
+ if (isSafeSubcommand(base, sub)) {
294
+ return { level: "safe", reason: `Read-only ${base} subcommand` };
295
+ }
160
296
  return { level: "confirm", reason: "Shell commands require confirmation" };
161
297
  }
162
298
  if (call.name === "net.scan") {
163
299
  const target = stringArg(call.args, "target") ?? "";
164
- const ownsTarget = call.args.iOwnThis === true || call.args.own === true;
165
- if (target && !isPrivateTarget(target) && !ownsTarget) {
166
- return {
167
- level: "block",
168
- reason: "Public target scan requires ownership confirmation",
169
- };
300
+ if (target && !isPrivateIpv4(target)) {
301
+ const scopeOk = isScopeActive(options.scope) && targetInScope(target, options.scope);
302
+ if (!scopeOk) {
303
+ return {
304
+ level: "block",
305
+ reason: "Public IP scan requires a configured engagement scope covering this target",
306
+ };
307
+ }
170
308
  }
171
309
  return { level: "confirm", reason: "Network scans require confirmation" };
172
310
  }
173
311
  if (call.name === "pentest.recon") {
174
312
  const target = stringArg(call.args, "target") ?? "";
175
- const ownsTarget = call.args.iOwnThis === true || call.args.own === true;
176
- if (target &&
177
- !isPrivateTarget(target) &&
178
- !ownsTarget) {
179
- return {
180
- level: "block",
181
- reason: "Public target recon requires ownership confirmation",
182
- };
313
+ const targetHost = target.split("/")[0] ?? target;
314
+ const isPublic = target && (net.isIP(targetHost) ? !isPrivateIpv4(target) : true);
315
+ if (isPublic) {
316
+ const scopeOk = isScopeActive(options.scope) && targetInScope(target, options.scope);
317
+ if (!scopeOk) {
318
+ return {
319
+ level: "block",
320
+ reason: "Public target recon requires a configured engagement scope covering this target",
321
+ };
322
+ }
183
323
  }
184
324
  return {
185
325
  level: "confirm",
@@ -187,6 +327,22 @@ export function classifyToolCall(call) {
187
327
  };
188
328
  }
189
329
  if (call.name === "fs.write" || call.name === "pkg.install") {
330
+ if (call.name === "fs.write") {
331
+ const pathArg = stringArg(call.args, "path");
332
+ if (pathArg) {
333
+ try {
334
+ if (isSecretPath(resolveForSecretCheck(pathArg))) {
335
+ return {
336
+ level: "block",
337
+ reason: "Refusing to write to a known secret path",
338
+ };
339
+ }
340
+ }
341
+ catch {
342
+ // fall through
343
+ }
344
+ }
345
+ }
190
346
  return {
191
347
  level: "confirm",
192
348
  reason: "Mutating operation requires confirmation",
@@ -1 +1 @@
1
- {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/safety/classifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/safety/classifier.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,52 @@
1
1
  export declare const destructiveCommandPatterns: RegExp[];
2
2
  export declare const exfiltrationPatterns: RegExp[];
3
3
  export declare const networkScanTools: string[];
4
- /** Read-only commands that are safe to auto-execute without user confirmation */
4
+ /**
5
+ * Commands that never mutate state and never expose secrets through their
6
+ * default arguments. Powerful commands whose arguments can leak data
7
+ * (cat, env, python, node, git, npm, pip, tee, xargs, curl, wget) are
8
+ * intentionally NOT here — they fall through to `subcommandSafeMap` or
9
+ * to the metacharacter-aware confirm path in classifier.ts.
10
+ */
5
11
  export declare const readOnlyShellCommands: Set<string>;
12
+ /**
13
+ * Subcommand allowlist for powerful CLIs. The classifier treats
14
+ * `<cmd> <subcmd> …` as safe iff `subcommandSafeMap[cmd]` contains
15
+ * `subcmd`. Anything else falls through to confirm.
16
+ *
17
+ * `config` is intentionally NOT here for git/npm/pnpm/yarn — `git config
18
+ * --global ...` and `npm config set ...` mutate user-level state and
19
+ * should always confirm. Read-only forms (`git config --get foo`,
20
+ * `npm config get registry`) are caught by `mutatingArgPatterns` below
21
+ * so they can still auto-execute when the args are clearly read-only.
22
+ */
23
+ export declare const subcommandSafeMap: Record<string, Set<string>>;
24
+ /**
25
+ * Patterns that move an otherwise-safe-looking command into the
26
+ * confirm bucket because their arguments mutate state, exfiltrate
27
+ * data, or escape into another shell:
28
+ * - `sed -i …` in-place file rewrite
29
+ * - `awk … system(...)` shell-out via awk's system()
30
+ * - `awk … |getline …` arbitrary command via getline
31
+ * - `find … -exec …` run arbitrary commands
32
+ * - `find … -delete` delete matched files
33
+ * - `git config --global` / `git config --system` write user/system git config
34
+ * - `npm config set …` persist npm/yarn/pnpm config
35
+ * - `<cmd> --output-document=…` / `-o …` for fetchers (curl/wget) when
36
+ * not GET — handled separately, but pattern caught here for safety
37
+ */
38
+ export declare const mutatingArgPatterns: RegExp[];
39
+ export declare function commandHasMutatingArg(command: string): boolean;
40
+ /**
41
+ * Paths that should never be read by an agent without explicit confirmation.
42
+ * Matched against the resolved (tilde-expanded) absolute path of any tool
43
+ * that takes a `path` argument, AND against shell command strings for
44
+ * `cat`/`less`/`more`/`head`/`tail`/`view`/`bat`.
45
+ */
46
+ export declare const secretPathPatterns: RegExp[];
47
+ /**
48
+ * Returns true if `path` (already resolved to an absolute path) points at
49
+ * a known secret location.
50
+ */
51
+ export declare function isSecretPath(path: string): boolean;
52
+ export declare function containsShellMetacharacter(command: string): boolean;