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>
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-ormto^0.45.2directly inapps/web/package.jsonandpackages/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
overridesinpnpm-workspace.yaml(pnpm 11 moved this out ofpackage.json'spnpm.overrides, which is silently ignored — that field was removed) forcingesbuild: ">=0.24.3"andpostcss: ">=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 audit → No 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).