mirror of https://github.com/garrytan/gstack.git
fix(security): remove deleted /sidebar-chat endpoint from tunnel allowlist
TUNNEL_PATHS is the audited tunnel attack surface — its own comment says every addition widens it. '/sidebar-chat' stayed in the set after the endpoint was deleted with the chat-queue path, meaning any future route matching that path would have been silently tunnel-exposed. The set is now exactly the pair ceremony (/connect) and the scoped command endpoint (/command), and the dual-listener closed-set pin enforces that. Also repairs a pre-existing red pin in dual-listener.test.ts: v1.63.0.0 made the tunnel allowlist args-aware (canDispatchOverTunnel gained a second param) without updating the test — red on main since then, invisible because the free suite had no CI job. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a646a32b41
commit
b241a79ee5
|
|
@ -303,7 +303,6 @@ export function resolveConfigFromEnv(): Omit<ServerConfig, 'browserManager' | 's
|
|||
const TUNNEL_PATHS = new Set<string>([
|
||||
'/connect',
|
||||
'/command',
|
||||
'/sidebar-chat',
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -48,9 +48,12 @@ describe('Dual-listener surface types', () => {
|
|||
});
|
||||
|
||||
describe('Tunnel path allowlist', () => {
|
||||
test('TUNNEL_PATHS is a closed set containing exactly /connect, /command, /sidebar-chat', () => {
|
||||
test('TUNNEL_PATHS is a closed set containing exactly /connect, /command', () => {
|
||||
// /sidebar-chat sat in this set long after the endpoint was deleted with
|
||||
// the chat-queue path — a stale entry in the audited tunnel attack
|
||||
// surface. The set is exactly the pair ceremony + command endpoint.
|
||||
const paths = extractSetContents(SERVER_SRC, 'TUNNEL_PATHS');
|
||||
expect(paths).toEqual(new Set(['/connect', '/command', '/sidebar-chat']));
|
||||
expect(paths).toEqual(new Set(['/connect', '/command']));
|
||||
});
|
||||
|
||||
test('TUNNEL_PATHS does NOT contain bootstrap or admin paths', () => {
|
||||
|
|
@ -220,7 +223,10 @@ describe('/command tunnel command allowlist', () => {
|
|||
'return handleCommand(body, tokenInfo)'
|
||||
);
|
||||
expect(commandBlock).toContain("surface === 'tunnel'");
|
||||
expect(commandBlock).toContain('canDispatchOverTunnel(body?.command)');
|
||||
// v1.63.0.0 made the allowlist args-aware (canDispatchOverTunnel gained a
|
||||
// second param for --out denial); this pin was stale from then until the
|
||||
// free suite got a CI job.
|
||||
expect(commandBlock).toContain('canDispatchOverTunnel(body?.command, body?.args)');
|
||||
expect(commandBlock).toContain('disallowed_command');
|
||||
expect(commandBlock).toContain('is not allowed over the tunnel surface');
|
||||
expect(commandBlock).toContain('status: 403');
|
||||
|
|
|
|||
Loading…
Reference in New Issue