Skip to content

Commit 0569950

Browse files
beastoinclaude
andcommitted
test(desktop): pi-mono denylist regression cases for review round 3 (#6594)
Pins the round-3 reviewer's quoted-target probe into regression suites. Five new suites cover: - rm with double-quoted dangerous targets (/etc/hosts, /, \$HOME, ...) - rm with single-quoted dangerous targets - chmod/chown with quoted dangerous targets - > / >> redirect into quoted system paths - positive control: quoted /tmp, ./build, /usr/local/etc still allowed Test count: 43 -> 48 (+5 suites). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3a4babb commit 0569950

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

desktop/pi-mono-extension/index.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,97 @@ test("classifyBash: allows redirect into unrelated dotfiles", () => {
289289
assert.equal(classifyBash("echo foo > ~/.config/app.conf"), null);
290290
});
291291

292+
// ---------------------------------------------------------------------------
293+
// Review round-3 bypass regressions — quoted dangerous targets
294+
//
295+
// Round-2 classifier rewrite used a shared `DANGEROUS_TARGET` matcher that
296+
// required an UNquoted path right after the rm/chmod/chown command token.
297+
// Reviewer probe caught six real shell spellings that bypassed the rule by
298+
// wrapping the target in "..." or '...'. Round-3 adds `['"]?` before the
299+
// target in rm / chmod+chown / redirect-to-system-path rules. These tests
300+
// nail down the exact bypass strings so the gap never reopens.
301+
// ---------------------------------------------------------------------------
302+
303+
test("classifyBash: blocks rm with double-quoted dangerous target", () => {
304+
const cases = [
305+
`rm "/etc/hosts"`,
306+
`rm "/etc/passwd"`,
307+
`rm --recursive --force "/"`,
308+
`rm -rf "/System/Library"`,
309+
`rm -rf "/usr/local/bin/foo"`,
310+
`rm -rf "$HOME"`,
311+
`rm "$HOME/"`,
312+
];
313+
for (const cmd of cases) {
314+
const d = classifyBash(cmd);
315+
assert.ok(d, `expected deny: ${cmd}`);
316+
assert.match(d!.reason, /root or system path/);
317+
}
318+
});
319+
320+
test("classifyBash: blocks rm with single-quoted dangerous target", () => {
321+
const cases = [
322+
`rm '/etc/hosts'`,
323+
`rm -rf '/System/Library'`,
324+
`rm --force --recursive '/usr'`,
325+
];
326+
for (const cmd of cases) {
327+
const d = classifyBash(cmd);
328+
assert.ok(d, `expected deny: ${cmd}`);
329+
assert.match(d!.reason, /root or system path/);
330+
}
331+
});
332+
333+
test("classifyBash: blocks chmod/chown with quoted dangerous target", () => {
334+
const cases = [
335+
`chmod 000 "/"`,
336+
`chmod 000 '/'`,
337+
`chmod -R 000 "$HOME"`,
338+
`chmod -R 000 "/etc"`,
339+
`chown root:wheel "/usr"`,
340+
`chown -R root:wheel '/System/Library'`,
341+
];
342+
for (const cmd of cases) {
343+
const d = classifyBash(cmd);
344+
assert.ok(d, `expected deny: ${cmd}`);
345+
assert.match(d!.reason, /permissions or ownership of a root or system/);
346+
}
347+
});
348+
349+
test("classifyBash: blocks redirect into quoted system paths", () => {
350+
const cases = [
351+
`echo bad > "/etc/hosts"`,
352+
`echo bad > '/etc/hosts'`,
353+
`cat bad.txt >> "/etc/passwd"`,
354+
`echo x > "/System/thing"`,
355+
`echo x > "/usr/bin/foo"`,
356+
`echo x > "/dev/disk2"`,
357+
];
358+
for (const cmd of cases) {
359+
const d = classifyBash(cmd);
360+
assert.ok(d, `expected deny: ${cmd}`);
361+
assert.match(d!.reason, /system path/);
362+
}
363+
});
364+
365+
test("classifyBash: still allows quoted non-system targets", () => {
366+
// Regression: the round-3 quoted-leading-char expansion must not bleed into
367+
// scratch paths. `rm "/tmp/scratch"` is a normal dev operation.
368+
const allowed = [
369+
`rm "/tmp/scratch"`,
370+
`rm -rf "/tmp/scratch"`,
371+
`rm -rf "./build"`,
372+
`rm -rf "node_modules"`,
373+
`chmod 644 "./src/index.ts"`,
374+
`chown staff "/tmp/mine"`,
375+
`echo hi > "/usr/local/etc/foo.conf"`,
376+
`echo hi > "/Library/Caches/com.omi.tmp"`,
377+
];
378+
for (const cmd of allowed) {
379+
assert.equal(classifyBash(cmd), null, `expected allow: ${cmd}`);
380+
}
381+
});
382+
292383
// ---------------------------------------------------------------------------
293384
// classifyFileWrite
294385
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)