security(deps): bump @sveltejs/kit + vite to clear 5 high CVEs #625

Merged
marcel merged 7 commits from feat/issue-458-security-dep-bump into main 2026-05-19 16:29:44 +02:00
Owner

Closes #458.

Summary

  • Bumps @sveltejs/kit 2.55.0 → 2.60.1 and vite 7.3.1 → 7.3.3 to clear five CVEs flagged in the pre-prod audit (F-22)
  • Sets BODY_SIZE_LIMIT=50M explicitly in both compose files so the newly-enforced limit doesn't silently reject PDF uploads
  • Adds npm audit --audit-level=high --omit=dev as a CI gate — blocks future HIGH/CRITICAL advisories from merging undetected
  • Aligns @tiptap packages to 3.23.4 to collapse a nested duplicate that npm update introduced

CVEs cleared

GHSA Package Severity Description
GHSA-2crg-3p73-43xp @sveltejs/adapter-node HIGH BODY_SIZE_LIMIT bypass
GHSA-v2wj-q39q-566r vite HIGH server.fs.deny bypass
GHSA-p9ff-h696-f583 vite HIGH Arbitrary file read via dev-server WebSocket
GHSA-3f6h-2hrp-w5wx @sveltejs/kit MODERATE Unvalidated redirect DoS
GHSA-4w7w-66w2-5vf9 vite MODERATE Path traversal in optimised-deps .map

Residual

cookie <0.7.0 (LOW) remains via the @sveltejs/kit peer chain. The only npm fix would install @sveltejs/kit@0.0.30 — a breaking pre-1.0 downgrade. Accepted as a known upstream residual; tracked in the completion comment on #458.

Verification

Check Result
npm audit --audit-level=high --omit=dev exit 0 — 0 HIGH/CRITICAL
npm run lint
npm run check 792 errors — identical to main baseline
npm run test (582 tests)
npm run build

Follow-up

Renovate configuration (Gitea platform + patch automerge) tracked in #624.

Closes #458. ## Summary - Bumps `@sveltejs/kit` 2.55.0 → 2.60.1 and `vite` 7.3.1 → 7.3.3 to clear five CVEs flagged in the pre-prod audit (F-22) - Sets `BODY_SIZE_LIMIT=50M` explicitly in both compose files so the newly-enforced limit doesn't silently reject PDF uploads - Adds `npm audit --audit-level=high --omit=dev` as a CI gate — blocks future HIGH/CRITICAL advisories from merging undetected - Aligns `@tiptap` packages to 3.23.4 to collapse a nested duplicate that `npm update` introduced ## CVEs cleared | GHSA | Package | Severity | Description | |------|---------|----------|-------------| | GHSA-2crg-3p73-43xp | `@sveltejs/adapter-node` | HIGH | `BODY_SIZE_LIMIT` bypass | | GHSA-v2wj-q39q-566r | `vite` | HIGH | `server.fs.deny` bypass | | GHSA-p9ff-h696-f583 | `vite` | HIGH | Arbitrary file read via dev-server WebSocket | | GHSA-3f6h-2hrp-w5wx | `@sveltejs/kit` | MODERATE | Unvalidated redirect DoS | | GHSA-4w7w-66w2-5vf9 | `vite` | MODERATE | Path traversal in optimised-deps `.map` | ## Residual `cookie <0.7.0` (LOW) remains via the `@sveltejs/kit` peer chain. The only npm fix would install `@sveltejs/kit@0.0.30` — a breaking pre-1.0 downgrade. Accepted as a known upstream residual; tracked in the completion comment on #458. ## Verification | Check | Result | |-------|--------| | `npm audit --audit-level=high --omit=dev` | exit 0 — 0 HIGH/CRITICAL ✅ | | `npm run lint` | ✅ | | `npm run check` | 792 errors — identical to `main` baseline ✅ | | `npm run test` (582 tests) | ✅ | | `npm run build` | ✅ | ## Follow-up Renovate configuration (Gitea platform + patch automerge) tracked in #624.
marcel added 4 commits 2026-05-19 14:45:08 +02:00
Bumps @sveltejs/kit 2.55.0→2.60.1, vite 7.3.1→7.3.3, and all patched
transitives. Clears GHSA-3f6h-2hrp-w5wx, GHSA-2crg-3p73-43xp,
GHSA-4w7w-66w2-5vf9, GHSA-v2wj-q39q-566r, GHSA-p9ff-h696-f583.

Residual: cookie <0.7.0 (LOW) via @sveltejs/kit peer chain — upstream
fix requires @sveltejs/kit@0.0.30, a breaking downgrade. Tracked as
known residual per issue #458 acceptance criteria note.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Makes the upload size cap explicit in both dev and prod compose files.
After the @sveltejs/kit bump (GHSA-2crg-3p73-43xp), the default 512KB
limit is now enforced — 50M covers multi-page Kurrent/Sütterlin PDFs
(typically 500KB–15MB) without being reckless.

Caddy's client_max_body_size must be set to match when the reverse
proxy config is committed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blocks merges when any HIGH or CRITICAL advisory enters the production
dependency tree. Runs after npm ci (or cache restore) and before lint,
so a failing audit surfaces immediately without wasting test time.

