Files
Epicure/SECURITY_AUDIT_2026-07-20_followup.md
T
Arnaud 7626f1b496 fix: close TOCTOU race + missing checks in tier limits, add AI rate limits, patch CVEs (v0.60.0)
Security audit fixes (see SECURITY_AUDIT.md):
- lib/tiers.ts: checkAndIncrementTierLimit's recipe/storage branches did a
  live count/sum check with no lock tying it to the later write, so two
  concurrent requests near the cap could both pass and both write past the
  limit. Added checkTierLimitInTransaction — holds a per-user Postgres
  advisory lock for the duration of the caller's transaction, so the check
  and the write are atomic together. Applied to every recipe/storage write
  path (recipes create/update, fork, rate, avatar, and 7 AI recipe-creation
  routes).
- Adversarial re-verification of that fix caught a bigger gap in the same
  area: four AI routes (photo import, idea generation, batch-cook,
  translate-to-new-draft) had no recipe-limit check at all. Fixed.
- Added missing per-user rate limits to 5 AI endpoints (adapt, drinks,
  pairings, translate, variations) that had none, unlike their siblings.
- search page now checks for a session server-side, matching every other
  page under (app)/ — defense in depth; the underlying API was already
  public by design and proxy.ts already blocked unauthenticated requests.
- admin/settings route now uses the shared requireAdmin instead of a local
  duplicate.
- Documented two admin support-ticket endpoints missing from the OpenAPI
  spec; verified full route/OpenAPI parity otherwise.
- Bumped drizzle-orm to fix a SQL-identifier-escaping CVE, and overrode two
  transitive deps (esbuild, postcss) with known CVEs. pnpm audit is clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 22:21:51 +02:00

3.3 KiB

Follow-up: dependency CVEs + OpenAPI coverage

Companion to SECURITY_AUDIT.md (same day, same session). Two additional checks requested after the tier-limit/rate-limit fixes shipped.

1. Dependency vulnerabilities (pnpm audit)

Before:

Severity Package Issue Fixed in
High drizzle-orm@0.44.7 SQL injection via improperly escaped SQL identifiers (GHSA-gpj5-g38j-94v9) >=0.45.2
Moderate esbuild@0.18.20 (transitive, via drizzle-kit@esbuild-kit/esm-loader, dev-only) esbuild dev server accepts requests from any origin (GHSA-67mh-4wv8-2f99) >=0.24.3
Moderate postcss@8.4.31 (transitive, via next@16.2.9) XSS via unescaped </style> in CSS stringify output (GHSA-qx2v-qp2m-jg93) >=8.5.10

Fix:

  • Bumped drizzle-orm to ^0.45.2 directly in apps/web/package.json and packages/db/package.json (it was a top-level dependency in both, not just transitive — straightforward version bump, no breaking API changes hit typecheck/tests/build).
  • Added overrides in pnpm-workspace.yaml (pnpm 11 moved this out of package.json's pnpm.overrides, which is silently ignored — that field was removed) forcing esbuild: ">=0.24.3" and postcss: ">=8.5.10" across the whole dependency tree, collapsing the vulnerable transitive copies into already-present patched versions (both packages already had a patched instance installed for other consumers — this just eliminates the vulnerable second copy rather than introducing a new major version).

After: pnpm auditNo known vulnerabilities found.

Verification: full re-run of pnpm typecheck, pnpm exec vitest run (191/193 passing — the 2 failures are the same pre-existing, unrelated baseline failures noted throughout this session), and pnpm build (production build succeeds, including the Tailwind/PostCSS pipeline under the new override) — all clean after the bump + overrides.

2. OpenAPI documentation coverage

Cross-referenced every route.ts under apps/web/app/api/v1/** (which HTTP methods each file exports) against every registry.registerPath({ method, path, ... }) call in apps/web/lib/openapi.ts.

Before: two endpoints existed with zero OpenAPI documentation:

  • GET /api/v1/admin/support — list all support tickets (admin only)
  • PATCH /api/v1/admin/support/{id} — update a ticket's status, or retry its Gitea issue creation (admin only)

Everything else (109 other route/method pairs) was already documented, and there were no stale entries (documented paths with no matching route file).

Fix: added AdminSupportTicketRef / UpdateAdminSupportTicketRef schemas and both registerPath entries to lib/openapi.ts, next to the existing admin/reports entry, matching the response/error shapes actually returned by the two route handlers.

After: re-ran the cross-reference — full parity. The only "missing" entry is GET /api/v1/openapi.json itself, which doesn't need to self-document.

Verification: pnpm typecheck and pnpm exec vitest run clean after the addition (the new refs slot into the existing Zod-based OpenAPI registry with no schema conflicts).