Skip to content

fix(api): rate-limit authenticated document-admin routes (audit H2, partial)#727

Merged
BigSimmo merged 8 commits into
mainfrom
claude/api-admin-rate-limits
Jul 17, 2026
Merged

fix(api): rate-limit authenticated document-admin routes (audit H2, partial)#727
BigSimmo merged 8 commits into
mainfrom
claude/api-admin-rate-limits

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Wave-H finding H2 (S6) — the authenticated document-admin routes had no rate-limit bucket, so a compromised or abusive authenticated client could hammer bulk metadata edits, label writes, and table-fact review with no per-owner ceiling.

  • Adds a document_admin bucket (60/min per owner) in src/lib/api-rate-limit.ts.
  • Consults it before any table access in documents/bulk (POST), documents/[id]/labels (POST/PATCH/DELETE), and documents/[id]/table-facts (GET/PATCH).
  • Exhaustion returns the shared 429 + Retry-After envelope via rateLimitJsonResponse.
  • Uses allowInMemoryFallbackOnUnavailable: true (matching the read/registry buckets), so a durable-limiter outage degrades these authenticated routes to per-instance limiting rather than failing them closed.

Scope note: H2's ingestion-quality and eval-cases routes (authenticated read/eval dashboards) are deferred to a follow-up, recorded in docs/audit-remediation-plan-2026-07-14.md.