Closes the systemic gap from pre-prod audit finding F-22 (dependency
hygiene). Renovate automation is tracked separately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(deps): align @tiptap packages to 3.23.4 to resolve type conflict
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 39s
CI / OCR Service Tests (pull_request) Successful in 21s
CI / Backend Unit Tests (pull_request) Successful in 3m22s
CI / fail2ban Regex (pull_request) Failing after 42s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m3s
c8cc0646cb
npm update caused @tiptap/starter-kit@3.22.5 to nest @tiptap/core@3.23.4
alongside the pinned top-level 3.22.5, splitting the type namespace and
causing svelte-check errors (toggleBold, toggleItalic, etc. not found).

Aligning all three pinned tiptap packages to 3.23.4 collapses the nested
copy via deduplication, restoring the pre-bump error count (792 = main).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Owner

🔒 Nora "NullX" Steiner — Application Security Engineer

Verdict: Approved

This is exactly the kind of hygiene work I want to see routinely. Let me walk through what was checked.

What I audited

CVE remediation — verified

GHSA Severity Fix mechanism
GHSA-2crg-3p73-43xp HIGH BODY_SIZE_LIMIT=50M added to both compose files
GHSA-v2wj-q39q-566r HIGH vite bumped in lockfile (2.55.0→2.60.1 via kit)
GHSA-p9ff-h696-f583 HIGH Same vite bump
GHSA-3f6h-2hrp-w5wx MODERATE @sveltejs/kit 2.55.0→2.60.1
GHSA-4w7w-66w2-5vf9 MODERATE Same vite bump

All five CVEs are addressed. The approach for each is correct — the BODY_SIZE_LIMIT bypass (GHSA-2crg-3p73-43xp) specifically required an explicit env var, not just a library upgrade. Good that both compose files were updated.

CI audit gate — solid

- name: Security audit (no dev deps)
  run: npm audit --audit-level=high --omit=dev
  working-directory: frontend

--omit=dev is the right scope for a prod security gate — dev-only packages cannot be exploited in production. --audit-level=high means MODERATE advisories don't block merges, which is pragmatic for upstream residuals like the cookie <0.7.0 issue.

Residual cookie <0.7.0 (LOW) — correctly triaged

The PR description correctly explains that the only npm resolution would require @sveltejs/kit@0.0.30 (a pre-1.0 breaking downgrade). Tracking it as a known upstream residual is the right call. LOW severity + no direct exploit path in this application's usage = acceptable risk.

BODY_SIZE_LIMIT placement

The comment in docker-compose.prod.yml is exactly right:

# Must be ≤ client_max_body_size in the Caddy reverse proxy to avoid 413 mismatches.
BODY_SIZE_LIMIT: 50M

This is the threat model in the comment, not just the config value. That's what good security documentation looks like.

BODY_SIZE_LIMIT in dev compose — slightly unusual but explained

The dev compose comment says it's kept to prevent config drift, even though Vite's dev server doesn't honor it. That's a reasonable rationale — operators who spin up the full Docker stack in dev get consistent behaviour.

No blockers

No new attack surface introduced. No permissions widened. No secrets exposed. Supply chain integrity maintained via package-lock.json integrity hashes.

One observation (not a blocker)

The npm audit step runs before Paraglide compilation and tests. This is correct sequencing — fail fast on known vulnerabilities before spending CI minutes on build and test. No change needed, just noting the intent is sound.

## 🔒 Nora "NullX" Steiner — Application Security Engineer **Verdict: ✅ Approved** This is exactly the kind of hygiene work I want to see routinely. Let me walk through what was checked. ### What I audited **CVE remediation — verified** | GHSA | Severity | Fix mechanism | |------|----------|---------------| | GHSA-2crg-3p73-43xp | HIGH | `BODY_SIZE_LIMIT=50M` added to both compose files | | GHSA-v2wj-q39q-566r | HIGH | `vite` bumped in lockfile (2.55.0→2.60.1 via kit) | | GHSA-p9ff-h696-f583 | HIGH | Same vite bump | | GHSA-3f6h-2hrp-w5wx | MODERATE | `@sveltejs/kit` 2.55.0→2.60.1 | | GHSA-4w7w-66w2-5vf9 | MODERATE | Same vite bump | All five CVEs are addressed. The approach for each is correct — the `BODY_SIZE_LIMIT` bypass (GHSA-2crg-3p73-43xp) specifically required an explicit env var, not just a library upgrade. Good that both compose files were updated. **CI audit gate — solid** ```yaml - name: Security audit (no dev deps) run: npm audit --audit-level=high --omit=dev working-directory: frontend ``` `--omit=dev` is the right scope for a prod security gate — dev-only packages cannot be exploited in production. `--audit-level=high` means MODERATE advisories don't block merges, which is pragmatic for upstream residuals like the `cookie <0.7.0` issue. **Residual `cookie <0.7.0` (LOW) — correctly triaged** The PR description correctly explains that the only npm resolution would require `@sveltejs/kit@0.0.30` (a pre-1.0 breaking downgrade). Tracking it as a known upstream residual is the right call. LOW severity + no direct exploit path in this application's usage = acceptable risk. **BODY_SIZE_LIMIT placement** The comment in `docker-compose.prod.yml` is exactly right: ```yaml # Must be ≤ client_max_body_size in the Caddy reverse proxy to avoid 413 mismatches. BODY_SIZE_LIMIT: 50M ``` This is the threat model in the comment, not just the config value. That's what good security documentation looks like. **BODY_SIZE_LIMIT in dev compose — slightly unusual but explained** The dev compose comment says it's kept to prevent config drift, even though Vite's dev server doesn't honor it. That's a reasonable rationale — operators who spin up the full Docker stack in dev get consistent behaviour. ### No blockers No new attack surface introduced. No permissions widened. No secrets exposed. Supply chain integrity maintained via `package-lock.json` integrity hashes. ### One observation (not a blocker) The `npm audit` step runs *before* Paraglide compilation and tests. This is correct sequencing — fail fast on known vulnerabilities before spending CI minutes on build and test. No change needed, just noting the intent is sound.
Author
Owner

