Skip to content
Closed
10 changes: 5 additions & 5 deletions backend/tests/test_container_dependency_pin_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,24 @@ def test_container_provenance_dependency_pins_match_reviewed_manifests() -> None
root_importer, "devDependencies", "postcss"
)
jsdom_resolution = importer_resolution(root_importer, "devDependencies", "jsdom")
assert postcss_resolution == {"specifier": "8.5.24", "version": "8.5.24"}
assert postcss_resolution == {"specifier": "8.5.26", "version": "8.5.26"}
assert jsdom_resolution == {"specifier": "^30.0.1", "version": "30.0.1"}

assert frontend_package["devDependencies"]["postcss"] == "8.5.24"
assert frontend_package["devDependencies"]["postcss"] == "8.5.26"
assert frontend_package["devDependencies"]["jsdom"] == "^30.0.1"
assert frontend_package["overrides"]["postcss"] == "8.5.24"
assert frontend_package["overrides"]["postcss"] == "8.5.26"
assert frontend_package["overrides"]["brace-expansion"] == "5.0.9"
assert frontend_package["overrides"]["undici"] == "8.9.0"

assert frontend_lock["overrides"] == {
**frontend_lock["overrides"],
"postcss": "8.5.24",
"postcss": "8.5.26",
"brace-expansion": "5.0.9",
"undici": "8.9.0",
}
package_records = frontend_lock["packages"]
for exact_lock_entry in (
"postcss@8.5.24",
"postcss@8.5.26",
"jsdom@30.0.1",
"brace-expansion@5.0.9",
"undici@8.9.0",
Expand Down
52 changes: 52 additions & 0 deletions backend/tests/test_frontend_framework_security_floor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Fail closed when frontend framework/image dependencies regress below patched floors."""

from __future__ import annotations

import json
import re
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[2]
FRONTEND_ROOT = REPO_ROOT / "frontend"


def _exact_version(value: str) -> tuple[int, int, int]:
"""Return a three-part exact version, rejecting ranges and prereleases."""

match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", value)
assert match is not None, f"expected exact semantic version, got {value!r}"
return tuple(int(part) for part in match.groups())


def test_frontend_framework_and_image_security_floors() -> None:
"""Keep Next.js and sharp at releases containing the reviewed security fixes."""

package = json.loads((FRONTEND_ROOT / "package.json").read_text(encoding="utf-8"))
next_value = package["dependencies"]["next"]
eslint_next_value = package["devDependencies"]["eslint-config-next"]

assert _exact_version(next_value) >= (16, 3, 3), (
"Next.js must include the fixes for CVE-2026-75604 and "
"GHSA-2xp9-vwfh-vxw4"
)
assert eslint_next_value == next_value, (
"eslint-config-next must stay on the same reviewed release as Next.js"
)

workspace = (FRONTEND_ROOT / "pnpm-workspace.yaml").read_text(encoding="utf-8")
sharp_match = re.search(
r"(?m)^\s{2}sharp:\s*[\"']?(\d+\.\d+\.\d+)[\"']?\s*$",
workspace,
)
assert sharp_match is not None, "pnpm workspace must keep an explicit sharp override"
sharp_value = sharp_match.group(1)
assert _exact_version(sharp_value) >= (0, 35, 4), (
"sharp must include the fix for GHSA-rgj7-g3m4-5g8c"
)

lock = (FRONTEND_ROOT / "pnpm-lock.yaml").read_text(encoding="utf-8")
assert f"next@{next_value}" in lock, "lockfile must resolve the reviewed Next.js release"
assert f"sharp@{sharp_value}" in lock, "lockfile must resolve the reviewed sharp release"
assert "next@16.3.1" not in lock, "vulnerable Next.js 16.3.1 must not remain locked"
assert "sharp@0.35.0" not in lock, "vulnerable sharp 0.35.0 must not remain locked"
2 changes: 1 addition & 1 deletion backend/tests/test_frontend_nanoid_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ def test_frontend_lock_resolves_only_patched_nanoid_3x() -> None:
)
assert nanoid_3x == [f"nanoid@{PATCHED_NANOID_VERSION}"]

postcss_snapshot = lock["snapshots"]["postcss@8.5.24"]
postcss_snapshot = lock["snapshots"]["postcss@8.5.26"]
assert postcss_snapshot["dependencies"]["nanoid"] == PATCHED_NANOID_VERSION
2 changes: 1 addition & 1 deletion frontend/.pnpmfile.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module.exports = {
if (pkg.peerDependencies && pkg.peerDependencies.postcss) {
pkg.peerDependencies.postcss = '^8.5.15';
}
if (pkg.devDependencies && pkg.devDependencies.postcss) {
if (pkg.devDependencies && pkg.devDependencies.postcss && pkg.devDependencies.postcss !== '8.5.26') {
Comment thread
seonghobae marked this conversation as resolved.
pkg.devDependencies.postcss = '^8.5.15';
}
return pkg;
Expand Down
26 changes: 13 additions & 13 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,45 @@
"test:e2e": "playwright test"
},
"dependencies": {
"@base-ui/react": "^1.6.0",
"@base-ui/react": "^1.7.0",
"@radix-ui/react-tabs": "^1.1.21",
"@tailwindcss/postcss": "^4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.27.0",
"next": "16.2.12",
"lucide-react": "^1.33.0",
"next": "16.3.4",
"react": "19.2.8",
"react-dom": "19.2.8",
"react-resizable-panels": "^4.12.2",
"react-resizable-panels": "^4.12.3",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"uuid": "^14.0.1",
"vis-network": "^10.0.2"
"uuid": "^14.0.2",
"vis-network": "^10.1.2"
Comment thread
seonghobae marked this conversation as resolved.
},
"devDependencies": {
"@playwright/test": "^1.62.0",
"@playwright/test": "^1.62.1",
"@types/node": "^26",
"@types/react": "^19",
"@types/react-dom": "^19",
"@vitest/coverage-v8": "4.1.10",
"@vitest/coverage-v8": "4.1.11",
"eslint": "^9",
"eslint-config-next": "16.2.12",
"eslint-config-next": "16.3.4",
"fast-check": "^4.9.0",
"jsdom": "^30.0.1",
"postcss": "8.5.24",
"postcss": "8.5.26",
Comment thread
seonghobae marked this conversation as resolved.
"typescript": "^6",
"vitest": "^4.1.10"
"vitest": "^4.1.11"
},
"overrides": {
"brace-expansion": "5.0.9",
"postcss": "8.5.24",
"postcss": "8.5.26",
"undici": "8.9.0",
"uuid": "^14.0.0"
},
"resolutions": {
"brace-expansion": "5.0.9",
"postcss": "8.5.24",
"postcss": "8.5.26",
"undici": "8.9.0"
}
}
Loading