mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-07 06:48:37 +02:00
fix: cookie picker auth token leak (v0.15.17.0) (#904)
* fix: cookie picker auth token leak (CVE — CVSS 7.8) GET /cookie-picker served HTML that inlined the master bearer token without authentication. Any local process could extract it and use it to call /command, executing arbitrary JS in the browser context. Fix: Jupyter-style one-time code exchange. The picker URL now includes a one-time code that is consumed via 302 redirect, setting an HttpOnly session cookie. The master AUTH_TOKEN never appears in HTML. The session cookie is isolated from the scoped token system (not valid for /command). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump version and changelog (v0.15.17.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: browse-snapshot E2E turn budget too tight (7 → 9) The agent consistently uses 8 turns for 5 snapshot commands because it reads the saved annotated PNG to verify it was created. All 3 CI attempts hit error_max_turns at exactly 8. Bumping to 9 gives headroom. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
b73f364411
commit
a7593d70ef
@@ -317,4 +317,28 @@ describe('Server auth security', () => {
|
||||
// The ownership check condition must exclude newtab
|
||||
expect(ownershipBlock).toContain("command !== 'newtab'");
|
||||
});
|
||||
|
||||
// CVE fix: cookie-picker HTML must NOT inline the auth token.
|
||||
// getCookiePickerHTML() must not accept an authToken parameter.
|
||||
test('cookie-picker UI does not accept or inline auth token', () => {
|
||||
const uiSrc = fs.readFileSync(path.join(import.meta.dir, '../src/cookie-picker-ui.ts'), 'utf-8');
|
||||
// Function signature must not include authToken
|
||||
expect(uiSrc).not.toMatch(/getCookiePickerHTML\([^)]*authToken/);
|
||||
// No AUTH_TOKEN interpolation in template
|
||||
expect(uiSrc).not.toContain("AUTH_TOKEN = '${authToken");
|
||||
expect(uiSrc).not.toContain("AUTH_TOKEN = '${auth");
|
||||
});
|
||||
|
||||
// CVE fix: cookie-picker route handler uses one-time code exchange, not open access.
|
||||
test('cookie-picker HTML route requires code or session cookie', () => {
|
||||
const routeSrc = fs.readFileSync(path.join(import.meta.dir, '../src/cookie-picker-routes.ts'), 'utf-8');
|
||||
// Must have code validation
|
||||
expect(routeSrc).toContain('pendingCodes');
|
||||
expect(routeSrc).toContain('validSessions');
|
||||
// Must NOT pass authToken to getCookiePickerHTML
|
||||
expect(routeSrc).not.toMatch(/getCookiePickerHTML\([^)]*authToken/);
|
||||
// Must set HttpOnly session cookie
|
||||
expect(routeSrc).toContain('HttpOnly');
|
||||
expect(routeSrc).toContain('SameSite=Strict');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user