diff --git a/browse/src/cdp-allowlist.ts b/browse/src/cdp-allowlist.ts index 9e1f9f655..b4faa46ff 100644 --- a/browse/src/cdp-allowlist.ts +++ b/browse/src/cdp-allowlist.ts @@ -162,6 +162,20 @@ export const CDP_ALLOWLIST: ReadonlyArray = Object.freeze([ output: 'trusted', justification: 'Media type/feature override (prefers-color-scheme, prefers-reduced-motion, prefers-contrast, forced-colors) so a11y and dark-mode CSS branches are testable. Returns an empty result; no page content. NOTE: like setUserAgentOverride the override persists on the tab until cleared with an empty features array.', }, + { + domain: 'Emulation', + method: 'setCPUThrottlingRate', + scope: 'tab', + output: 'trusted', + justification: 'CPU slowdown multiplier on the active tab, for measuring performance on a realistic low-end client instead of the developer workstation. Same domain and mutating character as setDeviceMetricsOverride; affects only timing, reads nothing, exfiltrates nothing.', + }, + { + domain: 'Network', + method: 'emulateNetworkConditions', + scope: 'tab', + output: 'trusted', + justification: 'Bandwidth/latency emulation on the active tab, for measuring page behaviour on a slow connection. Constrains traffic rather than reading it — no request bodies, headers or cookies are exposed.', + }, // ─── Page capture (output, not navigation) ───────────────── { domain: 'Page', diff --git a/browse/test/cdp-allowlist.test.ts b/browse/test/cdp-allowlist.test.ts index 73d339f23..0693781a1 100644 --- a/browse/test/cdp-allowlist.test.ts +++ b/browse/test/cdp-allowlist.test.ts @@ -82,6 +82,19 @@ describe('CDP allowlist (T2: deny-default)', () => { expect(e!.output).toBe('trusted'); }); + it('CPU + network throttling are allowed, tab-scoped, trusted (#2602)', () => { + // Perf-measurement emulation (PR #2602 by @henbima): both constrain the + // tab's timing/traffic, read nothing, and return empty results — same + // posture argument as setEmulatedMedia (#2419) and setDeviceMetricsOverride. + for (const method of ['Emulation.setCPUThrottlingRate', 'Network.emulateNetworkConditions']) { + expect(isCdpMethodAllowed(method)).toBe(true); + const e = lookupCdpMethod(method); + expect(e).not.toBeNull(); + expect(e!.scope).toBe('tab'); + expect(e!.output).toBe('trusted'); + } + }); + it('untrusted-output methods cover the read-everything-attacker-controlled cases', () => { // Anything that reads attacker-controlled strings (DOM/AX/CSS selectors) // should be tagged untrusted so the envelope wraps the result.