Skip to content

fix(cookie): cap chunk count in setChunkedCookie#1469

Merged
pi0 merged 1 commit into
mainfrom
fix/chunked-cookie-cap
Jul 15, 2026
Merged

fix(cookie): cap chunk count in setChunkedCookie#1469
pi0 merged 1 commit into
mainfrom
fix/chunked-cookie-cap

Conversation

@pi0x

@pi0x pi0x commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

setChunkedCookie had no upper bound on chunk count, but the reader getChunkedCookie caps at MAX_CHUNKED_COOKIE_COUNT (100) and returns undefined above it — so an oversized value (e.g. a large sealed session) was written yet never re-readable, silently losing data and emitting more Set-Cookie headers than browsers accept. It now throws when a value would exceed the cap so the set and get sides agree.

This also fixes an off-by-one in the stale-chunk cleanup loop (it re-deleted the last overlapping chunk), keeping cleanup correct when a value shrinks to a single non-chunked cookie. Regression tests cover the cap (throw + boundary round-trip) and the cleanup.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Prevented oversized chunked cookies from being written when they exceed the supported limit.
    • Improved cleanup when replacing chunked cookies with smaller or single-cookie values.
    • Ensured cookies at the maximum supported size remain readable.

`setChunkedCookie` had no upper bound on the number of chunks, while the
reader (`getChunkedCookie`) caps at `MAX_CHUNKED_COOKIE_COUNT` (100) and
returns `undefined` above it. An oversized value was written but never
re-readable (silent data loss) and emitted more Set-Cookie headers than
browsers accept. Now throws when the value would exceed the cap so the
set and get sides agree.

Also fixes an off-by-one in the stale-chunk cleanup loop that redundantly
re-deleted the last overlapping chunk, without breaking cleanup when a
value shrinks to a single non-chunked cookie.

Co-Authored-By: Claude Fable 5 <[email protected]>
@pi0x
pi0x requested a review from pi0 as a code owner July 14, 2026 12:38
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e5d0aa7d-a202-4214-8f7c-c8edf45b1573

📥 Commits

Reviewing files that changed from the base of the PR and between 2264316 and 28635bd.

📒 Files selected for processing (2)
  • src/utils/cookie.ts
  • test/cookies.test.ts

📝 Walkthrough

Walkthrough

setChunkedCookie now rejects values exceeding the supported chunk count, accepts values at the limit, and removes stale chunk cookies when switching to an unchunked value. Tests cover rejection, round-trip readability, header emission, and cleanup.

Changes

Chunked Cookie Handling

Layer / File(s) Summary
Chunked-cookie write validation and cleanup
src/utils/cookie.ts, test/cookies.test.ts
setChunkedCookie enforces the maximum chunk count, deletes obsolete chunks when values shrink to one cookie, and tests rejection, boundary acceptance, readability, and cleanup.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: danielroe, pi0

Poem

I’m a rabbit guarding cookies tight,
No giant crumb may take flight.
Old chunks hop away in a row,
The maximum stays readable below.
Fresh headers bloom—what a delightful byte!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: capping chunk count in setChunkedCookie.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/chunked-cookie-cap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pi0x pi0x left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

APPROVE

Reviewed the change independently (checked out the branch, ran tests + lint). The fix is correct and well-tested.

What I verified

Cap is placed before any mutation — no partial writes. The chunkCount > MAX_CHUNKED_COOKIE_COUNT throw runs before the getCookie/deleteCookie/setCookie calls, so a rejected value never leaves stray Set-Cookie headers. The test asserting getSetCookie() is empty on throw locks this in.

Set/get cap agreement is exact, no off-by-one. Reader getChunkedCookieCount returns NaN when count > 100, so 100 is valid and 101 is not. Writer throws when chunkCount > 100. Same constant, same > direction — a value needing exactly 100 chunks is both writable and readable, which the boundary round-trip test confirms.

Cleanup rework is correct across all transitions:

  • multi -> fewer-multi: deletes name.(chunkCount+1)..prev, skipping the benign re-delete of the overlapping last chunk (original started at chunkCount and re-deleted name.chunkCount, harmless only because the fresh write overwrites it via setCookie dedup — the author's claim checks out).
  • multi -> single non-chunked: newChunkCount = 0, so cleanup starts at name.1 and removes every prior chunk. Correct.
  • plain/single -> multi: previous cookie isn't __chunked__, so the block is skipped — nothing to clean.
  • multi -> same/more: loop no-ops; fresh writes overwrite. Correct.

No stale name.N survives and no needed chunk is deleted in any path. The newChunkCount + 1 choice correctly handles the single-value case that the plan's naive chunkCount + 1 would have broken (leaving name.1).

Error type (HTTPError 500): defensible. An oversized sealed session is a server-side condition (session.ts is the only internal caller), and HTTPError integrates with h3's error handling to yield a proper 500 rather than a raw throw; h3 masks 500 bodies by default so the name/counts in the message aren't leaked to clients. A plain TypeError would also be reasonable, but 500 is a fine call — not blocking.

Test quality: the throw test is a genuine regression test — I confirmed all 4 matrix variants fail with the cap check disabled. The boundary and cleanup tests are guards (they also pass on main, since the original already handled multi->single correctly); they don't catch a pre-existing bug but usefully lock in the refactor. Worth a mention, not a concern.

Style/checks: import of HTTPError added cleanly, comments are clear, matches AGENTS.md conventions. pnpm vitest run test/cookies.test.ts -> 108 passed; pnpm lint -> clean.

Nice catch on the cleanup off-by-one while adding the cap. LGTM.

🤖 Generated with Claude Code

@pi0
pi0 merged commit 8eb1063 into main Jul 15, 2026
9 checks passed
@pi0
pi0 deleted the fix/chunked-cookie-cap branch July 15, 2026 08:04
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.

2 participants