Skip to content

fix(core): skip malformed cookie fragments in mergeCookies#3822

Open
anxkhn wants to merge 1 commit into
apify:masterfrom
anxkhn:patch-4
Open

fix(core): skip malformed cookie fragments in mergeCookies#3822
anxkhn wants to merge 1 commit into
apify:masterfrom
anxkhn:patch-4

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

mergeCookies() splits each source cookie string on ; and forced the result
of Cookie.parse() with a non-null assertion:

const cookie = Cookie.parse(cookieString)!;

tough-cookie's Cookie.parse returns undefined (it does not throw) for a
fragment it cannot parse, for example a bare name with no = (sessionid) or
one that starts with =. The ! suppressed that undefined, so the very next
line dereferenced it and threw:

TypeError: Cannot read properties of undefined (reading 'key')

This runs on the navigation hot path: http-based crawlers call mergeCookies
for every request that carries a cookie header, and that header can be an
arbitrary string a user set on the Request. A single malformed fragment turns
into a confusing crash that repeats on every retry until the request is marked
failed, instead of the fragment simply being ignored.

The fix guards the parsed cookie and skips fragments that cannot be parsed,
matching the two existing defensive continues already in the same loop. Valid
cookies are unaffected.

const cookie = Cookie.parse(cookieString);
// ignore fragments that tough-cookie cannot parse (e.g. a bare name with no value)
if (!cookie) continue;

Testing

Added a regression test next to the existing mergeCookies() test in
test/core/crawlers/cheerio_crawler.test.ts. It fails on master with the
TypeError above and passes with the fix:

test('mergeCookies() skips malformed cookie fragments instead of throwing', () => {
    expect(mergeCookies('https://example.com', ['valid=1; brokenfragment'])).toBe('valid=1');
    expect(mergeCookies('https://example.com', ['a=b', 'c'])).toBe('a=b');
    expect(mergeCookies('https://example.com', ['sessionid'])).toBe('');
});

yarn vitest run test/core/crawlers/cheerio_crawler.test.ts -> 48 passed.
Lint (eslint), typecheck (tsc --noEmit -p packages/core/tsconfig.json), and
biome format are all clean.

`mergeCookies()` split each source cookie string on `; ` and forced the
result of `Cookie.parse()` with a non-null assertion. tough-cookie's
`Cookie.parse` returns `undefined` (it does not throw) for an unparseable
fragment, such as a bare name with no `=` (e.g. `sessionid`) or one starting
with `=`. The assertion suppressed that, so the next `.key` access (and
`setCookieSync`) threw `TypeError: Cannot read properties of undefined`.

Because this runs on the navigation hot path for every request that carries a
user-supplied cookie header, a single malformed fragment turned into a
confusing crash that repeated until the request failed.

Guard the parsed cookie and skip fragments that cannot be parsed, matching the
existing defensive `continue`s in the same loop. Valid cookies are unaffected.

@barjin barjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your interest in this project @anxkhn !

The change you're describing is breaking - Crawlee would now silently swallow errors about invalid cookies. I believe we want to keep the throwing behaviour.

If you think the error message is confusing or doesn't correctly describe the source of the error, I'd prefer to update the error handling (e.g., share more information about the error in the message). What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants