mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-22 21:00:51 +02:00
fix(security): delete chain's shadow dispatcher that skipped every security gate
meta-commands.ts carried a 'CLI mode' fallback that re-implemented command
routing without the server pipeline's gates: no scope check, no domain check,
no tab ownership, no rate limit, no hidden-element stripping, no scoped-token
enveloping — and it called handleReadCommand without a BrowserManager, which
also skipped the JS-origin cookie-exfiltration assertion. It was unreachable
in production (server.ts always passes executeCommand) and one boolean away
from being live.
chain now hard-errors without a server context. handleReadCommand's bm param
is required and assertJsOriginAllowed runs unconditionally. The chain tests
that exercised the deleted fallback now route through a server-shaped
executeCommand adapter (real handlers + trust wrapping + {status,result}
envelope), so their behavioral coverage — sequencing, trust markers, pipe
format, aliases, error reporting — survives on the production-shaped path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b241a79ee5
commit
ef03646e07
@@ -339,15 +339,18 @@ describe('frame --url ReDoS fix', () => {
|
||||
// ─── Task 7: watch-mode guard in chain command ───────────────────────────────
|
||||
|
||||
describe('chain command watch-mode guard', () => {
|
||||
it('chain loop contains isWatching() guard before write dispatch', () => {
|
||||
// Post-alias refactor: loop iterates over canonicalized `c of commands`.
|
||||
const block = sliceBetween(META_SRC, 'for (const c of commands)', 'Wait for network to settle');
|
||||
expect(block).toContain('isWatching');
|
||||
// The direct-dispatch fallback (which carried its own isWatching() guard)
|
||||
// was deleted — it skipped every OTHER server gate. Chain subcommands now
|
||||
// route exclusively through executeCommand -> handleCommandInternal, whose
|
||||
// watch-mode write gate covers them. Pin both halves of that contract.
|
||||
it('chain has no direct-dispatch fallback (executeCommand is mandatory)', () => {
|
||||
const block = sliceBetween(META_SRC, 'const executeCmd = opts?.executeCommand', 'Wait for network to settle');
|
||||
expect(block).toContain('chain requires the browse server (no executeCommand context)');
|
||||
expect(block).not.toContain('handleWriteCommand(');
|
||||
});
|
||||
|
||||
it('chain loop BLOCKED message appears for write commands in watch mode', () => {
|
||||
const block = sliceBetween(META_SRC, 'for (const c of commands)', 'Wait for network to settle');
|
||||
expect(block).toContain('BLOCKED: write commands disabled in watch mode');
|
||||
it('server pipeline blocks write commands in watch mode (covers chain subcommands)', () => {
|
||||
expect(SERVER_SRC).toMatch(/isWatching\(\)\s*&&\s*isWriteInvocation\(command, args\)/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user