From bee4ac4f73d78a4b4fbf1c33df04841129332272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 7 Nov 2026 02:22:28 +0100 Subject: [PATCH 1/2] build(deps): move Next.js to 16.4.5 16.4.5 is a security release with seven moderate advisories. Most are about 'use cache' and Cache Components, which the app does not use; one covers self-hosted apps that forward fetched responses. --- CHANGELOG.md | 4 ++ package.json | 6 +-- pnpm-lock.yaml | 116 ++++++++++++++++++++++++------------------------- 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5dc7c737..b6f74563e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -159,6 +159,10 @@ ### Security +- Next.js moves to 16.4.5, which fixes seven moderate advisories. Most + concern `use cache` and Cache Components, which HealthLog does not use; + one concerns self-hosted apps that pass a fetched response on to the + client, and one the Edge runtime's internal headers. - `@hono/node-server` moves to the 2.x line, which fixes a second decode of the request path in `serveStatic`. It arrives under the MCP SDK; the app uses the SDK's web-standard transport and never loads it, but the copy diff --git a/package.json b/package.json index 1323ed8a0..885aa8c5b 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "jspdf-autotable": "^5.0.9", "lucide-react": "^1.66.0", "mmdb-lib": "^3.0.3", - "next": "16.4.3", + "next": "16.4.5", "nodemailer": "10.3.1", "otpauth": "^9.5.2", "p-limit": "^7.3.3", @@ -107,7 +107,7 @@ }, "devDependencies": { "@axe-core/playwright": "^4.14.0", - "@next/bundle-analyzer": "16.4.3", + "@next/bundle-analyzer": "16.4.5", "@playwright/test": "^1.64.1", "@stryker-mutator/core": "10.1.1", "@stryker-mutator/vitest-runner": "10.0.0", @@ -125,7 +125,7 @@ "@vitest/coverage-v8": "^5.0.5", "ajv": "^8.20.0", "eslint": "^9", - "eslint-config-next": "16.4.3", + "eslint-config-next": "16.4.5", "fast-check": "4.10.2", "knip": "^6.44.1", "lefthook": "^2.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aace7cce0..d1673c6e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,8 +125,8 @@ importers: specifier: ^3.0.3 version: 3.0.3 next: - specifier: 16.4.3 - version: 16.4.3(@babel/core@8.0.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.6)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) + specifier: 16.4.5 + version: 16.4.5(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.6)(react-dom@19.3.1(react@19.3.1))(react@19.3.1) nodemailer: specifier: 10.3.1 version: 10.3.1 @@ -201,8 +201,8 @@ importers: specifier: ^4.14.0 version: 4.14.0(playwright-core@1.64.1) '@next/bundle-analyzer': - specifier: 16.4.3 - version: 16.4.3 + specifier: 16.4.5 + version: 16.4.5 '@playwright/test': specifier: ^1.64.1 version: 1.64.1 @@ -255,8 +255,8 @@ importers: specifier: ^9 version: 9.39.3(jiti@2.7.0)(supports-color@7.2.0) eslint-config-next: - specifier: 16.4.3 - version: 16.4.3(@typescript-eslint/parser@8.71.0(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + specifier: 16.4.5 + version: 16.4.5(@typescript-eslint/parser@8.71.0(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) fast-check: specifier: 4.10.2 version: 4.10.2 @@ -1329,63 +1329,63 @@ packages: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 - '@next/bundle-analyzer@16.4.3': - resolution: {integrity: sha512-FGcLziT2rqvGZTxZUjnCzwjCANnigN9vrbCm+1hQtzXbbtc1bXBTRmkXohVqyB/ReF5bOjpM2bNJQhz0PfLk9Q==} + '@next/bundle-analyzer@16.4.5': + resolution: {integrity: sha512-kIlb+9t1i/Zg7yuRXN7I9nhEb92UGbnMfIzpZPps7YoCgmneCMpmN6zG6mduUluhI+y00MGLytkDsccVCujrGw==} - '@next/env@16.4.3': - resolution: {integrity: sha512-a7tz1QCcWWkwC/rPJcPMJk/UNRu9CFhDd3OHYE5hddbJrs8BqRTalxI6GlKCWEGzRLHN+S/B7a4PAHCtm25aBQ==} + '@next/env@16.4.5': + resolution: {integrity: sha512-JwSPwpdkZL5VMl2jA/q+JI8MZdweAL3xmw5/tb9y/TLtmS0YXZnmYDzSihwk8xTj1OL+WM53gTqaj96yuLp6Xw==} - '@next/eslint-plugin-next@16.4.3': - resolution: {integrity: sha512-oxuC06wt/71h6ACYCV9GbKOfnf5a7VlRe/p80YHTut15TNgDEmynDnaq8t/xhy5rlvk7dhItIPs6H3rvIM4kyw==} + '@next/eslint-plugin-next@16.4.5': + resolution: {integrity: sha512-dNmc4y4En/34tWxaBFF2tG/R9GeiRwVPf7v8Az/Pqk8FcT4Uv0ePmrZKvWBL2nFy1QprPgJW+6XjfyV6jbRV3Q==} - '@next/swc-darwin-arm64@16.4.3': - resolution: {integrity: sha512-4yq5EXBt+VDtAZwTyfijaFp5D2C5oz3Ikw5a2kiX2K9nw72vrZX2q5UuAlkPtUMO1tQz6JTKpaKWjS7OOefTDQ==} + '@next/swc-darwin-arm64@16.4.5': + resolution: {integrity: sha512-W5TniBzWKASbNNwHZ/S/f1MxQUXU/LdjklVMWA0Ujbfl7SZnUzFls45hO0i/sF7Bx1jJBWp7WWmoQajXtX/O8A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.4.3': - resolution: {integrity: sha512-1Rvkn/LUhnEF8WxnRkOrqGhbwXui1bkWFxONKIbN+r7C+9KHDSZ+iUoj4CzymVMWUyZIyx9szcb9+kbKhD500A==} + '@next/swc-darwin-x64@16.4.5': + resolution: {integrity: sha512-uWq4qkHapBDS1oFltWS4YxTb9pLHAtLNcpbuK6SmRsebLYZqpBML/j/C0op+50JYGTpZgVL/DWNt/6oWlamrrw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.4.3': - resolution: {integrity: sha512-dCKpXErGg3abUM6t5JTJV/ottVBTStJB5lRJTbgdUdQaQ+jNHiK56Hsc0YAEg/rinSh42hYG3HWiYmEMdilnxg==} + '@next/swc-linux-arm64-gnu@16.4.5': + resolution: {integrity: sha512-FdRcyOx5u5P51mlAqLB9EXdd8jZO5fsdHtk4BAQSkqOywCsbTNN/rmw+jO7pnmjNdAtu2AG46NzbCL2FPDrrgw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.4.3': - resolution: {integrity: sha512-WwYbhWkhYBvBtib1K4W0kL8MW2snBD2A5b3SN4vPpqalZbZs5+bQ1yzAdepVoOqrFfEW3dmREy4Zs1MrWcrFbw==} + '@next/swc-linux-arm64-musl@16.4.5': + resolution: {integrity: sha512-cyvmmgtRXR8aGVT86wi9Py53IlfyCx7P2fZxuAXAoTummDnhKtUyjrMeXfapqp4qgD+gT5RqyGD/VfA16jEK7Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.4.3': - resolution: {integrity: sha512-QymWvfUwDEC5/lpJOsxX5VAbyoR2D13+2Jsjz4be7gT4LPqEgdpkeLIR/HFfCLgKzoMhpJbp6I/JnogyNZHcaA==} + '@next/swc-linux-x64-gnu@16.4.5': + resolution: {integrity: sha512-hPc+Fl+N22DAvqZHq559NmamXboOX5Lu8I5Wl0DezfrhTk1Zz21NQeYoZDmNI/NjTcrdsVxM72Fzn2lI+Q+vAw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.4.3': - resolution: {integrity: sha512-Vmjd9D7DpA4Vv3cUlr82QWgJD/BQZmns801vtC5VWHg6jCIA4a5vMKpQnfrM2pyCrp2eV0ENdUg4PlN3bAc2Yw==} + '@next/swc-linux-x64-musl@16.4.5': + resolution: {integrity: sha512-gbACYE8IP/jlprNTpiWXHb8N9CofOzpnEwH2SrIz1dUbiJf1FbDssDupCAPeP1Taxe8mgG20sGZamJI7KFOdZw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.4.3': - resolution: {integrity: sha512-lG1pPdPLk7gTyGb00n5JIMLdBJgjb+UEhSdZvFbhUMg0DyC6z4B7RnpSKF9VCd4imkLObVbXOFA0kFCpw7yFow==} + '@next/swc-win32-arm64-msvc@16.4.5': + resolution: {integrity: sha512-+8gj++ZMo8H20Xh2ijjNqyFk5LPLw0AC4Fs4Tq/G94rjjQXhOjN4jgSWsWKPFsyY26hfIeemJIwKtMxOsp5GBA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.4.3': - resolution: {integrity: sha512-NmQC9RkJdnPXRaMxtkCzobB3teVjqF33oSOnwDfi+XYGWWYpt+xlGK3mtktSKLbdjwaWbJu2OkkMiRFGUUncKg==} + '@next/swc-win32-x64-msvc@16.4.5': + resolution: {integrity: sha512-qNZj66pYXnaWLiF9R9GUbfBdrCv7kC6TaZfIwB2KH5WPc/8pEM3nVOj8hpQhWJU8S94rlH5MPDcX15+RrgUhiw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -4272,8 +4272,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@16.4.3: - resolution: {integrity: sha512-j9IqBma6abp800frdUURaizYMgXOK+jNGyq6CsYcM9AxpSJs5D/TgMNGeAmRUilZvA7g9V1Tr71gNg9/SVTuMQ==} + eslint-config-next@16.4.5: + resolution: {integrity: sha512-mvh39zYLoNjpT5tVmf8xZu7i5lEOb5jN5cW6qD9OpeCp4GEEOeYta0Z2XyKiD0pYdlvm5/Td6Bi890maPRlIjg==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -5611,8 +5611,8 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - next@16.4.3: - resolution: {integrity: sha512-OqxKTGi9u80FydydsMNYNjnuje9Au+kvHlt2GZ//ftdpDHm1dxKqAaENoOOTX5wKzn+9vaKUNIfZeyZUuLhCnA==} + next@16.4.5: + resolution: {integrity: sha512-rmdfsWosjTUO+6R0S3KokoIk6Kj0sWDRTe86tZHdqaWH/oXq/udX3/4xb+YQi7aWbyEFoglDU54kaifLCdXbBw==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -8480,44 +8480,44 @@ snapshots: '@tybys/wasm-util': 0.10.4 optional: true - '@next/bundle-analyzer@16.4.3': + '@next/bundle-analyzer@16.4.5': dependencies: webpack-bundle-analyzer: 4.10.1 transitivePeerDependencies: - bufferutil - utf-8-validate - '@next/env@16.4.3': {} + '@next/env@16.4.5': {} - '@next/eslint-plugin-next@16.4.3(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))': + '@next/eslint-plugin-next@16.4.5(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0)) fast-glob: 3.3.1 transitivePeerDependencies: - eslint - '@next/swc-darwin-arm64@16.4.3': + '@next/swc-darwin-arm64@16.4.5': optional: true - '@next/swc-darwin-x64@16.4.3': + '@next/swc-darwin-x64@16.4.5': optional: true - '@next/swc-linux-arm64-gnu@16.4.3': + '@next/swc-linux-arm64-gnu@16.4.5': optional: true - '@next/swc-linux-arm64-musl@16.4.3': + '@next/swc-linux-arm64-musl@16.4.5': optional: true - '@next/swc-linux-x64-gnu@16.4.3': + '@next/swc-linux-x64-gnu@16.4.5': optional: true - '@next/swc-linux-x64-musl@16.4.3': + '@next/swc-linux-x64-musl@16.4.5': optional: true - '@next/swc-win32-arm64-msvc@16.4.3': + '@next/swc-win32-arm64-msvc@16.4.5': optional: true - '@next/swc-win32-x64-msvc@16.4.3': + '@next/swc-win32-x64-msvc@16.4.5': optional: true '@noble/hashes@2.4.0': {} @@ -11518,9 +11518,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@16.4.3(@typescript-eslint/parser@8.71.0(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3): + eslint-config-next@16.4.5(@typescript-eslint/parser@8.71.0(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3): dependencies: - '@next/eslint-plugin-next': 16.4.3(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0)) + '@next/eslint-plugin-next': 16.4.5(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0)) eslint: 9.39.3(jiti@2.7.0)(supports-color@7.2.0) eslint-import-resolver-node: 0.3.10(supports-color@7.2.0) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) @@ -12860,25 +12860,25 @@ snapshots: negotiator@1.0.0: {} - next@16.4.3(@babel/core@8.0.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.6)(react-dom@19.3.1(react@19.3.1))(react@19.3.1): + next@16.4.5(@babel/core@7.29.7(supports-color@7.2.0))(@opentelemetry/api@1.9.1)(@playwright/test@1.64.1)(@types/node@26.6.6)(react-dom@19.3.1(react@19.3.1))(react@19.3.1): dependencies: - '@next/env': 16.4.3 + '@next/env': 16.4.5 '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.11.27 caniuse-lite: 1.0.30001815 postcss: 8.5.23 react: 19.3.1 react-dom: 19.3.1(react@19.3.1) - styled-jsx: 5.1.6(@babel/core@8.0.7)(react@19.3.1) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 16.4.3 - '@next/swc-darwin-x64': 16.4.3 - '@next/swc-linux-arm64-gnu': 16.4.3 - '@next/swc-linux-arm64-musl': 16.4.3 - '@next/swc-linux-x64-gnu': 16.4.3 - '@next/swc-linux-x64-musl': 16.4.3 - '@next/swc-win32-arm64-msvc': 16.4.3 - '@next/swc-win32-x64-msvc': 16.4.3 + '@next/swc-darwin-arm64': 16.4.5 + '@next/swc-darwin-x64': 16.4.5 + '@next/swc-linux-arm64-gnu': 16.4.5 + '@next/swc-linux-arm64-musl': 16.4.5 + '@next/swc-linux-x64-gnu': 16.4.5 + '@next/swc-linux-x64-musl': 16.4.5 + '@next/swc-win32-arm64-msvc': 16.4.5 + '@next/swc-win32-x64-msvc': 16.4.5 '@opentelemetry/api': 1.9.1 '@playwright/test': 1.64.1 sharp: 0.35.5(@types/node@26.6.6) @@ -14090,12 +14090,12 @@ snapshots: strip-json-comments@5.0.3: {} - styled-jsx@5.1.6(@babel/core@8.0.7)(react@19.3.1): + styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.3.1): dependencies: client-only: 0.0.1 react: 19.3.1 optionalDependencies: - '@babel/core': 8.0.7 + '@babel/core': 7.29.7(supports-color@7.2.0) supports-color@7.2.0: dependencies: From 73b9544f83d33f4987a032eb99aa584901aca4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 7 Nov 2026 03:37:26 +0100 Subject: [PATCH 2/2] test(e2e): check the PDF trailer instead of Content-Length Next 16.4.5 drops content-length, content-encoding and transfer-encoding from every route handler response, so the header the export route sets never reaches the browser. A truncated body still loses its %%EOF trailer, and that is what the spec now looks for. --- e2e/doctor-report.spec.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/e2e/doctor-report.spec.ts b/e2e/doctor-report.spec.ts index 764b658c3..b8909cd2a 100644 --- a/e2e/doctor-report.spec.ts +++ b/e2e/doctor-report.spec.ts @@ -287,7 +287,6 @@ async function chooseStandardPdfScope(page: Page): Promise { interface GeneratedReport { status: number; contentType: string; - contentLength: string | undefined; bytes: Buffer; text: string; filename: string; @@ -348,7 +347,6 @@ async function generateReport(page: Page): Promise { return { status: response.status(), contentType: response.headers()["content-type"] ?? "", - contentLength: response.headers()["content-length"], bytes, text, filename: download.suggestedFilename(), @@ -370,9 +368,13 @@ function expectRealPdf(report: GeneratedReport): void { expect(report.contentType).toContain("application/pdf"); expect(report.bytes.subarray(0, 5).toString("latin1")).toBe("%PDF-"); expect(report.bytes.byteLength).toBeGreaterThan(MIN_PDF_BYTES); - // The route declares the length it sent; a disagreement means the body was - // truncated on the way out, which a magic-number check alone would miss. - expect(report.contentLength).toBe(String(report.bytes.byteLength)); + // A body truncated on the way out keeps its header but loses the trailer, + // which a magic-number check alone would miss. The route's Content-Length + // cannot say this: Next drops that header from every route response since + // 16.4.5, so the bytes have to. + expect(report.bytes.subarray(-1024).toString("latin1").trimEnd()).toMatch( + /%%EOF$/, + ); // The name the CLIENT anchor invents, not the route's `Content-Disposition` // — the two differ by a word today. This says the panel saved a PDF, and it // is not contract coverage of the header.