mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 16:08:59 +02:00
Merge origin/main (v1.60.1.0) into garrytan/gstack-fix-wave
Semantic reconciliation with the parallel time-attack wave (#2264):
- careful: adopt main's anchored full-command whitelist (stricter — also
catches comment-hiding), re-apply this wave's two hardenings on top
(capital -[rR] in the flag cluster; exclude `(` and backtick from safe
targets so $()/backtick substitution cannot ride the whitelist). Union
of both waves' test batteries passes (main's test.each incl. comment
case + this wave's substitution/capital-R/FP-pin cases).
- one-way-doors: main landed the singular noun unification (a2a447a1);
keep this wave's superset (plural s? + --summary-stdin runtime wiring).
- gbrain-local-status: union of states — main's engine-locked (#2194,
exit 124 PGLite lock) + this wave's thin-client (#2051). --is-ok keeps
main's intent (engine-locked = STOP) and this wave's (thin-client =
usable). Test harness unions both fake behaviors.
- sync-gbrain/setup-gbrain tmpls: both Step 1.5 branches kept; generated
SKILL.md resolved via bun run gen:skill-docs (never hand-edited).
- VERSION/package.json -> 1.61.0.0 per bin/gstack-next-version (main took
1.60.1.0; PR #2470 claims 1.60.2.0). CHANGELOG: wave entry renumbered
1.61.0.0 on top of main's 1.60.1.0; careful/#2024 bullets updated to
describe the delta vs current main. TODOS: union.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -97,10 +97,9 @@ describe('check-careful.sh', () => {
|
||||
expect(output.message).toContain('recursive delete');
|
||||
});
|
||||
|
||||
// Regression: the safe-exception extracts targets from only the LAST `rm` in
|
||||
// the command (greedy match), so a chain that ends in a safe target must not
|
||||
// wave through a destructive earlier rm. The shortcut only applies to a
|
||||
// single rm invocation; any shell separator falls through to the warning.
|
||||
// The safe exception matches the COMPLETE command against an anchored
|
||||
// whitelist shape — anything else (chains, comments, substitution) falls
|
||||
// through to the destructive-pattern warning.
|
||||
test('rm -rf /; rm -rf node_modules warns (semicolon chain, dangerous first)', () => {
|
||||
const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf /; rm -rf node_modules'));
|
||||
expect(exitCode).toBe(0);
|
||||
@@ -122,9 +121,9 @@ describe('check-careful.sh', () => {
|
||||
expect(output.message).toContain('recursive delete');
|
||||
});
|
||||
|
||||
// Command substitution is a chaining form: the substitution token can end in
|
||||
// a whitelisted suffix while running anything inside $(...) or backticks,
|
||||
// and the safe-exception early exit would skip ALL downstream checks.
|
||||
// Command substitution can end in a whitelisted suffix while running
|
||||
// anything inside $(...) or backticks — the whitelist's target tokens
|
||||
// exclude `(` and backtick so these cannot ride the safe exception.
|
||||
test('rm -rf $(./wipe-all)/node_modules warns (command substitution)', () => {
|
||||
const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf $(./wipe-all)/node_modules'));
|
||||
expect(exitCode).toBe(0);
|
||||
@@ -162,8 +161,8 @@ describe('check-careful.sh', () => {
|
||||
expect(output.permissionDecision).toBeUndefined();
|
||||
});
|
||||
|
||||
// The JSON-escaped-newline separator branch (literal two-char \n surviving
|
||||
// the grep extraction path) had dedicated code but no test exercising it.
|
||||
// JSON-escaped newline (literal two-char \n surviving the grep extraction
|
||||
// path) breaks the anchored whitelist shape → falls through to the warn.
|
||||
test('newline-chained rm warns (escaped-newline separator branch)', () => {
|
||||
const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput('rm -rf /etc/x\nrm -rf node_modules'));
|
||||
expect(exitCode).toBe(0);
|
||||
@@ -181,6 +180,21 @@ describe('check-careful.sh', () => {
|
||||
expect(output.permissionDecision).toBe('ask');
|
||||
expect(output.message).toContain('recursive delete');
|
||||
});
|
||||
|
||||
test.each([
|
||||
'rm -rf /; rm -rf node_modules',
|
||||
'rm -rf / && rm -rf node_modules',
|
||||
'rm -rf / # rm -rf node_modules',
|
||||
'rm -rf node_modules; rm -rf /',
|
||||
'rm -rf node_modules || rm -rf /',
|
||||
'echo ok && rm -rf /',
|
||||
'rm -rf node_modules\nrm -rf /',
|
||||
])('never lets a safe-looking target hide a destructive command: %s', (command) => {
|
||||
const { exitCode, output } = runHook(CAREFUL_SCRIPT, carefulInput(command));
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output.permissionDecision).toBe('ask');
|
||||
expect(output.message).toContain('recursive delete');
|
||||
});
|
||||
});
|
||||
|
||||
// --- SQL destructive commands ---
|
||||
|
||||
Reference in New Issue
Block a user