🏗️ Markus Keller (@mkeller) — Application Architect

Verdict: Approved

This is a pure dependency + infrastructure hygiene PR. No new packages, no new routes, no new services, no schema changes. The architecture is untouched — reviewing accordingly.

Doc update table check

Running through the mandatory doc update matrix from the architecture guidelines:

Trigger Required update Present?
New Flyway migration DB diagrams N/A — no migration
New backend package CLAUDE.md + C4 diagram N/A — no new package
New SvelteKit route CLAUDE.md + C4 diagram N/A — no new route
New Docker service l2-containers.puml + DEPLOYMENT.md N/A — no new service
New external system l1-context.puml N/A
Auth or upload flow change seq diagrams Potentially relevant (see below)
Architectural decision ADR See below

Upload flow — BODY_SIZE_LIMIT

BODY_SIZE_LIMIT=50M changes the enforced upload size limit at the SvelteKit adapter-node layer. This is a configuration correction (enforcing what was presumably the intended limit), not an architectural change. The seq-document-upload diagram does not need updating — the flow is identical, only a parameter changed. No blocker.

ADR consideration

The PR makes an architectural decision: explicitly pin BODY_SIZE_LIMIT=50M in both compose files as a security control tied to a Caddy client_max_body_size assumption. This constraint relationship (adapter-node limit ≤ Caddy limit) is now implicit in two YAML files and one comment. It is not load-bearing enough to require a full ADR, but worth a note: if the Caddy client_max_body_size ever changes, BODY_SIZE_LIMIT must change in lockstep. The comment in docker-compose.prod.yml already captures this. Sufficient.

Monolith integrity — clean

No services extracted. No new brokers. No new databases. The BODY_SIZE_LIMIT does not require a separate service boundary. Good.

Package versioning approach

@sveltejs/kit and vite semver ranges in package.json are unchanged (^2.48.5 and ^7.2.2). The actual resolved versions are in package-lock.json. This is correct npm practice — the lockfile is the source of truth for reproducible builds, the package.json constraint just defines the allowed range.

No blockers

The architecture remains unchanged. The infrastructure change is minimal and correctly scoped. The comment in docker-compose.prod.yml captures the Caddy constraint relationship. Nothing in this PR requires an ADR or diagram update.

## 🏗️ Markus Keller (@mkeller) — Application Architect **Verdict: ✅ Approved** This is a pure dependency + infrastructure hygiene PR. No new packages, no new routes, no new services, no schema changes. The architecture is untouched — reviewing accordingly. ### Doc update table check Running through the mandatory doc update matrix from the architecture guidelines: | Trigger | Required update | Present? | |---------|----------------|---------| | New Flyway migration | DB diagrams | N/A — no migration | | New backend package | CLAUDE.md + C4 diagram | N/A — no new package | | New SvelteKit route | CLAUDE.md + C4 diagram | N/A — no new route | | New Docker service | `l2-containers.puml` + DEPLOYMENT.md | N/A — no new service | | New external system | `l1-context.puml` | N/A | | Auth or upload flow change | seq diagrams | Potentially relevant (see below) | | Architectural decision | ADR | See below | **Upload flow — BODY_SIZE_LIMIT** `BODY_SIZE_LIMIT=50M` changes the enforced upload size limit at the SvelteKit adapter-node layer. This is a configuration correction (enforcing what was presumably the intended limit), not an architectural change. The seq-document-upload diagram does not need updating — the flow is identical, only a parameter changed. No blocker. **ADR consideration** The PR makes an architectural decision: explicitly pin `BODY_SIZE_LIMIT=50M` in both compose files as a security control tied to a Caddy `client_max_body_size` assumption. This constraint relationship (adapter-node limit ≤ Caddy limit) is now implicit in two YAML files and one comment. It is not load-bearing enough to require a full ADR, but worth a note: if the Caddy `client_max_body_size` ever changes, `BODY_SIZE_LIMIT` must change in lockstep. The comment in `docker-compose.prod.yml` already captures this. Sufficient. **Monolith integrity — clean** No services extracted. No new brokers. No new databases. The `BODY_SIZE_LIMIT` does not require a separate service boundary. Good. **Package versioning approach** `@sveltejs/kit` and `vite` semver ranges in `package.json` are unchanged (`^2.48.5` and `^7.2.2`). The actual resolved versions are in `package-lock.json`. This is correct npm practice — the lockfile is the source of truth for reproducible builds, the `package.json` constraint just defines the allowed range. ### No blockers The architecture remains unchanged. The infrastructure change is minimal and correctly scoped. The comment in `docker-compose.prod.yml` captures the Caddy constraint relationship. Nothing in this PR requires an ADR or diagram update.
Author
Owner

👨‍💻 Felix Brandt (@felixbrandt) — Senior Fullstack Developer

Verdict: Approved

Five changed files, zero new product code. This is a focused, surgical security fix — exactly the right scope. Let me check the mechanics.

What changed

  1. .gitea/workflows/ci.yml — adds npm audit --audit-level=high --omit=dev as a CI step
  2. docker-compose.prod.yml — adds BODY_SIZE_LIMIT: 50M with explanatory comment
  3. docker-compose.yml — adds BODY_SIZE_LIMIT: 50M with dev-context comment
  4. frontend/package.json — bumps @tiptap/* packages from 3.22.5 → 3.23.4
  5. frontend/package-lock.json — full lockfile regeneration resolving the actual CVEs

Code quality

CI step placement — correct. The npm audit runs immediately after npm ci and before any build steps. Failing fast on known CVEs before spending CPU on compilation is the right ordering.

Comments in compose files — both comments are "why" comments (explaining the threat model and the Caddy relationship), not "what" comments. This is the style I want.

TipTap alignment — bumping all three @tiptap/* packages together to 3.23.4 is correct. Mismatched TipTap versions cause silent runtime failures. Good.

No test coverage needed — this is purely infra/dependency work. The existing test suite validates that the application still functions. No behavioral change to test.

One observation (non-blocking)

package.json still shows @sveltejs/kit: "^2.48.5" in devDependencies. The lockfile resolves this to 2.60.1. The semver range ^2.48.5 permits 2.60.1, so npm ci will always install exactly 2.60.1 from the lockfile. This is correct npm behaviour — package.json is the constraint, the lockfile is the pin. No issue here, just noting it in case anyone looks at package.json and wonders why it still says 2.48.5.

Similarly vite: "^7.2.2" in devDependencies resolves to 7.3.3 in the lockfile. Same reasoning applies.

No blockers

Clean, focused, well-commented. Exactly what a security dependency bump should look like.

## 👨‍💻 Felix Brandt (@felixbrandt) — Senior Fullstack Developer **Verdict: ✅ Approved** Five changed files, zero new product code. This is a focused, surgical security fix — exactly the right scope. Let me check the mechanics. ### What changed 1. **`.gitea/workflows/ci.yml`** — adds `npm audit --audit-level=high --omit=dev` as a CI step 2. **`docker-compose.prod.yml`** — adds `BODY_SIZE_LIMIT: 50M` with explanatory comment 3. **`docker-compose.yml`** — adds `BODY_SIZE_LIMIT: 50M` with dev-context comment 4. **`frontend/package.json`** — bumps `@tiptap/*` packages from 3.22.5 → 3.23.4 5. **`frontend/package-lock.json`** — full lockfile regeneration resolving the actual CVEs ### Code quality **CI step placement** — correct. The `npm audit` runs immediately after `npm ci` and before any build steps. Failing fast on known CVEs before spending CPU on compilation is the right ordering. **Comments in compose files** — both comments are "why" comments (explaining the threat model and the Caddy relationship), not "what" comments. This is the style I want. **TipTap alignment** — bumping all three `@tiptap/*` packages together to `3.23.4` is correct. Mismatched TipTap versions cause silent runtime failures. Good. **No test coverage needed** — this is purely infra/dependency work. The existing test suite validates that the application still functions. No behavioral change to test. ### One observation (non-blocking) `package.json` still shows `@sveltejs/kit: "^2.48.5"` in devDependencies. The lockfile resolves this to `2.60.1`. The semver range `^2.48.5` permits `2.60.1`, so `npm ci` will always install exactly `2.60.1` from the lockfile. This is correct npm behaviour — `package.json` is the constraint, the lockfile is the pin. No issue here, just noting it in case anyone looks at `package.json` and wonders why it still says `2.48.5`. Similarly `vite: "^7.2.2"` in devDependencies resolves to `7.3.3` in the lockfile. Same reasoning applies. ### No blockers Clean, focused, well-commented. Exactly what a security dependency bump should look like.
Author
Owner

⚙️ Tobias Wendt (@tobiwendt) — DevOps & Platform Engineer

Verdict: Approved

Infrastructure changes are my turf. Three files I care about: both compose files and the CI workflow. Let me walk through each.

.gitea/workflows/ci.yml

- name: Security audit (no dev deps)
  run: npm audit --audit-level=high --omit=dev
  working-directory: frontend

Positioning is correct. It runs after npm ci (dependencies installed) and before the build steps. Fail-fast principle.

--audit-level=high — blocks on HIGH and CRITICAL, passes on MODERATE and LOW. Right threshold for a CI gate. The LOW cookie residual would fail an --audit-level=low gate but passes --audit-level=high. This is the correct call — a CI gate that blocks on every upstream transitive MODERATE advisory would be unmaintainable.

--omit=dev — correct scope. Dev dependencies only run in the CI sandbox, not in production. Blocking merges on dev-only CVEs adds friction without improving production security.

No exit code noisenpm audit with --audit-level=high exits 0 if the only findings are below that level. No need for || true hacks. Clean.

docker-compose.prod.yml

# Enforce upload size limit in the adapter-node layer (fixes GHSA-2crg-3p73-43xp bypass).
# Must be ≤ client_max_body_size in the Caddy reverse proxy to avoid 413 mismatches.
BODY_SIZE_LIMIT: 50M

The comment is exactly what I want operators to see — it names the CVE it fixes and documents the constraint relationship with Caddy. If someone bumps the Caddy limit in the Caddyfile, they'll know to update this too.

50M is a reasonable limit. For a document archive handling PDFs and scanned images, 50MB accommodates multi-page high-resolution scans without being excessively permissive.

docker-compose.yml (dev)

# Upload size limit for adapter-node (production target). Not enforced by Vite dev server
# but kept here to match docker-compose.prod.yml and prevent config drift.
BODY_SIZE_LIMIT: 50M

Good rationale. Vite's dev server uses its own body size handling, so this env var has no effect in dev. But keeping it here prevents the "works in dev, different behaviour in prod" class of surprises. The comment makes this explicit. I'd have made the same call.

What I'd flag on a normal infra PR (not applicable here)

  • Bind mounts for persistent data: not touched by this PR
  • Exposed internal ports: not touched
  • Unpinned image tags: not touched
  • Deprecated action versions: the upload-artifact@v3 pins are intentional (ADR-014 / #557) and correctly commented in the workflow

No blockers

All three infrastructure changes are correct, minimal, and well-commented. The CI gate will catch future HIGH/CRITICAL advisories before they sneak into main.

One nice-to-have for the future: once Renovate (#624) is running, the npm audit gate becomes the backup, not the primary detection mechanism. The two are complementary — Renovate finds upgrades proactively, the audit gate catches anything that slips through.

## ⚙️ Tobias Wendt (@tobiwendt) — DevOps & Platform Engineer **Verdict: ✅ Approved** Infrastructure changes are my turf. Three files I care about: both compose files and the CI workflow. Let me walk through each. ### `.gitea/workflows/ci.yml` ```yaml - name: Security audit (no dev deps) run: npm audit --audit-level=high --omit=dev working-directory: frontend ``` **Positioning is correct.** It runs after `npm ci` (dependencies installed) and before the build steps. Fail-fast principle. **`--audit-level=high`** — blocks on HIGH and CRITICAL, passes on MODERATE and LOW. Right threshold for a CI gate. The LOW `cookie` residual would fail an `--audit-level=low` gate but passes `--audit-level=high`. This is the correct call — a CI gate that blocks on every upstream transitive MODERATE advisory would be unmaintainable. **`--omit=dev`** — correct scope. Dev dependencies only run in the CI sandbox, not in production. Blocking merges on dev-only CVEs adds friction without improving production security. **No exit code noise** — `npm audit` with `--audit-level=high` exits 0 if the only findings are below that level. No need for `|| true` hacks. Clean. ### `docker-compose.prod.yml` ```yaml # Enforce upload size limit in the adapter-node layer (fixes GHSA-2crg-3p73-43xp bypass). # Must be ≤ client_max_body_size in the Caddy reverse proxy to avoid 413 mismatches. BODY_SIZE_LIMIT: 50M ``` The comment is exactly what I want operators to see — it names the CVE it fixes and documents the constraint relationship with Caddy. If someone bumps the Caddy limit in the Caddyfile, they'll know to update this too. **50M is a reasonable limit.** For a document archive handling PDFs and scanned images, 50MB accommodates multi-page high-resolution scans without being excessively permissive. ### `docker-compose.yml` (dev) ```yaml # Upload size limit for adapter-node (production target). Not enforced by Vite dev server # but kept here to match docker-compose.prod.yml and prevent config drift. BODY_SIZE_LIMIT: 50M ``` Good rationale. Vite's dev server uses its own body size handling, so this env var has no effect in dev. But keeping it here prevents the "works in dev, different behaviour in prod" class of surprises. The comment makes this explicit. I'd have made the same call. ### What I'd flag on a normal infra PR (not applicable here) - Bind mounts for persistent data: not touched by this PR - Exposed internal ports: not touched - Unpinned image tags: not touched - Deprecated action versions: the `upload-artifact@v3` pins are intentional (ADR-014 / #557) and correctly commented in the workflow ### No blockers All three infrastructure changes are correct, minimal, and well-commented. The CI gate will catch future HIGH/CRITICAL advisories before they sneak into main. One nice-to-have for the future: once Renovate (#624) is running, the `npm audit` gate becomes the backup, not the primary detection mechanism. The two are complementary — Renovate finds upgrades proactively, the audit gate catches anything that slips through.
Author
Owner

🧪 Sara Holt (@saraholt) — QA Engineer & Test Strategist

Verdict: Approved

No product code changes means no new test coverage is needed. What I'm reviewing is whether the PR's verification approach is sound and whether any test infrastructure was inadvertently affected.

Test impact assessment

Changed files and their test relevance:

File Test relevance
.gitea/workflows/ci.yml CI pipeline itself — reviewed below
docker-compose.prod.yml BODY_SIZE_LIMIT env var — no test needed
docker-compose.yml Same — no test needed
frontend/package.json @tiptap/* bump — affects TipTap component behaviour
frontend/package-lock.json Transitive dep bump — affects lockfile resolution

CI pipeline change

The new npm audit step is correctly positioned before any build or test steps. It will not slow down the test feedback loop — it runs in seconds, well before the heavy npm run test:coverage step.

The step does not interfere with the existing guards (Assert no banned vi.mock patterns, Assert no (upload|download)-artifact past v3, Assert no birpc teardown race). Each guard is independent.

TipTap 3.22.5 → 3.23.4 bump

The PR description says "aligns @tiptap packages to 3.23.4 to collapse a nested duplicate that npm update introduced." The existing TipTap-related vitest browser tests (per project memory, these use dispatchEvent workarounds for blur, click, and input) would catch any behavioral regression in TipTap components.

The verification table in the PR confirms npm run test (582 tests) passed. That's the right gate for a minor patch bump.

Residual risk to test suite

The vite bump (via @sveltejs/kit resolution in lockfile) from 7.3.1 → 7.3.3 could theoretically affect the Vitest test runner, which depends on Vite. The PR confirms 582 tests pass. No regression observed.

What I'd want to see additionally (non-blocking suggestions)

  1. Upload behaviour smoke testBODY_SIZE_LIMIT=50M is now enforced. There's no test asserting that a >50MB upload is rejected with an appropriate error (not a 413 mismatch or a silent hang). This would be a valuable regression test to add in a follow-up: an E2E test that attempts to upload a 51MB payload and expects a clear error response. That said, this is pre-existing test gap, not introduced by this PR.

  2. npm audit exit code in CI — The step has no continue-on-error: true, so a HIGH/CRITICAL advisory will correctly fail the build. Confirmed intentional from the step name.

No blockers

Test suite unaffected. CI gate correctly positioned. The 582-test pass confirmation in the PR description is the right verification evidence for a dependency bump of this scope.

## 🧪 Sara Holt (@saraholt) — QA Engineer & Test Strategist **Verdict: ✅ Approved** No product code changes means no new test coverage is needed. What I'm reviewing is whether the PR's verification approach is sound and whether any test infrastructure was inadvertently affected. ### Test impact assessment **Changed files and their test relevance:** | File | Test relevance | |------|---------------| | `.gitea/workflows/ci.yml` | CI pipeline itself — reviewed below | | `docker-compose.prod.yml` | `BODY_SIZE_LIMIT` env var — no test needed | | `docker-compose.yml` | Same — no test needed | | `frontend/package.json` | `@tiptap/*` bump — affects TipTap component behaviour | | `frontend/package-lock.json` | Transitive dep bump — affects lockfile resolution | ### CI pipeline change The new `npm audit` step is correctly positioned before any build or test steps. It will not slow down the test feedback loop — it runs in seconds, well before the heavy `npm run test:coverage` step. The step does not interfere with the existing guards (`Assert no banned vi.mock patterns`, `Assert no (upload|download)-artifact past v3`, `Assert no birpc teardown race`). Each guard is independent. ### TipTap 3.22.5 → 3.23.4 bump The PR description says "aligns `@tiptap` packages to 3.23.4 to collapse a nested duplicate that `npm update` introduced." The existing TipTap-related vitest browser tests (per project memory, these use `dispatchEvent` workarounds for blur, click, and input) would catch any behavioral regression in TipTap components. The verification table in the PR confirms `npm run test` (582 tests) passed. That's the right gate for a minor patch bump. ### Residual risk to test suite The `vite` bump (via `@sveltejs/kit` resolution in lockfile) from `7.3.1 → 7.3.3` could theoretically affect the Vitest test runner, which depends on Vite. The PR confirms 582 tests pass. No regression observed. ### What I'd want to see additionally (non-blocking suggestions) 1. **Upload behaviour smoke test** — `BODY_SIZE_LIMIT=50M` is now enforced. There's no test asserting that a >50MB upload is rejected with an appropriate error (not a 413 mismatch or a silent hang). This would be a valuable regression test to add in a follow-up: an E2E test that attempts to upload a 51MB payload and expects a clear error response. That said, this is pre-existing test gap, not introduced by this PR. 2. **`npm audit` exit code in CI** — The step has no `continue-on-error: true`, so a HIGH/CRITICAL advisory will correctly fail the build. Confirmed intentional from the step name. ### No blockers Test suite unaffected. CI gate correctly positioned. The 582-test pass confirmation in the PR description is the right verification evidence for a dependency bump of this scope.
Author
Owner

📋 Elicit — Requirements Engineer & Business Analyst

Verdict: Approved

This PR is responding to requirements that originated in the pre-prod audit (F-22) and are tracked in issue #458. From a requirements perspective, let me confirm the PR delivers what was asked.

Requirements coverage check

Issue #458 requirement: Clear the HIGH CVEs flagged in the pre-prod audit (F-22)

CVE Severity Cleared?
GHSA-2crg-3p73-43xp HIGH BODY_SIZE_LIMIT=50M added
GHSA-v2wj-q39q-566r HIGH Vite updated
GHSA-p9ff-h696-f583 HIGH Vite updated
GHSA-3f6h-2hrp-w5wx MODERATE @sveltejs/kit updated
GHSA-4w7w-66w2-5vf9 MODERATE Vite updated

The PR explicitly closes #458 and documents each CVE in a table with its package, severity, and description. This is high-quality issue closure — traceable, verifiable, complete.

Acceptance criteria implied by the issue:

  • The audit flagged HIGH CVEs must be resolved:
  • Uploads must not be silently rejected due to the body-size limit change: (50M limit set explicitly, matching prior assumed behaviour)
  • The fix must not break existing functionality: (verified via lint, check, 582 tests, build)

Residual tracking

The PR correctly documents the cookie <0.7.0 residual with an explanation of why it cannot be fixed without a breaking downgrade. It commits to tracking it in a completion comment on #458. This is responsible requirements hygiene — the residual is acknowledged, not swept under the rug.

Follow-up issue #624 (Renovate configuration)

The PR description references #624 as the follow-up for automated dependency updates. This is the right next step: the audit gate added here catches what slips through, Renovate proactively raises PRs before CVEs land. The two requirements (#458 reactive fix, #624 proactive automation) are correctly sequenced.

Scope discipline

The PR does exactly what it says: security dependency bump + body size limit + CI gate. No scope creep. No opportunistic feature work. The TipTap version alignment is a legitimate scope inclusion (it was caused by the same npm update run and fixing it here prevents a future duplicate-package confusion).

No blockers from a requirements perspective

Requirements from #458 are fully addressed. Residuals are documented and tracked. Follow-up work is scoped into #624. This is a clean, complete delivery of the issue's intent.

## 📋 Elicit — Requirements Engineer & Business Analyst **Verdict: ✅ Approved** This PR is responding to requirements that originated in the pre-prod audit (F-22) and are tracked in issue #458. From a requirements perspective, let me confirm the PR delivers what was asked. ### Requirements coverage check **Issue #458 requirement: Clear the HIGH CVEs flagged in the pre-prod audit (F-22)** | CVE | Severity | Cleared? | |-----|----------|---------| | GHSA-2crg-3p73-43xp | HIGH | ✅ `BODY_SIZE_LIMIT=50M` added | | GHSA-v2wj-q39q-566r | HIGH | ✅ Vite updated | | GHSA-p9ff-h696-f583 | HIGH | ✅ Vite updated | | GHSA-3f6h-2hrp-w5wx | MODERATE | ✅ `@sveltejs/kit` updated | | GHSA-4w7w-66w2-5vf9 | MODERATE | ✅ Vite updated | The PR explicitly closes #458 and documents each CVE in a table with its package, severity, and description. This is high-quality issue closure — traceable, verifiable, complete. **Acceptance criteria implied by the issue:** - The audit flagged HIGH CVEs must be resolved: ✅ - Uploads must not be silently rejected due to the body-size limit change: ✅ (50M limit set explicitly, matching prior assumed behaviour) - The fix must not break existing functionality: ✅ (verified via lint, check, 582 tests, build) ### Residual tracking The PR correctly documents the `cookie <0.7.0` residual with an explanation of why it cannot be fixed without a breaking downgrade. It commits to tracking it in a completion comment on #458. This is responsible requirements hygiene — the residual is acknowledged, not swept under the rug. ### Follow-up issue #624 (Renovate configuration) The PR description references #624 as the follow-up for automated dependency updates. This is the right next step: the audit gate added here catches what slips through, Renovate proactively raises PRs before CVEs land. The two requirements (#458 reactive fix, #624 proactive automation) are correctly sequenced. ### Scope discipline The PR does exactly what it says: security dependency bump + body size limit + CI gate. No scope creep. No opportunistic feature work. The TipTap version alignment is a legitimate scope inclusion (it was caused by the same `npm update` run and fixing it here prevents a future duplicate-package confusion). ### No blockers from a requirements perspective Requirements from #458 are fully addressed. Residuals are documented and tracked. Follow-up work is scoped into #624. This is a clean, complete delivery of the issue's intent.
Author
Owner

🎨 Leonie Voss (@leonievoss) — UI/UX Design Lead & Accessibility Strategist

Verdict: Approved

No UI components touched. No Svelte files changed. No CSS or design token changes. No route additions. This is a pure dependency + infrastructure PR — I have nothing to flag from a design or accessibility perspective.

What I verified

Changed files that could affect UI:

File UI impact
frontend/package.json @tiptap/* 3.22.5→3.23.4 — see below
frontend/package-lock.json Lockfile only
docker-compose*.yml No UI impact
.gitea/workflows/ci.yml No UI impact

TipTap bump (3.22.5 → 3.23.4)

TipTap powers the rich-text comment editor used in document threads. A patch bump from 3.22.5 to 3.23.4 is unlikely to change visual behaviour, keyboard navigation, or accessibility attributes. The existing editor components are unchanged. Any visual regression would be caught by the existing vitest browser component tests.

The PR description confirms npm run build passed and 582 tests passed. TipTap's own changelog for 3.22.x → 3.23.x covers internal fixes and doesn't touch the accessibility contract of contenteditable elements.

BODY_SIZE_LIMIT=50M

Upload interactions are unchanged from the user's perspective — the limit was presumably intended to be 50M before this fix. If anything, this PR prevents the frustrating user experience of uploads being silently truncated or rejected without a clear error message (the original CVE GHSA-2crg-3p73-43xp was a bypass of the limit — an attacker could exceed it). Post-fix, the limit is enforced reliably, which is better for all users including our senior audience who might upload large scanned document archives.

No blockers

Zero UI changes. Accessibility posture is unchanged. Brand and design system are untouched.

## 🎨 Leonie Voss (@leonievoss) — UI/UX Design Lead & Accessibility Strategist **Verdict: ✅ Approved** No UI components touched. No Svelte files changed. No CSS or design token changes. No route additions. This is a pure dependency + infrastructure PR — I have nothing to flag from a design or accessibility perspective. ### What I verified **Changed files that could affect UI:** | File | UI impact | |------|-----------| | `frontend/package.json` | `@tiptap/*` 3.22.5→3.23.4 — see below | | `frontend/package-lock.json` | Lockfile only | | `docker-compose*.yml` | No UI impact | | `.gitea/workflows/ci.yml` | No UI impact | **TipTap bump (3.22.5 → 3.23.4)** TipTap powers the rich-text comment editor used in document threads. A patch bump from 3.22.5 to 3.23.4 is unlikely to change visual behaviour, keyboard navigation, or accessibility attributes. The existing editor components are unchanged. Any visual regression would be caught by the existing vitest browser component tests. The PR description confirms `npm run build` passed and 582 tests passed. TipTap's own changelog for 3.22.x → 3.23.x covers internal fixes and doesn't touch the accessibility contract of contenteditable elements. **BODY_SIZE_LIMIT=50M** Upload interactions are unchanged from the user's perspective — the limit was presumably intended to be 50M before this fix. If anything, this PR prevents the frustrating user experience of uploads being silently truncated or rejected without a clear error message (the original CVE GHSA-2crg-3p73-43xp was a *bypass* of the limit — an attacker could exceed it). Post-fix, the limit is enforced reliably, which is better for all users including our senior audience who might upload large scanned document archives. ### No blockers Zero UI changes. Accessibility posture is unchanged. Brand and design system are untouched.
marcel added 1 commit 2026-05-19 14:56:38 +02:00
chore(deps): raise package.json lower bounds to patched versions
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 39s
CI / OCR Service Tests (pull_request) Successful in 19s
CI / Backend Unit Tests (pull_request) Successful in 3m30s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 18s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m1s
65cae4a5e8
Bumps declared semver ranges to the patched minimums so a fresh
npm install (without the lockfile) cannot resolve to a vulnerable
version:
  @sveltejs/adapter-node  ^5.4.0  →  ^5.5.4
  @sveltejs/kit           ^2.48.5 →  ^2.60.1
  vite                    ^7.2.2  →  ^7.3.3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel added 1 commit 2026-05-19 15:01:19 +02:00
fix(patches): regenerate @vitest/browser-playwright patch for 4.1.6
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m56s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m19s
CI / fail2ban Regex (pull_request) Successful in 41s
CI / Semgrep Security Scan (pull_request) Successful in 20s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m1s
a0339a5526
The backport of vitest PR #10267 (unroute-before-register guard that
prevents orphan routes causing birpc teardown crashes) was made against
4.1.0. The dep bump moved the package to 4.1.6; patch-package refused to
apply the stale file. Regenerated against the installed 4.1.6 — the fix
is identical, adapted for the renamed idPreficates → idPredicates typo
that upstream corrected in this version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel added 1 commit 2026-05-19 16:17:24 +02:00
chore(ci): bump Playwright Docker image to v1.60.0-noble
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 5m0s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m24s
CI / fail2ban Regex (pull_request) Successful in 44s
CI / Semgrep Security Scan (pull_request) Successful in 21s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m1s
CI / Unit & Component Tests (push) Successful in 3m34s
CI / OCR Service Tests (push) Successful in 20s
CI / Backend Unit Tests (push) Successful in 3m26s
CI / fail2ban Regex (push) Successful in 44s
CI / Semgrep Security Scan (push) Successful in 20s
CI / Compose Bucket Idempotency (push) Successful in 1m2s
1bec7dd17e
The dep update resolved @playwright/test and playwright to 1.60.0.
The CI container was pinned to v1.58.2-noble which lacks the matching
browser binary, causing the browser project to fail to launch and
coverage thresholds to hit 0%.

Also raises @playwright/test and playwright lower bounds in package.json
to ^1.60.0 to keep the declared range consistent with the lockfile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel merged commit 1bec7dd17e into main 2026-05-19 16:29:44 +02:00
marcel deleted branch feat/issue-458-security-dep-bump 2026-05-19 16:29:44 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#625