From 2031b5a72254de99a907523ef06da2bcf62edfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 2 Oct 2026 21:03:43 +0200 Subject: [PATCH 1/4] chore(deps): ignore the node-forge advisory until a fix is published GHSA-86w9-cpqp-85rv affects RSA PKCS#1 v1.5 signature verification in node-forge up to 1.4.0, which arrives only through @parse/node-apn for certificate-based APNs credentials. HealthLog uses token authentication, so that path never runs, and no patched release exists yet. --- pnpm-workspace.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d1e045b06..a1304d0c1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -110,3 +110,13 @@ minimumReleaseAgeExclude: - "@next/bundle-analyzer@16.2.12" - lucide-react@1.27.0 - undici@8.10.0 + +auditConfig: + ignoreGhsas: + # node-forge <=1.4.0, RSA PKCS#1 v1.5 signature verification. It arrives + # only through @parse/node-apn, which uses it for certificate-based APNs + # credentials (p12 / PEM certificate). HealthLog authenticates to APNs + # with a token (.p8 key, APNS_KEY_ID / APNS_TEAM_ID), so that code path + # never runs. No patched release is published yet (latest is 1.4.0); + # remove this entry and raise the floor once 1.4.1 is out. + - GHSA-86w9-cpqp-85rv From 0cb193f812e4d80cd0d0fd02270b1bc161141c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 2 Oct 2026 21:06:37 +0200 Subject: [PATCH 2/4] chore(deps): carry the node-forge advisory in the Trivy ignore list too Same advisory, same reasoning as the pnpm audit entry: the affected signature verification sits in node-apn's certificate credential path, which token-authenticated APNs never reaches, and no fix is published. --- .trivyignore | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.trivyignore b/.trivyignore index 829fa1515..ccf17286e 100644 --- a/.trivyignore +++ b/.trivyignore @@ -21,3 +21,13 @@ CVE-2026-33671 # never reachable at runtime. Re-check when the Node base image ships an npm # build whose bundled sigstore is >= 4.1.1, then drop this entry. CVE-2026-48815 + +# CVE-2026-85393 (node-forge RSA PKCS#1 v1.5 signature forgery, HIGH; GitHub +# advisory GHSA-86w9-cpqp-85rv) — node-forge <= 1.4.0 arrives only through +# @parse/node-apn, which uses it in its certificate-based credential path +# (credentials/certificate/*: p12 / PEM certificate). HealthLog authenticates +# to APNs with a token (.p8 key via APNS_KEY_ID / APNS_TEAM_ID), so that code +# never runs. No fixed release is published (latest is 1.4.0). Remove this +# entry, the matching `auditConfig.ignoreGhsas` entry in pnpm-workspace.yaml, +# and raise the floor once node-forge 1.4.1 is out. +CVE-2026-85393 From 4125d08fb47ddb57707e01837b9764b4ac7d04f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 2 Oct 2026 22:33:17 +0200 Subject: [PATCH 3/4] test(e2e): wait for the lab review sheet to stop sliding before measuring On a phone and a small tablet the review sheet slides up from the bottom; its width settles before the slide ends, so waiting on width alone still compared fields from different frames. The spec now waits for every running animation to finish and for the surface to hold its position and width across two frames. --- e2e/dialog-horizontal-overflow.spec.ts | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/e2e/dialog-horizontal-overflow.spec.ts b/e2e/dialog-horizontal-overflow.spec.ts index 3dbbbde62..07b198b23 100644 --- a/e2e/dialog-horizontal-overflow.spec.ts +++ b/e2e/dialog-horizontal-overflow.spec.ts @@ -428,14 +428,23 @@ test.describe("lab-scan review dialog uses the width it is given", () => { await expect(rows).toHaveCount(REVIEW_ROWS.length); const label = `scan review @${vp.label}`; - // The sheet widens from the picking stage to the review stage and the - // row grid re-flows from the container width it is given. Measuring in - // the middle of that sees a half-laid-out row, so wait until the - // surface has kept the same width across two frames. + // The sheet widens from the picking stage to the review stage, and on a + // phone or small tablet it also slides up from the bottom. Measuring + // while either runs compares fields from different frames (the width + // settles before the slide ends, so waiting on width alone was not + // enough). Wait for every running animation and transition to finish, + // then for the surface to hold still across two frames. + await page.evaluate(() => + Promise.all( + document + .getAnimations() + .map((animation) => animation.finished.catch(() => undefined)), + ), + ); await expect .poll( async () => { - const before = (await surface.boundingBox())!.width; + const before = (await surface.boundingBox())!; await page.evaluate( () => new Promise((resolve) => @@ -444,10 +453,13 @@ test.describe("lab-scan review dialog uses the width it is given", () => { ), ), ); - const after = (await surface.boundingBox())!.width; - return Math.abs(after - before); + const after = (await surface.boundingBox())!; + return ( + Math.abs(after.width - before.width) + + Math.abs(after.y - before.y) + ); }, - { message: `${label}: dialog width settles` }, + { message: `${label}: dialog settles` }, ) .toBeLessThan(0.5); await expectNoSidewaysScroll(page, label); From c08dfc618f12e91a3e40a809cf260310ba0503a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 2 Oct 2026 22:56:27 +0200 Subject: [PATCH 4/4] test(e2e): call a lab review field beside the value when it shares its row The spec compared field tops within 8 px. A field label that wraps onto a second line, which depends on the runner's font metrics, moves that field's input down by one line while it stays in the same row, and the 560 px case failed on it. A field is now beside the value when it sits to its right and starts above the value's bottom edge. --- e2e/dialog-horizontal-overflow.spec.ts | 28 +++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/e2e/dialog-horizontal-overflow.spec.ts b/e2e/dialog-horizontal-overflow.spec.ts index 07b198b23..72d49564f 100644 --- a/e2e/dialog-horizontal-overflow.spec.ts +++ b/e2e/dialog-horizontal-overflow.spec.ts @@ -482,20 +482,24 @@ test.describe("lab-scan review dialog uses the width it is given", () => { // Value, unit and date share a line when the fields have room for three // columns, and the date wraps under them when they do not. + // "Beside" means the same grid row: to the right of the value and + // starting above the value's bottom edge. Comparing the tops alone was + // too strict, because a field label that wraps onto a second line (it + // depends on the runner's font metrics) moves that field's input down + // by one line while it stays in the same row. + const besideValue = (field: { x: number; y: number }, name: string) => { + expect(field.x, `${label}: ${name} right of value`).toBeGreaterThan( + value.x, + ); + expect(field.y, `${label}: ${name} in the value's row`).toBeLessThan( + value.y + value.height, + ); + }; if (vp.threeAcross) { - expect( - Math.abs(unit.y - value.y), - `${label}: unit beside value`, - ).toBeLessThan(8); - expect( - Math.abs(date.y - value.y), - `${label}: date beside value`, - ).toBeLessThan(8); + besideValue(unit, "unit"); + besideValue(date, "date"); } else { - expect( - Math.abs(unit.y - value.y), - `${label}: unit beside value`, - ).toBeLessThan(8); + besideValue(unit, "unit"); expect(date.y, `${label}: date wraps`).toBeGreaterThan( value.y + value.height - 1, );