Verification

  • New tests/document-admin-rate-limit.test.ts — 429 + Retry-After when the durable limiter reports the bucket exhausted, and no table access occurs.
  • 6 affected route suites pass (159): document-admin-rate-limit, document-mutation-routes, public-access-deep, api-route-coverage, private-access-routes, api-validation-contract. api-rate-limit-fallback passes (11).
  • tsc, eslint, prettier clean on every changed file; rebased cleanly onto latest main.
  • npm run verify:pr-local not run: the repo-wide typecheck fails only on pre-existing missing optional test dev deps (@testing-library/*, @axe-core/playwright) absent in this container and unrelated to this change.
  • npm run verify:ui — N/A; no UI, routing, or styling code changed (only src/app/api/** + src/lib).

Risk and rollout

  • Risk: low; adds a per-owner rate-limit ceiling to existing authenticated owner-scoped admin routes. No change to auth, owner scoping, or data semantics; legitimate users stay well under 60/min, and a limiter outage degrades to per-instance limiting rather than open access.
  • Rollback: revert this PR's single commit. No migration, schema, or data change — the document_admin bucket is application config only, so reverting fully removes the limiter with no cleanup or backfill.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

Summary by CodeRabbit

  • New Features

    • Added rate limiting to document administration actions, including label updates, table-fact access, and bulk document operations.
    • Requests exceeding the limit now receive a retryable response with rate-limit details.
  • Tests

    • Added coverage verifying rate-limited requests return HTTP 429 responses before database access.
    • Updated document mutation tests to support rate-limit checks.

@supabase

supabase Bot commented Jul 17, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: adb467ee-fdcc-43de-9ea4-78c5e7483378

📥 Commits

Reviewing files that changed from the base of the PR and between 8adba31 and 282eb86.

📒 Files selected for processing (1)
  • src/lib/api-rate-limit.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/api-rate-limit.ts

📝 Walkthrough

Walkthrough

Document administration routes now use a shared document_admin API rate-limit bucket. Limited requests return early 429 responses, while tests cover exhausted and available limiter paths.

Changes

Document administration rate limiting

Layer / File(s) Summary
Rate-limit bucket configuration
src/lib/api-rate-limit.ts
Adds the document_admin bucket with a limit of 60 requests per 60 seconds.
Route enforcement
src/app/api/documents/[id]/labels/route.ts, src/app/api/documents/[id]/table-facts/route.ts, src/app/api/documents/bulk/route.ts
Adds rate-limit checks to label, table-fact, and bulk document handlers before document operations. Limited requests return the shared JSON rate-limit response.
Rate-limit validation
tests/document-admin-rate-limit.test.ts, tests/document-mutation-routes.test.ts
Tests 429 responses, retry headers, skipped database access, and successful mocked limiter calls.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AuthenticatedRoute
  participant consumeApiRateLimit
  participant SupabaseRPC
  participant rateLimitJsonResponse
  AuthenticatedRoute->>consumeApiRateLimit: Check document_admin bucket
  consumeApiRateLimit->>SupabaseRPC: Call consume_api_rate_limit
  SupabaseRPC-->>consumeApiRateLimit: Return limited status and retry data
  consumeApiRateLimit-->>AuthenticatedRoute: Return rate-limit result
  AuthenticatedRoute->>rateLimitJsonResponse: Build 429 response when limited
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding rate limits to authenticated document-admin routes.
Description check ✅ Passed The description matches the template well, covering summary, verification, risk, governance, and notes with only minor omissions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/api-admin-rate-limits

Comment @coderabbitai help to get the list of available commands.

…artial)

Wave-H finding H2 (S6): the authenticated document-admin routes had no rate-limit
bucket, so a compromised or abusive authenticated client could hammer bulk metadata
edits, label writes, and table-fact review with no per-owner ceiling.

Adds a `document_admin` bucket (60/min per owner) and consults it — before any table
access — in the document-admin routes:
- `documents/bulk` (POST)
- `documents/[id]/labels` (POST/PATCH/DELETE)
- `documents/[id]/table-facts` (GET/PATCH)

Exhaustion returns the shared 429 + `Retry-After` envelope via `rateLimitJsonResponse`.
The bucket uses `allowInMemoryFallbackOnUnavailable: true` (matching the read/registry
buckets), so a durable-limiter outage degrades these authenticated routes to
per-instance limiting rather than failing them closed.

Tests: new tests/document-admin-rate-limit.test.ts proves a 429 + Retry-After when the
durable limiter reports the bucket exhausted (and no table access occurs). Added a
not-limited `rpc` stub to tests/document-mutation-routes.test.ts so its functional
cases exercise the happy path through the new limiter check.

Verification: the 6 affected route suites pass (159), api-rate-limit-fallback passes
(11); tsc/eslint/prettier clean on all changed files. (Repo-wide verify:cheap
typecheck fails only on pre-existing missing optional test dev deps, unrelated.)

Scope note: H2 also lists the ingestion-quality and eval-cases routes; those are
authenticated read/eval dashboards and are deferred to a follow-up (separate bucket
naming + their own test-mock updates), recorded in docs/audit-remediation-plan-2026-07-14.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zbpyexer9cLgxhrB61RCU
@BigSimmo
BigSimmo force-pushed the claude/api-admin-rate-limits branch from 0bf5239 to 8adba31 Compare July 17, 2026 17:27
@BigSimmo
BigSimmo marked this pull request as ready for review July 17, 2026 17:28
@BigSimmo
BigSimmo enabled auto-merge July 17, 2026 17:28
BigSimmo and others added 6 commits July 18, 2026 01:54
…mit buckets)

Resolves the expected conflict in src/lib/api-rate-limit.ts after #731 (the H2
remainder) merged first: #731 added the `ingestion_admin` bucket and this PR adds
`document_admin`. Keep-both resolution — both entries retained in the
ApiRateLimitBucket union and apiRateLimitDefaults. No behaviour change; the two
buckets are independent.

Verified: document-admin-rate-limit + api-rate-limit-fallback + document-mutation-routes
pass (19); tsc/eslint/prettier clean on api-rate-limit.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zbpyexer9cLgxhrB61RCU
@BigSimmo
BigSimmo merged commit b04c95f into main Jul 17, 2026
15 checks passed
@BigSimmo
BigSimmo deleted the claude/api-admin-rate-limits branch July 17, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants