mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-19 11:22:21 +02:00
fix(browse): adversarial-review hardening — 6 findings fixed, regression-pinned
Pre-push adversarial review (4 lenses, refute-style verification: 13 raw
findings, 7 refuted, 6 confirmed) caught these; each fix carries a pin:
1. --restrict=read (equals form) sailed past validatePairAgentFlags —
hasFlag/parseFlag are exact-token matches — so the user asked for a
read-only sandbox and silently got FULL access: the exact failure mode
this branch claims to close. The equals form is now a hard error before
any server work.
2. handleTunnel trimmed the agent name but clientIds are stored verbatim,
so a space-padded agent was unrevocable by the documented kill switch
(trimmed DELETE 404'd while the grant stayed live). Names now pass
through verbatim; the live-daemon test revokes ' padded'.
3. The sole pin for "CLI always sends explicit scopes" passed vacuously on
a simulated revert: toContain('DEFAULT_PAIR_SCOPES') was satisfied by a
comment. The tripwire now matches the code shape with a regex and bans
the conditional spread formatting-insensitively.
4. The rewritten 403 scope hint was unpinned — new e2e asserts it names
--restrict and --control and never --admin.
5. tunnelRevoke's verify-failure and HTTP-error branches and tunnelAgents'
unreadable-list branch had no coverage — three stub-daemon pins added
(an unreadable list must never render as "No paired agents").
6. CHANGELOG claimed "40+ new test cases"; the honest count is 35.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5b22add861
commit
1ae9aef999
@@ -264,6 +264,33 @@ describe('pair-agent flow end-to-end (HTTP only, no ngrok)', () => {
|
||||
expect(body.error).toContain('control');
|
||||
});
|
||||
|
||||
test('scope-denied 403 hint points at --restrict/--control, never --admin', async () => {
|
||||
// Regression: the old hint said "re-pair with --admin", which is a legacy
|
||||
// alias for --control — following it over-granted browser-wide control.
|
||||
const pairResp = await fetch(`${daemon.baseUrl}/pair`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${daemon.token}` },
|
||||
body: JSON.stringify({ clientId: 'hint-agent', scopes: ['read'] }),
|
||||
});
|
||||
const { setup_key } = await pairResp.json() as any;
|
||||
const connectResp = await fetch(`${daemon.baseUrl}/connect`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ setup_key }),
|
||||
});
|
||||
const { token } = await connectResp.json() as any;
|
||||
const resp = await fetch(`${daemon.baseUrl}/command`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||
body: JSON.stringify({ command: 'goto', args: ['https://example.com'] }),
|
||||
});
|
||||
expect(resp.status).toBe(403);
|
||||
const body = await resp.json() as any;
|
||||
expect(body.hint).toContain('--restrict');
|
||||
expect(body.hint).toContain('--control');
|
||||
expect(body.hint).not.toContain('--admin');
|
||||
});
|
||||
|
||||
// ─── Revocation e2e: revoke-all + the /agents verification surface ────
|
||||
|
||||
test('DELETE /token revokes session AND setup keys; agent leaves /agents; token 401s; re-connect fails', async () => {
|
||||
|
||||
Reference in New Issue
Block a user