mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
chore(deps): raise the stale advisory floors and pin valibot
Three overrides had drifted behind the advisories they were written for, and the scan found the packages at versions the pins were supposed to exclude. dompurify moves from <=3.4.10 to <3.4.13. GHSA-55q2-fjhq-7xh7 is fixed in 3.4.13 and the tree carried 3.4.12. The copy is jspdf's optional HTML renderer, an optionalDependency at ^3.3.1, so the raise stays inside the range jspdf declares. postcss moves from <8.5.10 to <8.5.23 for CVE-2026-69153. next declares an exact postcss@8.4.31, so nothing but this bound moves it; the build has run on the 8.5 line since the first bump and stays there. valibot gets a floor it never had, <1.4.2, for CVE-2026-59952. Two copies arrive and neither is ours to call: an optional peer of @hookform/resolvers, which this app does not use because it validates with zod, and prisma's local dev server. 1.4.2 is the same major, so no consumer is moved onto a new one. @hono/node-server moves from <1.19.13 to <1.19.15 for GHSA-frvp-7c67-39w9. The app tree already resolved 1.19.17, so this changes nothing here; it exists so the bound states the real floor and so the Dockerfile mirror has something correct to match. The floor test in ci-determinism asserted the exact key and replacement string, which meant every legitimate raise failed it while a floor gone stale against a newer advisory passed it unchanged. It now reduces each key to a package name and asserts the pin is at or above the fixed version, with a case proving the comparison can return false.
This commit is contained in:
1 parent
c2ea07b0bc
commit
f62cd59789
3 files changed
+114
-36
No files matched your search
Generated
+17
-26
@@ -5,10 +5,10 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
dompurify@<=3.4.10: ^3.4.11
|
||||
postcss@<8.5.10: ^8.5.10
|
||||
dompurify@<3.4.13: ^3.4.13
|
||||
postcss@<8.5.23: ^8.5.23
|
||||
nanoid@<3.3.18: ^3.3.18
|
||||
'@hono/node-server@<1.19.13': ^1.19.13
|
||||
'@hono/node-server@<1.19.15': ^1.19.15
|
||||
hono@<4.12.25: ^4.12.25
|
||||
picomatch@<4.0.4: ^4.0.4
|
||||
body-parser@<2.3.0: ^2.3.0
|
||||
@@ -21,6 +21,7 @@ overrides:
|
||||
sharp@<0.35.0: ^0.35.3
|
||||
pdfjs-dist@>=5.6.83 <6.2.108: 6.2.108
|
||||
deepmerge-ts@<8.0.0: ^8.0.0
|
||||
valibot@<1.4.2: ^1.4.2
|
||||
|
||||
importers:
|
||||
|
||||
@@ -40,7 +41,7 @@ importers:
|
||||
version: 3.2.2(react@19.2.7)
|
||||
'@hookform/resolvers':
|
||||
specifier: ^5.9.1
|
||||
version: 5.9.1(@standard-schema/spec@1.1.0)(ajv-formats@2.1.1(ajv@8.20.0))(ajv@8.20.0)(effect@3.20.0)(react-hook-form@7.83.0(react@19.2.7))(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3)
|
||||
version: 5.9.1(@standard-schema/spec@1.1.0)(ajv-formats@2.1.1(ajv@8.20.0))(ajv@8.20.0)(effect@3.20.0)(react-hook-form@7.83.0(react@19.2.7))(valibot@1.4.2(typescript@6.0.3))(zod@4.4.3)
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.30.0
|
||||
version: 1.30.0(@cfworker/json-schema@4.1.1)(supports-color@7.2.0)(zod@4.4.3)
|
||||
@@ -864,7 +865,7 @@ packages:
|
||||
react-hook-form: ^7.55.0
|
||||
superstruct: '>=0.12.0'
|
||||
typanion: ^3.3.2
|
||||
valibot: '>=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc'
|
||||
valibot: ^1.4.2
|
||||
vest: '>=6.0.0'
|
||||
yup: ^1.0.0
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
@@ -4204,8 +4205,8 @@ packages:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dompurify@3.4.12:
|
||||
resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==}
|
||||
dompurify@3.4.14:
|
||||
resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==}
|
||||
|
||||
dot-prop@6.0.1:
|
||||
resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==}
|
||||
@@ -5961,10 +5962,6 @@ packages:
|
||||
resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
postcss@8.5.20:
|
||||
resolution: {integrity: sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postcss@8.5.25:
|
||||
resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -6923,8 +6920,8 @@ packages:
|
||||
utrie@1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
|
||||
valibot@1.2.0:
|
||||
resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==}
|
||||
valibot@1.4.2:
|
||||
resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
peerDependenciesMeta:
|
||||
@@ -7923,7 +7920,7 @@ snapshots:
|
||||
dependencies:
|
||||
hono: 4.13.3
|
||||
|
||||
'@hookform/resolvers@5.9.1(@standard-schema/spec@1.1.0)(ajv-formats@2.1.1(ajv@8.20.0))(ajv@8.20.0)(effect@3.20.0)(react-hook-form@7.83.0(react@19.2.7))(valibot@1.2.0(typescript@6.0.3))(zod@4.4.3)':
|
||||
'@hookform/resolvers@5.9.1(@standard-schema/spec@1.1.0)(ajv-formats@2.1.1(ajv@8.20.0))(ajv@8.20.0)(effect@3.20.0)(react-hook-form@7.83.0(react@19.2.7))(valibot@1.4.2(typescript@6.0.3))(zod@4.4.3)':
|
||||
dependencies:
|
||||
'@standard-schema/utils': 0.3.0
|
||||
react-hook-form: 7.83.0(react@19.2.7)
|
||||
@@ -7932,7 +7929,7 @@ snapshots:
|
||||
ajv: 8.20.0
|
||||
ajv-formats: 2.1.1(ajv@8.20.0)
|
||||
effect: 3.20.0
|
||||
valibot: 1.2.0(typescript@6.0.3)
|
||||
valibot: 1.4.2(typescript@6.0.3)
|
||||
zod: 4.4.3
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
@@ -8894,7 +8891,7 @@ snapshots:
|
||||
proper-lockfile: 4.1.2
|
||||
remeda: 2.33.4
|
||||
std-env: 3.10.0
|
||||
valibot: 1.2.0(typescript@6.0.3)
|
||||
valibot: 1.4.2(typescript@6.0.3)
|
||||
zeptomatch: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
@@ -11068,7 +11065,7 @@ snapshots:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dompurify@3.4.12:
|
||||
dompurify@3.4.14:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
optional: true
|
||||
@@ -12253,7 +12250,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
canvg: 3.0.11
|
||||
core-js: 3.49.0
|
||||
dompurify: 3.4.12
|
||||
dompurify: 3.4.14
|
||||
html2canvas: 1.4.1
|
||||
|
||||
jsx-ast-utils@3.3.5:
|
||||
@@ -12603,7 +12600,7 @@ snapshots:
|
||||
'@swc/helpers': 0.5.15
|
||||
baseline-browser-mapping: 2.10.43
|
||||
caniuse-lite: 1.0.30001805
|
||||
postcss: 8.5.20
|
||||
postcss: 8.5.26
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@7.2.0))(react@19.2.7)
|
||||
@@ -12990,12 +12987,6 @@ snapshots:
|
||||
cssesc: 3.0.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
postcss@8.5.20:
|
||||
dependencies:
|
||||
nanoid: 3.3.18
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
postcss@8.5.25:
|
||||
dependencies:
|
||||
nanoid: 3.3.18
|
||||
@@ -14169,7 +14160,7 @@ snapshots:
|
||||
base64-arraybuffer: 1.0.2
|
||||
optional: true
|
||||
|
||||
valibot@1.2.0(typescript@6.0.3):
|
||||
valibot@1.4.2(typescript@6.0.3):
|
||||
optionalDependencies:
|
||||
typescript: 6.0.3
|
||||
|
||||
|
||||
+21
-3
@@ -1,8 +1,20 @@
|
||||
overrides:
|
||||
"dompurify@<=3.4.10": "^3.4.11"
|
||||
"postcss@<8.5.10": "^8.5.10"
|
||||
# GHSA-55q2-fjhq-7xh7: a crafted document gets past the sanitiser and its
|
||||
# markup survives into the output. The copy here is jspdf's optional HTML
|
||||
# renderer, which the doctor-report PDF path reaches.
|
||||
"dompurify@<3.4.13": "^3.4.13"
|
||||
# CVE-2026-69153. Next declares an exact `postcss@8.4.31`, so nothing except
|
||||
# this bound moves it. The build has run on the 8.5 line since the first
|
||||
# bump; the fix landed in 8.5.23.
|
||||
"postcss@<8.5.23": "^8.5.23"
|
||||
"nanoid@<3.3.18": "^3.3.18"
|
||||
"@hono/node-server@<1.19.13": "^1.19.13"
|
||||
# GHSA-frvp-7c67-39w9 raised the floor from 1.19.13 to 1.19.15. Mirrored for
|
||||
# /opt/prisma-cli in the Dockerfile, which is where the flagged 1.19.11
|
||||
# actually landed: prisma pulls it through @prisma/dev, and that install
|
||||
# reads npm `overrides`, not this file. The bound stays on the 1.x line
|
||||
# because the advisory is fixed there too and 2.x is a major nothing asked
|
||||
# for.
|
||||
"@hono/node-server@<1.19.15": "^1.19.15"
|
||||
"hono@<4.12.25": "^4.12.25"
|
||||
"picomatch@<4.0.4": "^4.0.4"
|
||||
"body-parser@<2.3.0": "^2.3.0"
|
||||
@@ -39,6 +51,12 @@ overrides:
|
||||
# override is the only way through. It runs on the config-loading and CLI
|
||||
# path, not under a request, but a red audit blocks the whole release train.
|
||||
"deepmerge-ts@<8.0.0": "^8.0.0"
|
||||
# CVE-2026-59952. Two copies arrive, neither of them ours to call: an
|
||||
# optional peer of `@hookform/resolvers` (this app validates with zod) and
|
||||
# prisma's local dev server. Mirrored for /opt/prisma-cli in the Dockerfile,
|
||||
# where the flagged 1.2.0 landed. 1.4.2 is the same major, so no consumer is
|
||||
# dragged onto a new one.
|
||||
"valibot@<1.4.2": "^1.4.2"
|
||||
|
||||
allowBuilds:
|
||||
"@prisma/engines": true
|
||||
|
||||
@@ -150,17 +150,86 @@ describe("container dependency installation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* `>= 0` comparison over a dotted version, enough for the floor checks below.
|
||||
* Pre-release suffixes are not expected in an override replacement and are
|
||||
* ignored rather than guessed at.
|
||||
*/
|
||||
function atLeast(version: string, floor: string): boolean {
|
||||
const parts = (value: string) =>
|
||||
value
|
||||
.replace(/^[\^~>=<\s]+/, "")
|
||||
.split("-")[0]
|
||||
.split(".")
|
||||
.map((piece) => Number.parseInt(piece, 10) || 0);
|
||||
const [a, b] = [parts(version), parts(floor)];
|
||||
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
|
||||
const left = a[i] ?? 0;
|
||||
const right = b[i] ?? 0;
|
||||
if (left !== right) return left > right;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
describe("production dependency advisory floors", () => {
|
||||
it("pins vulnerable transitive ranges to compatible patched versions", () => {
|
||||
/**
|
||||
* The floor an advisory demands, by package. Raising an override past one of
|
||||
* these must not break this test: an earlier version of it asserted the exact
|
||||
* key and replacement string, so every legitimate floor raise failed it while
|
||||
* a floor that had gone STALE against a newer advisory passed. It now asserts
|
||||
* the property that matters, which is that the pin is at or above the fixed
|
||||
* version. Update a floor here when a new advisory raises it.
|
||||
*/
|
||||
const floors: Record<string, string> = {
|
||||
// GHSA-55q2-fjhq-7xh7, reached through jspdf's optional HTML renderer.
|
||||
dompurify: "3.4.13",
|
||||
// CVE-2026-69153.
|
||||
postcss: "8.5.23",
|
||||
// GHSA-frvp-7c67-39w9 and CVE-2026-39406. Mirrored in the Dockerfile.
|
||||
"@hono/node-server": "1.19.15",
|
||||
hono: "4.12.25",
|
||||
// CVE-2026-59952. Mirrored in the Dockerfile.
|
||||
valibot: "1.4.2",
|
||||
};
|
||||
|
||||
it("pins vulnerable transitive ranges at or above the fixed version", () => {
|
||||
const workspace = parse(readRepoFile("pnpm-workspace.yaml")) as {
|
||||
overrides?: Record<string, string>;
|
||||
};
|
||||
const overrides = workspace.overrides ?? {};
|
||||
|
||||
expect(workspace.overrides).toMatchObject({
|
||||
"dompurify@<=3.4.10": "^3.4.11",
|
||||
"postcss@<8.5.10": "^8.5.10",
|
||||
"@hono/node-server@<1.19.13": "^1.19.13",
|
||||
"hono@<4.12.25": "^4.12.25",
|
||||
});
|
||||
const byName = new Map<string, string>();
|
||||
for (const [key, replacement] of Object.entries(overrides)) {
|
||||
const selectorAt = key.startsWith("@")
|
||||
? key.indexOf("@", 1)
|
||||
: key.indexOf("@");
|
||||
byName.set(
|
||||
selectorAt === -1 ? key : key.slice(0, selectorAt),
|
||||
replacement,
|
||||
);
|
||||
}
|
||||
expect(byName.size).toBeGreaterThan(0);
|
||||
|
||||
for (const [name, floor] of Object.entries(floors)) {
|
||||
const replacement = byName.get(name);
|
||||
expect(
|
||||
replacement,
|
||||
`${name} has no override in pnpm-workspace.yaml`,
|
||||
).toBeDefined();
|
||||
expect(
|
||||
atLeast(replacement as string, floor),
|
||||
`${name} is pinned to ${replacement}, below the advisory floor ${floor}.`,
|
||||
).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("compares versions rather than always agreeing", () => {
|
||||
// The comparison is the whole test above; a helper that never returns
|
||||
// false would make it vacuous.
|
||||
expect(atLeast("^3.4.13", "3.4.13")).toBe(true);
|
||||
expect(atLeast("^3.4.14", "3.4.13")).toBe(true);
|
||||
expect(atLeast("^3.4.12", "3.4.13")).toBe(false);
|
||||
expect(atLeast("^1.19.9", "1.19.15")).toBe(false);
|
||||
expect(atLeast("^2.0.5", "1.19.15")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user