@lannguyensi/harness 0.53.0 → 0.54.0
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.
- package/CHANGELOG.md +40 -1
- package/dist/cli/delegate/index.d.ts +15 -2
- package/dist/cli/delegate/index.js +128 -4
- package/dist/cli/delegate/index.js.map +1 -1
- package/dist/cli/doctor/bypass-without-auto-approve.js +10 -4
- package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -1
- package/dist/cli/dry-run.js +32 -1
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/index.js +13 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/profiles.js +89 -8
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.js +163 -3
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +200 -95
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/record/index.d.ts +8 -0
- package/dist/cli/record/index.js +55 -7
- package/dist/cli/record/index.js.map +1 -1
- package/dist/cli/validate/checks.js +61 -9
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/extract.d.ts +29 -0
- package/dist/io/extract.js +36 -0
- package/dist/io/extract.js.map +1 -1
- package/dist/policies/index.d.ts +1 -1
- package/dist/policies/index.js +1 -1
- package/dist/policies/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +37 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +41 -1
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +9 -3
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -1
- package/dist/runtime/destructive-shell-floor.d.ts +19 -0
- package/dist/runtime/destructive-shell-floor.js +597 -0
- package/dist/runtime/destructive-shell-floor.js.map +1 -0
- package/dist/runtime/intercept.d.ts +10 -7
- package/dist/runtime/intercept.js +108 -9
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/read-only-bash.d.ts +21 -0
- package/dist/runtime/read-only-bash.js +484 -0
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/runtime/risk-classifier.js +68 -13
- package/dist/runtime/risk-classifier.js.map +1 -1
- package/dist/runtime/when-eval.d.ts +18 -5
- package/dist/runtime/when-eval.js +64 -11
- package/dist/runtime/when-eval.js.map +1 -1
- package/dist/runtime/workflow-policies.d.ts +43 -1
- package/dist/runtime/workflow-policies.js +158 -1
- package/dist/runtime/workflow-policies.js.map +1 -1
- package/dist/schema/extract.d.ts +4 -0
- package/dist/schema/extract.js +64 -1
- package/dist/schema/extract.js.map +1 -1
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/policies.d.ts +19 -0
- package/dist/schema/policies.js +17 -1
- package/dist/schema/policies.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
// Built-in destructive-shell floor for the Risk Classifier (task 2929c5b7).
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS IS CODE AND NOT ONLY A MANIFEST PATTERN. `when-eval.ts` no
|
|
4
|
+
// longer lets an UNCLASSIFIED action satisfy `risk.severity_at_least:
|
|
5
|
+
// critical` on its own. That change ships in the binary and takes effect
|
|
6
|
+
// for every install on upgrade. Its compensating half, explicit
|
|
7
|
+
// classification for the mutating heads that used to ride the blanket
|
|
8
|
+
// fail-close (`dd`, `truncate`, `shred`, `mkfs`, `find -delete`, ...),
|
|
9
|
+
// would take effect only for installs that ADOPT the new `dangerous-shell`
|
|
10
|
+
// template patterns, because `harness init` writes the manifest once and
|
|
11
|
+
// never rewrites it. An existing manifest carrying the original four
|
|
12
|
+
// patterns would have kept the loosened fallback and lost the hard block
|
|
13
|
+
// in the same upgrade. So the recognition ships HERE, composed into
|
|
14
|
+
// `classifyRisk` like the existing built-in floors, and the template
|
|
15
|
+
// patterns are its operator-visible, operator-editable mirror.
|
|
16
|
+
//
|
|
17
|
+
// FLOOR SEMANTICS, same as every other built-in floor: it composes under
|
|
18
|
+
// highest-severity-wins. It can only RAISE an action's severity, never
|
|
19
|
+
// lower one an operator classifier already assigned, and an operator
|
|
20
|
+
// pattern can always raise above it.
|
|
21
|
+
//
|
|
22
|
+
// SCOPE, deliberately narrow. This is not a general "is this command
|
|
23
|
+
// dangerous" oracle and must not grow into one: it recognises a closed set
|
|
24
|
+
// of heads whose DESTRUCTIVE INVOCATION SHAPE is decidable from argv
|
|
25
|
+
// tokens alone. Anything else stays unclassified and rides the "high" rung
|
|
26
|
+
// fallback (approval-gated), which is the honest answer. A truncating
|
|
27
|
+
// redirection (`cmd > file`) is NOT recognised here for the same reason it
|
|
28
|
+
// is not a template pattern: this module has no redirection model, and a
|
|
29
|
+
// text heuristic for a bare `>` over-matches `2>&1`, heredocs, and
|
|
30
|
+
// comparison operators. See docs/risk-gate.md.
|
|
31
|
+
//
|
|
32
|
+
// TWO KNOWN OVER-BLOCKS, both in the raise-severity direction and both
|
|
33
|
+
// accepted. (1) The boundary split is not quote-aware, so a floored head
|
|
34
|
+
// name inside a torn-open quoted string is examined as if it were a real
|
|
35
|
+
// invocation. (2) `shred` and `mkfs` are matched on their HEAD TOKEN
|
|
36
|
+
// ALONE, with no argument shape to check, so any such fragment starting
|
|
37
|
+
// with one of them classifies `critical`. The single exception carved out
|
|
38
|
+
// of (2) is an invocation whose only argument is a help/version flag
|
|
39
|
+
// (`isLoneVersionOrHelp`), which cannot destroy anything.
|
|
40
|
+
//
|
|
41
|
+
// HEAD-RESOLUTION SPELLINGS THIS FLOOR DOES NOT REACH, by design and
|
|
42
|
+
// measured: a brace group (`{ dd of=y; }`), a backtick substitution
|
|
43
|
+
// (`` `dd of=y` ``), `eval "dd of=y"`, an `-exec sh -c '<cmd>'` payload
|
|
44
|
+
// (only a direct `-exec rm` is recognised), and `git -c k=v push --force`
|
|
45
|
+
// (`-c` is deliberately absent from the git global-flag skip list because
|
|
46
|
+
// config injection can execute code, so the subcommand walk stops there).
|
|
47
|
+
// Each stays UNCLASSIFIED, which is approval-gated rather than silently
|
|
48
|
+
// allowed, and each is pinned as such in
|
|
49
|
+
// tests/runtime/destructive-shell-floor.test.ts so a future change to the
|
|
50
|
+
// split or the wrapper peel cannot flip one without a test noticing.
|
|
51
|
+
// Closing them would mean a real shell parser; see docs/risk-gate.md's
|
|
52
|
+
// "Not recognized, by design".
|
|
53
|
+
import { GIT_GLOBAL_NO_VALUE_FLAGS, GIT_GLOBAL_VALUE_TAKING_FLAGS, GIT_TOKEN_RE, peelWrapperPrefixes, } from "./command-normalize.js";
|
|
54
|
+
import { decodeShellWord } from "./shell-word.js";
|
|
55
|
+
// Shell boundary characters. The command text is split on these before any
|
|
56
|
+
// head resolution, so a destructive invocation after `;`, `&&`, `||`, `|`,
|
|
57
|
+
// `&`, a subshell paren, or a newline is still examined. Splitting is
|
|
58
|
+
// deliberately NOT quote-aware: over-splitting inside a quoted string only
|
|
59
|
+
// produces MORE candidate heads to examine, which is the safe direction
|
|
60
|
+
// for a floor that raises severity. Under-splitting is what would hide a
|
|
61
|
+
// destructive tail, and that cannot happen here.
|
|
62
|
+
const BOUNDARY_SPLIT_RE = /[;&|()\n]+/;
|
|
63
|
+
/** Multi-call binaries: `busybox dd if=... of=...` really runs `dd`. */
|
|
64
|
+
const MULTICALL_HEADS = new Set(["busybox", "toybox"]);
|
|
65
|
+
/** Shells whose `-c` argument is a nested command string. */
|
|
66
|
+
const SHELL_HEADS = new Set([
|
|
67
|
+
"sh", "bash", "zsh", "dash", "ksh", "ash",
|
|
68
|
+
]);
|
|
69
|
+
/** Depth bound for `sh -c "sh -c ..."` nesting. */
|
|
70
|
+
const MAX_NESTING_DEPTH = 4;
|
|
71
|
+
/**
|
|
72
|
+
* Recognise every built-in destructive invocation in `command`.
|
|
73
|
+
*
|
|
74
|
+
* Pure and linear in the length of the command: whitespace tokenisation,
|
|
75
|
+
* set lookups, and a bounded wrapper peel. No regex with a backtracking
|
|
76
|
+
* risk runs over the command text (`BOUNDARY_SPLIT_RE` is a character
|
|
77
|
+
* class), so this may be called on the UNCAPPED command string, which it
|
|
78
|
+
* must be, so a destructive tail past the classifier's 16 KiB ReDoS cap
|
|
79
|
+
* cannot be laundered by a long benign head.
|
|
80
|
+
*/
|
|
81
|
+
export function classifyDestructiveShellFloor(command) {
|
|
82
|
+
const hits = [];
|
|
83
|
+
scanCommand(command, 0, hits);
|
|
84
|
+
return dedupe(hits);
|
|
85
|
+
}
|
|
86
|
+
function dedupe(hits) {
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const hit of hits) {
|
|
90
|
+
if (seen.has(hit.reason))
|
|
91
|
+
continue;
|
|
92
|
+
seen.add(hit.reason);
|
|
93
|
+
out.push(hit);
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
function scanCommand(command, depth, hits) {
|
|
98
|
+
if (depth > MAX_NESTING_DEPTH)
|
|
99
|
+
return;
|
|
100
|
+
// WHOLE-STRING pass first, and only for the `<shell> -c "<command>"`
|
|
101
|
+
// shape. The boundary split below is not quote-aware, so it would tear
|
|
102
|
+
// `sh -c "dd if=x of=y; rm -rf /"` apart mid-quote and leave the nested
|
|
103
|
+
// `dd` unrecognisable. Resolving the shell head before splitting keeps
|
|
104
|
+
// the nested command intact for the recursive scan.
|
|
105
|
+
const wholeTokens = tokenize(command);
|
|
106
|
+
const wholeHead = resolveHead(wholeTokens);
|
|
107
|
+
if (wholeHead !== null && SHELL_HEADS.has(wholeHead.name)) {
|
|
108
|
+
scanShellDashC(wholeTokens.slice(wholeHead.idx + 1), depth, hits);
|
|
109
|
+
}
|
|
110
|
+
for (const piece of command.split(BOUNDARY_SPLIT_RE)) {
|
|
111
|
+
const tokens = tokenize(piece);
|
|
112
|
+
if (tokens.length === 0)
|
|
113
|
+
continue;
|
|
114
|
+
scanInvocation(tokens, depth, hits);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function tokenize(s) {
|
|
118
|
+
return s.trim().split(/\s+/).filter((t) => t !== "");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Resolve the real head of one invocation: peel `VAR=x` assignments and
|
|
122
|
+
* the `sudo`/`env`/`command`/`timeout`/... wrapper chain, then any
|
|
123
|
+
* `busybox`/`toybox` multi-call prefix. Returns the head's literal name
|
|
124
|
+
* (decoded, path prefix stripped) and its token index, or `null` when no
|
|
125
|
+
* head token survives.
|
|
126
|
+
*/
|
|
127
|
+
function resolveHead(rawTokens) {
|
|
128
|
+
// Basename + decode every token that is not a flag and not a `NAME=value`
|
|
129
|
+
// assignment, so `/bin/dd`, `/usr/bin/env`, and `"sudo"` all resolve like
|
|
130
|
+
// their bare spellings. Assignments and `of=`-style operands keep their
|
|
131
|
+
// text (their `=` is load-bearing), and flags keep theirs (a flag has no
|
|
132
|
+
// path prefix to strip).
|
|
133
|
+
const peelTokens = rawTokens.map((t) => ({ text: headNameOf(t) }));
|
|
134
|
+
let idx = 0;
|
|
135
|
+
for (let guard = 0; guard <= MAX_NESTING_DEPTH; guard += 1) {
|
|
136
|
+
const before = idx;
|
|
137
|
+
// Shared wrapper vocabulary (`peelWrapperPrefixes`, command-normalize.ts)
|
|
138
|
+
// rather than a second hand-rolled copy of it: a wrapper added there
|
|
139
|
+
// benefits this floor automatically.
|
|
140
|
+
idx = peelWrapperPrefixes(peelTokens, idx).idx;
|
|
141
|
+
const peeled = peelTokens[idx]?.text;
|
|
142
|
+
if (peeled !== undefined && MULTICALL_HEADS.has(peeled)) {
|
|
143
|
+
idx += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (idx === before)
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
const name = peelTokens[idx]?.text;
|
|
150
|
+
return name === undefined ? null : { name, idx };
|
|
151
|
+
}
|
|
152
|
+
/** Resolve one boundary-delimited invocation's head, then dispatch on it. */
|
|
153
|
+
function scanInvocation(rawTokens, depth, hits) {
|
|
154
|
+
const resolved = resolveHead(rawTokens);
|
|
155
|
+
if (resolved === null)
|
|
156
|
+
return;
|
|
157
|
+
const head = resolved.name;
|
|
158
|
+
const args = rawTokens.slice(resolved.idx + 1);
|
|
159
|
+
if (SHELL_HEADS.has(head)) {
|
|
160
|
+
scanShellDashC(args, depth, hits);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (head === "dd")
|
|
164
|
+
return scanDd(args, hits);
|
|
165
|
+
if (head === "truncate")
|
|
166
|
+
return scanTruncate(args, hits);
|
|
167
|
+
if (head === "shred") {
|
|
168
|
+
if (isLoneVersionOrHelp(args))
|
|
169
|
+
return;
|
|
170
|
+
hits.push({
|
|
171
|
+
severity: "critical",
|
|
172
|
+
categories: ["destructive", "data_loss", "irreversible_action"],
|
|
173
|
+
reason: "built-in destructive floor: shred overwrites a file in place",
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (head === "mkfs" || head.startsWith("mkfs.")) {
|
|
178
|
+
if (isLoneVersionOrHelp(args))
|
|
179
|
+
return;
|
|
180
|
+
hits.push({
|
|
181
|
+
severity: "critical",
|
|
182
|
+
categories: ["destructive", "data_loss", "infrastructure_change"],
|
|
183
|
+
reason: "built-in destructive floor: mkfs formats a filesystem",
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (head === "find")
|
|
188
|
+
return scanFind(args, hits);
|
|
189
|
+
if (GIT_TOKEN_RE.test(head))
|
|
190
|
+
return scanGit(args, hits);
|
|
191
|
+
if (head === "chmod" || head === "chown")
|
|
192
|
+
return scanChmodChown(head, args, hits);
|
|
193
|
+
if (head === "sed")
|
|
194
|
+
return scanSed(args, hits);
|
|
195
|
+
if (head === "curl")
|
|
196
|
+
return scanCurl(args, hits);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* `sh -c "<command>"` / `bash -lc '<command>'`: the nested command string
|
|
200
|
+
* is re-scanned from the top. The `-c` value is reconstructed by rejoining
|
|
201
|
+
* the tokens after the flag run (the whitespace tokenizer split the quoted
|
|
202
|
+
* string apart) and stripping one layer of surrounding quotes.
|
|
203
|
+
*/
|
|
204
|
+
function scanShellDashC(args, depth, hits) {
|
|
205
|
+
let i = 0;
|
|
206
|
+
let sawDashC = false;
|
|
207
|
+
for (; i < args.length; i += 1) {
|
|
208
|
+
const t = decodeShellWord(args[i]);
|
|
209
|
+
if (!t.startsWith("-"))
|
|
210
|
+
break;
|
|
211
|
+
if (t === "--") {
|
|
212
|
+
i += 1;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
if (!t.startsWith("--") && t.includes("c")) {
|
|
216
|
+
sawDashC = true;
|
|
217
|
+
i += 1;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
if (t === "--command") {
|
|
221
|
+
sawDashC = true;
|
|
222
|
+
i += 1;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!sawDashC)
|
|
227
|
+
return;
|
|
228
|
+
const nested = stripOuterQuotes(args.slice(i).join(" "));
|
|
229
|
+
if (nested === "")
|
|
230
|
+
return;
|
|
231
|
+
scanCommand(nested, depth + 1, hits);
|
|
232
|
+
}
|
|
233
|
+
function stripOuterQuotes(s) {
|
|
234
|
+
const t = s.trim();
|
|
235
|
+
if (t.length >= 2) {
|
|
236
|
+
const first = t[0];
|
|
237
|
+
const last = t[t.length - 1];
|
|
238
|
+
if ((first === "'" || first === '"') && first === last)
|
|
239
|
+
return t.slice(1, -1);
|
|
240
|
+
}
|
|
241
|
+
return t;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The literal name a head token invokes: decoded, with any path prefix
|
|
245
|
+
* stripped. `NAME=value` assignment tokens and flags are returned
|
|
246
|
+
* unchanged (stripping a `/` out of an assignment's VALUE would corrupt
|
|
247
|
+
* the token `peelWrapperPrefixes` needs to recognise, and a flag has no
|
|
248
|
+
* path prefix).
|
|
249
|
+
*/
|
|
250
|
+
function headNameOf(token) {
|
|
251
|
+
const decoded = decodeShellWord(token);
|
|
252
|
+
if (decoded.startsWith("-"))
|
|
253
|
+
return decoded;
|
|
254
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(decoded))
|
|
255
|
+
return decoded;
|
|
256
|
+
const slash = decoded.lastIndexOf("/");
|
|
257
|
+
return slash === -1 ? decoded : decoded.slice(slash + 1);
|
|
258
|
+
}
|
|
259
|
+
/** `true` when `arg`, raw or decoded, satisfies `pred`. */
|
|
260
|
+
function eitherForm(arg, pred) {
|
|
261
|
+
return pred(arg) || pred(decodeShellWord(arg));
|
|
262
|
+
}
|
|
263
|
+
/** `true` when `t` is a single-dash short-flag cluster containing `ch`. */
|
|
264
|
+
function shortClusterHas(t, ch) {
|
|
265
|
+
return t.startsWith("-") && !t.startsWith("--") && t.slice(1).includes(ch);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Flags that mean "print and exit", never "act". Same set and same
|
|
269
|
+
* precedent as `VERSION_OR_HELP_FLAGS` in `read-only-bash.ts` (kept as a
|
|
270
|
+
* local copy rather than an import so this module stays free of that
|
|
271
|
+
* 2000-line predicate; the two sets are five literals and are pinned
|
|
272
|
+
* against each other in tests/runtime/destructive-shell-floor.test.ts).
|
|
273
|
+
*/
|
|
274
|
+
const VERSION_OR_HELP_FLAGS = new Set([
|
|
275
|
+
"--version", "-V", "-v", "--help", "-h",
|
|
276
|
+
]);
|
|
277
|
+
/**
|
|
278
|
+
* `true` for an invocation whose ONLY argument is a help/version flag.
|
|
279
|
+
*
|
|
280
|
+
* The argument-free heads below (`shred`, `mkfs`) are matched on the head
|
|
281
|
+
* token alone, because every real invocation of them destroys something.
|
|
282
|
+
* That over-blocks the one shape that destroys nothing: `shred --help`.
|
|
283
|
+
* The LONE requirement is what keeps this safe: `shred -v secret.txt` is
|
|
284
|
+
* a real shred with a verbose flag and keeps its `critical`, and a bare
|
|
285
|
+
* `shred -v` has no operand to destroy.
|
|
286
|
+
*
|
|
287
|
+
* NOTE, the same over-block in general: an argument-free head match fires
|
|
288
|
+
* on ANY boundary-split fragment whose first token resolves to that head,
|
|
289
|
+
* so a fragment like `echo mkfs` never reaches here (its head is `echo`)
|
|
290
|
+
* but `mkfs` inside a quoted string the non-quote-aware split tore open
|
|
291
|
+
* does. That is the deliberate direction for a floor that only RAISES
|
|
292
|
+
* severity, and is the same trade the split itself documents.
|
|
293
|
+
*/
|
|
294
|
+
function isLoneVersionOrHelp(args) {
|
|
295
|
+
return args.length === 1 && VERSION_OR_HELP_FLAGS.has(decodeShellWord(args[0]));
|
|
296
|
+
}
|
|
297
|
+
// `dd` writes only when it is given an output file. `dd if=/dev/sda | gzip`
|
|
298
|
+
// (a read into a pipe) is deliberately NOT floored: it mutates nothing, and
|
|
299
|
+
// flooring it would hard-block a backup on a production-resolved session,
|
|
300
|
+
// the exact false positive this task exists to remove.
|
|
301
|
+
function scanDd(args, hits) {
|
|
302
|
+
if (!args.some((a) => eitherForm(a, (t) => t.startsWith("of="))))
|
|
303
|
+
return;
|
|
304
|
+
hits.push({
|
|
305
|
+
severity: "critical",
|
|
306
|
+
categories: ["destructive", "data_loss"],
|
|
307
|
+
reason: "built-in destructive floor: dd with an of= write target",
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
// `truncate -s <size>` / `--size=<size>`, including the glued `-s0` form
|
|
311
|
+
// and any cluster carrying `s` (`-cs 0`). Without a size there is nothing
|
|
312
|
+
// to truncate to.
|
|
313
|
+
function scanTruncate(args, hits) {
|
|
314
|
+
const sized = args.some((a) => eitherForm(a, (t) => t === "--size" || t.startsWith("--size=") || shortClusterHas(t, "s")));
|
|
315
|
+
if (!sized)
|
|
316
|
+
return;
|
|
317
|
+
hits.push({
|
|
318
|
+
severity: "critical",
|
|
319
|
+
categories: ["destructive", "data_loss"],
|
|
320
|
+
reason: "built-in destructive floor: truncate with a size argument",
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
// `find ... -delete` and `find ... -exec/-execdir rm`. Both spellings are
|
|
324
|
+
// compared raw OR decoded, the measured bypass class from task fdee7d0f
|
|
325
|
+
// (`-"delete"`, `-'delete'`, `-\delete` all reach find as `-delete`).
|
|
326
|
+
function scanFind(args, hits) {
|
|
327
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
328
|
+
const a = args[i];
|
|
329
|
+
if (eitherForm(a, (t) => t === "-delete")) {
|
|
330
|
+
hits.push({
|
|
331
|
+
severity: "critical",
|
|
332
|
+
categories: ["destructive", "data_loss"],
|
|
333
|
+
reason: "built-in destructive floor: find -delete",
|
|
334
|
+
});
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (eitherForm(a, (t) => t === "-exec" || t === "-execdir")) {
|
|
338
|
+
const payload = args[i + 1];
|
|
339
|
+
if (payload !== undefined && headNameOf(payload) === "rm") {
|
|
340
|
+
hits.push({
|
|
341
|
+
severity: "critical",
|
|
342
|
+
categories: ["destructive", "data_loss"],
|
|
343
|
+
reason: "built-in destructive floor: find -exec rm",
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* git's own global options sit BETWEEN the binary and the subcommand
|
|
351
|
+
* (`git -C /repo push -f`), so they are skipped before the subcommand is
|
|
352
|
+
* read, reusing command-normalize.ts's exported flag sets. `-c` is
|
|
353
|
+
* deliberately absent from `GIT_GLOBAL_VALUE_TAKING_FLAGS` (config
|
|
354
|
+
* injection can execute code); a command carrying it stops the walk and
|
|
355
|
+
* simply yields no subcommand here.
|
|
356
|
+
*/
|
|
357
|
+
function scanGit(args, hits) {
|
|
358
|
+
let i = 0;
|
|
359
|
+
while (i < args.length) {
|
|
360
|
+
const t = decodeShellWord(args[i]);
|
|
361
|
+
if (!t.startsWith("-"))
|
|
362
|
+
break;
|
|
363
|
+
if (GIT_GLOBAL_NO_VALUE_FLAGS.has(t)) {
|
|
364
|
+
i += 1;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const eq = t.indexOf("=");
|
|
368
|
+
const name = eq === -1 ? t : t.slice(0, eq);
|
|
369
|
+
if (GIT_GLOBAL_VALUE_TAKING_FLAGS.has(name)) {
|
|
370
|
+
i += eq === -1 ? 2 : 1;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
const sub = args[i] === undefined ? "" : decodeShellWord(args[i]);
|
|
376
|
+
const subArgs = args.slice(i + 1);
|
|
377
|
+
const push = (categories, reason) => {
|
|
378
|
+
hits.push({ severity: "high", categories, reason: `built-in destructive floor: ${reason}` });
|
|
379
|
+
};
|
|
380
|
+
if (sub === "reset" && subArgs.some((a) => eitherForm(a, (t) => t === "--hard"))) {
|
|
381
|
+
push(["destructive", "data_loss"], "git reset --hard");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (sub === "push") {
|
|
385
|
+
const forced = subArgs.some((a) => eitherForm(a, (t) => t === "--force" ||
|
|
386
|
+
t === "--force-with-lease" ||
|
|
387
|
+
t.startsWith("--force-with-lease=") ||
|
|
388
|
+
t.startsWith("+") ||
|
|
389
|
+
shortClusterHas(t, "f")));
|
|
390
|
+
if (forced)
|
|
391
|
+
push(["destructive", "production_mutation", "deployment_change"], "git push --force");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (sub === "clean") {
|
|
395
|
+
const forced = subArgs.some((a) => eitherForm(a, (t) => t === "--force" || shortClusterHas(t, "f")));
|
|
396
|
+
if (forced)
|
|
397
|
+
push(["destructive", "data_loss"], "git clean --force");
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (sub === "checkout" || sub === "restore") {
|
|
401
|
+
const operands = subArgs.map((a) => decodeShellWord(a)).filter((t) => t !== "--");
|
|
402
|
+
if (operands.length === 1 && operands[0] === ".") {
|
|
403
|
+
push(["destructive", "data_loss"], `git ${sub} . discards every uncommitted change`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// `chmod -R` / `chown -R` (and `--recursive`), cluster-aware so `-Rf` and
|
|
408
|
+
// `-fR` are both caught. Categories: `mass_update` only, deliberately NOT
|
|
409
|
+
// `destructive`/`data_loss`: a recursive mode or owner change rewrites
|
|
410
|
+
// metadata across a tree (worth a `high` gate) but is reversible by the
|
|
411
|
+
// inverse command, so the profile stays `reversible: true`.
|
|
412
|
+
function scanChmodChown(head, args, hits) {
|
|
413
|
+
const recursive = args.some((a) => eitherForm(a, (t) => t === "--recursive" || shortClusterHas(t, "R")));
|
|
414
|
+
if (!recursive)
|
|
415
|
+
return;
|
|
416
|
+
hits.push({
|
|
417
|
+
severity: "high",
|
|
418
|
+
categories: ["mass_update"],
|
|
419
|
+
reason: `built-in destructive floor: ${head} -R rewrites a whole tree`,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
// `sed -i` in every spelling: `-i`, `-i.bak`, the cluster `-ni`,
|
|
423
|
+
// `--in-place`, `--in-place=SUFFIX`.
|
|
424
|
+
function scanSed(args, hits) {
|
|
425
|
+
const inPlace = args.some((a) => eitherForm(a, (t) => t === "--in-place" || t.startsWith("--in-place=") || shortClusterHas(t, "i")));
|
|
426
|
+
if (!inPlace)
|
|
427
|
+
return;
|
|
428
|
+
hits.push({
|
|
429
|
+
severity: "high",
|
|
430
|
+
categories: ["destructive", "data_loss"],
|
|
431
|
+
reason: "built-in destructive floor: sed in-place edit",
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/** curl flags that write a LOCAL file. */
|
|
435
|
+
const CURL_LOCAL_WRITE_LONG_FLAGS = new Set([
|
|
436
|
+
"--output", "--remote-name", "--dump-header", "--cookie-jar", "--config",
|
|
437
|
+
"--create-dirs", "--output-dir", "--etag-save", "--trace", "--trace-ascii",
|
|
438
|
+
"--stderr", "--remote-header-name",
|
|
439
|
+
]);
|
|
440
|
+
const CURL_LOCAL_WRITE_SHORT_CHARS = ["o", "O", "D", "c", "K"];
|
|
441
|
+
/** curl flags that send a REQUEST BODY (making the request a write). */
|
|
442
|
+
const CURL_BODY_LONG_FLAGS = new Set([
|
|
443
|
+
"--data", "--data-raw", "--data-binary", "--data-urlencode", "--data-ascii",
|
|
444
|
+
"--json", "--form", "--form-string", "--form-escape", "--upload-file",
|
|
445
|
+
]);
|
|
446
|
+
const CURL_BODY_SHORT_CHARS = ["d", "F", "T"];
|
|
447
|
+
/**
|
|
448
|
+
* `-H`/`--header`: curl reads the VALUE from a local file when it starts
|
|
449
|
+
* with `@` (`-H @/etc/passwd` sends that file's lines as request
|
|
450
|
+
* headers).
|
|
451
|
+
*/
|
|
452
|
+
const CURL_HEADER_FLAG = { long: "--header", short: "H" };
|
|
453
|
+
/**
|
|
454
|
+
* `-b`/`--cookie` uses a DIFFERENT rule than `-H`, measured on curl 8.7.1:
|
|
455
|
+
* a value containing no `=` is read as a cookie-jar FILENAME, `@`-prefixed
|
|
456
|
+
* or not (`curl -b jar.txt` reads the jar; `curl -b @jar.txt` opens a file
|
|
457
|
+
* literally named `@jar.txt`, the `@` is not a file marker for this
|
|
458
|
+
* flag). A value containing `=` is sent as inline cookie data
|
|
459
|
+
* (`-b 'name=value'`) and stays inert.
|
|
460
|
+
*/
|
|
461
|
+
const CURL_COOKIE_FLAG = { long: "--cookie", short: "b" };
|
|
462
|
+
/**
|
|
463
|
+
* The value curl reads for one flag, whichever of the three ways it is
|
|
464
|
+
* spelled: `=`-joined (`--header=@f`), glued into a short-flag cluster
|
|
465
|
+
* (`-H@f`, `-sH@f`), or the next argv token (`-H @f`, `--header @f`).
|
|
466
|
+
* `undefined` when this token does not carry the flag at all.
|
|
467
|
+
*
|
|
468
|
+
* Short-flag lookup is a plain cluster search, so a value character that
|
|
469
|
+
* happens to sit inside ANOTHER flag's glued value can be misread as this
|
|
470
|
+
* flag. That direction only ever adds a candidate value to inspect, which
|
|
471
|
+
* for a floor that exclusively RAISES severity is the safe one.
|
|
472
|
+
*/
|
|
473
|
+
function curlFlagValue(token, next, longName, shortChar) {
|
|
474
|
+
const nextValue = () => next === undefined ? undefined : decodeShellWord(next);
|
|
475
|
+
if (token.startsWith("--")) {
|
|
476
|
+
const eq = token.indexOf("=");
|
|
477
|
+
if ((eq === -1 ? token : token.slice(0, eq)) !== longName)
|
|
478
|
+
return undefined;
|
|
479
|
+
return eq === -1 ? nextValue() : token.slice(eq + 1);
|
|
480
|
+
}
|
|
481
|
+
if (!token.startsWith("-"))
|
|
482
|
+
return undefined;
|
|
483
|
+
const at = token.indexOf(shortChar, 1);
|
|
484
|
+
if (at === -1)
|
|
485
|
+
return undefined;
|
|
486
|
+
const glued = token.slice(at + 1);
|
|
487
|
+
return glued.length > 0 ? glued : nextValue();
|
|
488
|
+
}
|
|
489
|
+
/** `true` when `t` is `-w`/`--write-out` in any spelling. */
|
|
490
|
+
function isCurlWriteOutFlag(t) {
|
|
491
|
+
return t === "--write-out" || t.startsWith("--write-out=") || shortClusterHas(t, "w");
|
|
492
|
+
}
|
|
493
|
+
// What the read-only side no longer tries to decide. Decision D-013 removed
|
|
494
|
+
// the `curl` `low` floor entirely: a per-flag allowlist has to know every
|
|
495
|
+
// curl flag's write capability across curl versions, and two rounds of it
|
|
496
|
+
// leaked. This scan carries the opposite, tractable obligation: it names
|
|
497
|
+
// only capability it CAN name, and everything it does not name stays
|
|
498
|
+
// unclassified (approval-gated) rather than floored.
|
|
499
|
+
function scanCurl(args, hits) {
|
|
500
|
+
let localWrite = false;
|
|
501
|
+
let body = false;
|
|
502
|
+
let atFile = false;
|
|
503
|
+
// `-w`/`--write-out`'s format string writes a LOCAL FILE when it carries
|
|
504
|
+
// curl 8.3.0's `%output{FILE}` directive (verified on curl 8.7.1), which
|
|
505
|
+
// is the hole review round 3 found in the removed read-only floor: it
|
|
506
|
+
// allowlisted `-w` as inert. The format string is operator text that this
|
|
507
|
+
// module's whitespace tokenizer may have split across several tokens
|
|
508
|
+
// (`-w 'code=%{http_code} %output{/etc/x}'`), so `%output` is looked for
|
|
509
|
+
// in EVERY token once a write-out flag is present, not only in the one
|
|
510
|
+
// token that follows the flag. That over-matches a URL or an unrelated
|
|
511
|
+
// argument carrying the literal `%output`, which is the safe direction.
|
|
512
|
+
const decoded = args.map((a) => decodeShellWord(a));
|
|
513
|
+
if (decoded.some(isCurlWriteOutFlag) && decoded.some((t) => t.includes("%output"))) {
|
|
514
|
+
localWrite = true;
|
|
515
|
+
}
|
|
516
|
+
// `-H @FILE`: a LOCAL file read into the outgoing request. The `@` sits
|
|
517
|
+
// at the very start of the value, so the first token of the value is
|
|
518
|
+
// always the right place to look and the precise per-flag extraction is
|
|
519
|
+
// hole-free here.
|
|
520
|
+
//
|
|
521
|
+
// `-b`/`--cookie` is checked separately, by its own rule (see
|
|
522
|
+
// CURL_COOKIE_FLAG above): a value with no `=` is a filename curl reads,
|
|
523
|
+
// regardless of an `@` prefix.
|
|
524
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
525
|
+
const headerValue = curlFlagValue(decoded[i], args[i + 1], CURL_HEADER_FLAG.long, CURL_HEADER_FLAG.short);
|
|
526
|
+
if (headerValue !== undefined && headerValue.startsWith("@"))
|
|
527
|
+
atFile = true;
|
|
528
|
+
const cookieValue = curlFlagValue(decoded[i], args[i + 1], CURL_COOKIE_FLAG.long, CURL_COOKIE_FLAG.short);
|
|
529
|
+
// An empty value (-b '') only switches curl's cookie engine on without
|
|
530
|
+
// opening a file; it is raised anyway, an accepted over-block in the safe
|
|
531
|
+
// direction.
|
|
532
|
+
if (cookieValue !== undefined && !cookieValue.includes("="))
|
|
533
|
+
atFile = true;
|
|
534
|
+
}
|
|
535
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
536
|
+
const t = decodeShellWord(args[i]);
|
|
537
|
+
if (!t.startsWith("-") || t === "-" || t === "--")
|
|
538
|
+
continue;
|
|
539
|
+
const eq = t.indexOf("=");
|
|
540
|
+
const name = eq === -1 ? t : t.slice(0, eq);
|
|
541
|
+
if (name === "--request" || (t.startsWith("-X") && !t.startsWith("--"))) {
|
|
542
|
+
let value;
|
|
543
|
+
if (name === "--request") {
|
|
544
|
+
value = eq === -1 ? (args[i + 1] === undefined ? undefined : decodeShellWord(args[i + 1])) : t.slice(eq + 1);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
const glued = t.slice(2);
|
|
548
|
+
value = glued.length > 0 ? glued : args[i + 1] === undefined ? undefined : decodeShellWord(args[i + 1]);
|
|
549
|
+
}
|
|
550
|
+
const method = (value ?? "").toUpperCase();
|
|
551
|
+
if (method !== "" && method !== "GET" && method !== "HEAD")
|
|
552
|
+
body = true;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (CURL_LOCAL_WRITE_LONG_FLAGS.has(name)) {
|
|
556
|
+
localWrite = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (CURL_BODY_LONG_FLAGS.has(name)) {
|
|
560
|
+
body = true;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (t.startsWith("--"))
|
|
564
|
+
continue;
|
|
565
|
+
if (CURL_LOCAL_WRITE_SHORT_CHARS.some((c) => shortClusterHas(t, c)))
|
|
566
|
+
localWrite = true;
|
|
567
|
+
if (CURL_BODY_SHORT_CHARS.some((c) => shortClusterHas(t, c)))
|
|
568
|
+
body = true;
|
|
569
|
+
}
|
|
570
|
+
if (localWrite) {
|
|
571
|
+
hits.push({
|
|
572
|
+
severity: "high",
|
|
573
|
+
categories: ["destructive", "data_loss"],
|
|
574
|
+
reason: "built-in destructive floor: curl writes a local file",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
if (body) {
|
|
578
|
+
hits.push({
|
|
579
|
+
severity: "high",
|
|
580
|
+
categories: ["production_mutation", "network_exfiltration"],
|
|
581
|
+
reason: "built-in destructive floor: curl sends a request body or a non-GET/HEAD method",
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (atFile) {
|
|
585
|
+
// `network_exfiltration` alone, not the request-body pair: reading a
|
|
586
|
+
// local file into a header or a cookie ships data OUT, but it does not
|
|
587
|
+
// by itself mutate the remote resource, so `production_mutation` would
|
|
588
|
+
// be a claim this scan cannot make. Without an irreversible category
|
|
589
|
+
// the profile stays `reversible: true`, which is honest for a read.
|
|
590
|
+
hits.push({
|
|
591
|
+
severity: "high",
|
|
592
|
+
categories: ["network_exfiltration"],
|
|
593
|
+
reason: "built-in destructive floor: curl reads a local file into the request (@ value on -H, or a no-= value on -b)",
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
//# sourceMappingURL=destructive-shell-floor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destructive-shell-floor.js","sourceRoot":"","sources":["../../src/runtime/destructive-shell-floor.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,13 +44,16 @@ export interface PolicyDecision {
|
|
|
44
44
|
*/
|
|
45
45
|
recordHint?: string;
|
|
46
46
|
/**
|
|
47
|
-
* True when the policy's `when:` block matched
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
47
|
+
* True when the policy's `when:` block matched because the action was
|
|
48
|
+
* unclassified (the fail-close rule in `when-eval.ts`: "unknown is not
|
|
49
|
+
* safe" for `risk.category_in` / `action.reversible`, the "treated as
|
|
50
|
+
* high" rung for `risk.severity_at_least`). Absent when the policy has
|
|
51
|
+
* no `when:` block, when the match was a genuine classification hit,
|
|
52
|
+
* or when `unclassifiedFallback` was false. Present in the audit
|
|
53
|
+
* record, the non-ux block-time deny message, AND (task 2929c5b7) the
|
|
54
|
+
* ux-declared `cannot:` deny message, so an operator — or the agent
|
|
55
|
+
* reading its own deny — can distinguish a real critical-severity
|
|
56
|
+
* match from a fail-closed unclassified command at a glance.
|
|
54
57
|
*/
|
|
55
58
|
whenUnclassifiedFallback?: boolean;
|
|
56
59
|
evaluatedAt: string;
|