Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the ability to measure authenticated pages by seeding cookies extracted from a raw curl command (e.g., copied from Chrome DevTools). It adds --curl and --curl-file options to the CLI, implements a parser to extract cookies from these curl commands, and updates the core types and Playwright driver to support seeding these cookies and extra HTTP headers into the browser context. Feedback focuses on improving the robustness of the curl cookie parser to handle unquoted headers, zsh/bash escaped single quotes, and adding corresponding test cases. Additionally, it is recommended to explicitly error out if both --curl and --curl-file arguments are provided simultaneously.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const HEADER_COOKIE_RE = /(?:-H|--header)\s+\$?(['"])\s*cookie\s*:\s*(.*?)\1/is; | ||
| const COOKIE_FLAG_RE = /(?:-b|--cookie)\s+\$?(['"])(.*?)\1/is; | ||
|
|
||
| /** | ||
| * Extracts cookies from a raw curl command, e.g. one pasted from Chrome DevTools | ||
| * "Copy as cURL". Looks for a `-H 'Cookie: ...'` header or a `-b '...'` flag. | ||
| * Does not attempt full shell-quote parsing (no support for escaped quotes inside | ||
| * the cookie value) — good enough for the common browser-exported curl formats. | ||
| */ | ||
| export function parseCookiesFromCurl(curl: string): ParsedCookie[] { | ||
| const raw = curl.match(HEADER_COOKIE_RE)?.[2] ?? curl.match(COOKIE_FLAG_RE)?.[2]; | ||
| if (!raw) return []; | ||
| return raw | ||
| .split(";") | ||
| .map((pair) => pair.trim()) | ||
| .filter(Boolean) | ||
| .map((pair) => { | ||
| const eq = pair.indexOf("="); | ||
| if (eq === -1) return { name: pair, value: "" }; | ||
| return { name: pair.slice(0, eq).trim(), value: pair.slice(eq + 1).trim() }; | ||
| }); | ||
| } |
There was a problem hiding this comment.
The current regular expressions do not support unquoted cookie headers/flags, and they will truncate or corrupt cookie values containing single quotes (which are common in session/auth tokens) due to bash/zsh escaping patterns (e.g., \'' or \').
We can resolve this by:
- Normalizing bash-escaped single quotes (
'\'') to\'before matching. - Updating the regexes to match quoted strings with backslash escapes, or unquoted values.
- Unescaping the matched cookie string to restore the original characters.
const HEADER_COOKIE_RE = /(?:-H|--header)\s+(?:\$?(['"])\s*cookie\s*:\s*((?:(?!\1).|\\.)*)\1|cookie\s*:\s*([^\s'"]+))/is;
const COOKIE_FLAG_RE = /(?:-b|--cookie)\s+(?:\$?(['"])((?:(?!\1).|\\.)*)\1|([^\s'"]+))/is;
/**
* Extracts cookies from a raw curl command, e.g. one pasted from Chrome DevTools
* "Copy as cURL". Looks for a `-H 'Cookie: ...'` header or a `-b '...'` flag.
* Supports bash-escaped single quotes, zsh ANSI-C quotes, and unquoted values.
*/
export function parseCookiesFromCurl(curl: string): ParsedCookie[] {
const normalized = curl.replace(/'\\''/g, "\\'");
const matchHeader = normalized.match(HEADER_COOKIE_RE);
const matchFlag = normalized.match(COOKIE_FLAG_RE);
const raw = matchHeader?.[2] ?? matchHeader?.[3] ?? matchFlag?.[2] ?? matchFlag?.[3];
if (!raw) return [];
const unescaped = raw.replace(/\\(.)/g, "$1");
return unescaped
.split(";")
.map((pair) => pair.trim())
.filter(Boolean)
.map((pair) => {
const eq = pair.indexOf("=");
if (eq === -1) return { name: pair, value: "" };
return { name: pair.slice(0, eq).trim(), value: pair.slice(eq + 1).trim() };
});
}| const curlFile = optString(args["curl-file"]); | ||
| let curlFileContents: string | undefined; | ||
| if (curlFile) { | ||
| try { | ||
| curlFileContents = readFileSync(curlFile, "utf8"); | ||
| } catch (err) { | ||
| logger.error(`failed to read --curl-file '${curlFile}': ${err instanceof Error ? err.message : String(err)}`); | ||
| process.exit(EXIT_CODES.invalidUsage); | ||
| } | ||
| } | ||
| const curlRaw = optString(args.curl) ?? curlFileContents; |
There was a problem hiding this comment.
If both --curl and --curl-file are provided, the CLI silently prioritizes --curl and ignores --curl-file. It is safer and more user-friendly to explicitly error out when conflicting arguments are provided.
| const curlFile = optString(args["curl-file"]); | |
| let curlFileContents: string | undefined; | |
| if (curlFile) { | |
| try { | |
| curlFileContents = readFileSync(curlFile, "utf8"); | |
| } catch (err) { | |
| logger.error(`failed to read --curl-file '${curlFile}': ${err instanceof Error ? err.message : String(err)}`); | |
| process.exit(EXIT_CODES.invalidUsage); | |
| } | |
| } | |
| const curlRaw = optString(args.curl) ?? curlFileContents; | |
| const curlFile = optString(args["curl-file"]); | |
| const curlArg = optString(args.curl); | |
| if (curlArg && curlFile) { | |
| logger.error("cannot provide both --curl and --curl-file"); | |
| process.exit(EXIT_CODES.invalidUsage); | |
| } | |
| let curlFileContents: string | undefined; | |
| if (curlFile) { | |
| try { | |
| curlFileContents = readFileSync(curlFile, "utf8"); | |
| } catch (err) { | |
| logger.error(`failed to read --curl-file '${curlFile}': ${err instanceof Error ? err.message : String(err)}`); | |
| process.exit(EXIT_CODES.invalidUsage); | |
| } | |
| } | |
| const curlRaw = curlArg ?? curlFileContents; |
| it("extracts cookies from an $'...' ANSI-C quoted header (Chrome zsh export)", () => { | ||
| const curl = `curl 'https://tradeit.gg/' -H $'cookie: session=abc123'`; | ||
| expect(parseCookiesFromCurl(curl)).toEqual([{ name: "session", value: "abc123" }]); | ||
| }); | ||
|
|
||
| it("returns an empty array when no cookie header/flag is present", () => { | ||
| expect(parseCookiesFromCurl(`curl 'https://tradeit.gg/'`)).toEqual([]); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Add test cases to verify the robust parsing of escaped single quotes (bash and zsh formats) and unquoted cookie headers/flags.
it("extracts cookies from an $'...' ANSI-C quoted header (Chrome zsh export)", () => {
const curl = `curl 'https://tradeit.gg/' -H $'cookie: session=abc123'`;
expect(parseCookiesFromCurl(curl)).toEqual([{ name: "session", value: "abc123" }]);
});
it("extracts cookies with escaped quotes (bash and zsh formats)", () => {
const curlBash = `curl 'https://tradeit.gg/' -H 'cookie: session=abc'\\''123; theme=dark'`;
expect(parseCookiesFromCurl(curlBash)).toEqual([
{ name: "session", value: "abc'123" },
{ name: "theme", value: "dark" },
]);
const curlZsh = `curl 'https://tradeit.gg/' -H $'cookie: session=abc\\\'123'`;
expect(parseCookiesFromCurl(curlZsh)).toEqual([{ name: "session", value: "abc'123" }]);
});
it("extracts cookies from unquoted headers and flags", () => {
expect(parseCookiesFromCurl(`curl https://tradeit.gg/ -b session=abc123`)).toEqual([
{ name: "session", value: "abc123" },
]);
expect(parseCookiesFromCurl(`curl https://tradeit.gg/ -H cookie:session=abc123`)).toEqual([
{ name: "session", value: "abc123" },
]);
});
it("returns an empty array when no cookie header/flag is present", () => {
expect(parseCookiesFromCurl(`curl 'https://tradeit.gg/'`)).toEqual([]);
});…ookies
Adds --curl / --curl-file to `ohmyperf run` so a curl command pasted from
Chrome DevTools ("Copy as cURL") can seed the browser context with the
Cookie header before navigation, enabling CWV measurement of pages behind
login. Cookies are set via Playwright's storageState (scoped by domain/path,
not leaked to third-party origins like extraHTTPHeaders would).
Also handles bash's `'\''` escape for a literal quote inside a cookie value,
and errors out if both --curl and --curl-file are passed together.
2c50537 to
8df0c30
Compare
|
Addressed in 8df0c30:
Left as-is: unquoted Also amended the commit author to match my account. |
Summary
--curl <command>and--curl-file <path>toohmyperf run, letting a curl command pasted from Chrome DevTools ("Copy as cURL") seed the browser context's cookies before navigation — so pages behind login can be measured.-H 'Cookie: ...'header or-b/--cookieflag (bash- and zsh-quoted curl exports both supported) and applied via Playwright'sstorageState.cookies, scoped by domain/path — not leaked to third-party origins the way a blanketextraHTTPHeaderswould.CookieInputtype onLaunchOpts(@ohmyperf/core) andcookies/extraHTTPHeaderssupport onPlaywrightAdapterOptions(@ohmyperf/driver-playwright), so the SDK consumers get this too, not just the CLI.Why
ohmyperfcurrently only measures anonymous/logged-out pages — no way to pass auth. Real product pages (account, checkout, inventory, etc.) are behind login, so this closes a real gap for the tool's stated "measure and let an AI agent fix it" use case.Test plan
tsc -bclean onpackages/core,packages/driver-playwright,apps/cliapps/cli/src/curl-cookies.test.ts) — bash header,-bflag, zsh$'...'header, no-cookie casedriver-playwrightandclitest suites pass (20/20, no regressions)createPlaywrightAdapterpath and hitpostman-echo.com/cookies— the server echoed back exactly the cookies parsed from the curl text--curl-fileagainst a live URL to confirm the flag wiring works, not just the internal functions--curl-filepath exits cleanly with an error message instead of a raw stack trace