From dcbaaad64e552efbf777f8b5209b8bcdcd01deb4 Mon Sep 17 00:00:00 2001 From: Sean Oliver <882952+seanoliver@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:15:11 -0700 Subject: [PATCH] fix(common): decode URL-encoded mw_diag cookie before parsing (#43606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The Phase 1 mw_diag monitoring dashboard wasn't showing any data despite the middleware PR (#43413) being live in production. Next.js `response.cookies.set()` URL-encodes cookie values, so `hit=1&would_stamp=0&has_cookie=0` becomes `hit%3D1%26would_stamp%3D0%26has_cookie%3D0` in `document.cookie`. Our `parseMwDiagCookie` was passing that encoded string directly to `URLSearchParams`, which splits on literal `&` and `=` — the encoded `%26`/`%3D` delimiters were invisible to it. Result: `params.get('hit')` always returned `null`, so every diagnostic cookie was silently dropped. ## Changes - Add `decodeURIComponent()` before `URLSearchParams` in `parseMwDiagCookie` - Add 3 test cases covering URL-encoded cookie values (matching what Next.js actually produces in production) ## Testing - All 34 unit tests pass (31 existing + 3 new) - Verified locally with Playwright: manually set the URL-encoded cookie format on localhost:8082, confirmed `parseMwDiagCookie` now returns `{hit: true, ...}` and the data flows through to `PageTelemetry` GROWTH-625 --- packages/common/first-referrer-cookie.test.ts | 27 +++++++++++++++++++ packages/common/first-referrer-cookie.ts | 4 +-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/common/first-referrer-cookie.test.ts b/packages/common/first-referrer-cookie.test.ts index 18f54214620..4fa97a92751 100644 --- a/packages/common/first-referrer-cookie.test.ts +++ b/packages/common/first-referrer-cookie.test.ts @@ -232,6 +232,33 @@ describe('first-referrer-cookie', () => { has_existing_cookie: false, }) }) + + it('parses URL-encoded value from Next.js response.cookies.set()', () => { + const header = `${MW_DIAG_COOKIE_NAME}=hit%3D1%26would_stamp%3D1%26has_cookie%3D0` + expect(parseMwDiagCookie(header)).toEqual({ + hit: true, + would_stamp: true, + has_existing_cookie: false, + }) + }) + + it('parses URL-encoded value with has_cookie=1', () => { + const header = `${MW_DIAG_COOKIE_NAME}=hit%3D1%26would_stamp%3D0%26has_cookie%3D1` + expect(parseMwDiagCookie(header)).toEqual({ + hit: true, + would_stamp: false, + has_existing_cookie: true, + }) + }) + + it('parses URL-encoded value among multiple cookies', () => { + const header = `session=abc; ${MW_DIAG_COOKIE_NAME}=hit%3D1%26would_stamp%3D0%26has_cookie%3D0; theme=dark` + expect(parseMwDiagCookie(header)).toEqual({ + hit: true, + would_stamp: false, + has_existing_cookie: false, + }) + }) }) describe('shouldRefreshCookie', () => { diff --git a/packages/common/first-referrer-cookie.ts b/packages/common/first-referrer-cookie.ts index 4f9a8436fb6..2fef56d656b 100644 --- a/packages/common/first-referrer-cookie.ts +++ b/packages/common/first-referrer-cookie.ts @@ -290,8 +290,8 @@ export function parseMwDiagCookie(cookieHeader: string): MwDiagData | null { if (!match) return null - const value = match.slice(`${MW_DIAG_COOKIE_NAME}=`.length) - const params = new URLSearchParams(value) + const rawValue = match.slice(`${MW_DIAG_COOKIE_NAME}=`.length) + const params = new URLSearchParams(decodeURIComponent(rawValue)) if (params.get('hit') !== '1') return null