mirror of https://github.com/garrytan/gstack.git
fix(browse): reject NaN/Infinity expires and case-fold internal domains
filterPersistableCookies trusted expires as a finite number and matched internal-network domains case-sensitively. A cookie with expires=NaN or Infinity slipped past the `=== -1` session check (NaN <= nowSec is false), and an uppercase `LOCALHOST` / `Foo.INTERNAL` bypassed the internal-domain block. Add a `Number.isFinite` guard and a shared `normalizeCookieDomain` that lower-cases before the internal-domain comparison. Allowlist matching already lower-cases internally, so behavior there is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1404106293
commit
c0d85f7c0e
|
|
@ -133,6 +133,11 @@ function lockPath(config: BrowseConfig): string {
|
|||
return path.join(autoDir(config), `${computeProfileId(config)}.lock`);
|
||||
}
|
||||
|
||||
function normalizeCookieDomain(domain: string): string {
|
||||
const d = domain.startsWith('.') ? domain.slice(1) : domain;
|
||||
return d.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep only cookies that are safe and sensible to persist:
|
||||
* - persistent (expires !== -1) and not already expired,
|
||||
|
|
@ -150,9 +155,9 @@ export function filterPersistableCookies(
|
|||
if (!c || typeof c.name !== 'string' || typeof c.value !== 'string') return false;
|
||||
if (typeof c.domain !== 'string' || !c.domain) return false;
|
||||
// Session cookie — Playwright reports expires === -1. Never persist.
|
||||
if (typeof c.expires !== 'number' || c.expires === -1) return false;
|
||||
if (typeof c.expires !== 'number' || c.expires === -1 || !Number.isFinite(c.expires)) return false;
|
||||
if (c.expires <= nowSec) return false; // already expired
|
||||
const d = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;
|
||||
const d = normalizeCookieDomain(c.domain);
|
||||
if (d === 'localhost' || d.endsWith('.internal') || d === '169.254.169.254') return false;
|
||||
if (allowlist && !hostMatchesAllowlist(c.domain, allowlist)) return false;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -100,13 +100,24 @@ describe('cookie filter', () => {
|
|||
test('rejects internal-network domains', () => {
|
||||
const kept = filterPersistableCookies([
|
||||
cookie({ name: 'lh', domain: 'localhost' }),
|
||||
cookie({ name: 'lh2', domain: 'LOCALHOST' }),
|
||||
cookie({ name: 'meta', domain: '169.254.169.254' }),
|
||||
cookie({ name: 'int', domain: 'foo.internal' }),
|
||||
cookie({ name: 'int2', domain: 'Foo.INTERNAL' }),
|
||||
cookie({ name: 'ok', domain: 'example.com' }),
|
||||
], null);
|
||||
expect(kept.map(c => c.name)).toEqual(['ok']);
|
||||
});
|
||||
|
||||
test('rejects malformed expires values', () => {
|
||||
const kept = filterPersistableCookies([
|
||||
cookie({ name: 'nan', expires: Number.NaN }),
|
||||
cookie({ name: 'inf', expires: Number.POSITIVE_INFINITY }),
|
||||
cookie({ name: 'ok' }),
|
||||
], null);
|
||||
expect(kept.map(c => c.name)).toEqual(['ok']);
|
||||
});
|
||||
|
||||
test('applies host allowlist incl. leading-wildcard + apex', () => {
|
||||
const cookies = [
|
||||
cookie({ name: 'a', domain: 'example.com' }),
|
||||
|
|
|
|||
Loading…
Reference in New Issue