Skip to content

fix(api): unify auth 401 envelope and cap document list offset (audit H1, H3)#723

Merged
BigSimmo merged 6 commits into
mainfrom
claude/api-contract-hardening-wave-h
Jul 17, 2026
Merged

fix(api): unify auth 401 envelope and cap document list offset (audit H1, H3)#723
BigSimmo merged 6 commits into
mainfrom
claude/api-contract-hardening-wave-h

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Two self-contained API-contract-hygiene fixes from the 2026-07-14 audit (Wave H), offline-verifiable with no schema, migration, or provider dependency.

  • H1 (S4/S5)unauthorizedResponse returned a bare { error } 401 without the stable code/message every other API failure carries. It now routes through the shared jsonError helper, so a 401 body is { error, message, code: "authentication_required" } (and inherits Cache-Control: private, no-store). jsonError gains an optional { log } flag; the auth helper passes log: false so a routine unauthenticated request is not logged as a server-side error.
  • H3 (S7) — cap the document list offset at 10_000 (matching the jobs list) instead of 1_000_000, so a deep-offset request cannot force PostgREST to skip through a million rows as a slow-query/DoS lever.

Deferred from Wave H (reasons in docs/audit-remediation-plan-2026-07-14.md): H2 (shipped separately as #727), H4 (upload Content-Length admission — needs an admission-API change), H5 (authed-summarize public scope — live-eval-gated).

Verification

  • Focused vitest (http-error-response, api-validation-contract, upload-admission) — pass, including the new H1/H3 tests.
  • Auth/route-adjacent suites (private-access-routes, document-mutation-routes, api-route-coverage, private-rag-access) — pass. No test asserts the exact 401 body, so adding message/code breaks nothing.
  • tsc, eslint, prettier clean on every changed file; rebased cleanly onto latest main (jsonError now carries both the new Cache-Control header and the log option).
  • npm run verify:pr-local not run: the repo-wide typecheck fails only on pre-existing missing optional test dev deps 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; API request/response-contract hardening only. The 401 change adds fields (and a cache header) without changing the status code or the generic message; the offset cap only bounds pagination depth. No auth, data, or schema semantics change.
  • Rollback: revert this PR's single commit. No migration or data change, so reverting fully restores the prior 401 shape and the prior offset bound with no cleanup.

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

  • Bug Fixes

    • Limited document pagination offsets to 10,000 for more predictable list requests.
    • Standardized unauthenticated responses with a consistent 401 error format.
    • Prevented routine authentication failures from being logged unnecessarily.
    • Ensured unauthorized responses are marked private and non-cacheable.
  • Tests

    • Expanded coverage for pagination limits, boundary values, error responses, logging behavior, and authentication handling.

@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: 9a2f1304-fe73-431c-a405-7a3c02bbc17a

📥 Commits

Reviewing files that changed from the base of the PR and between 6a399f9 and 7aff93f.

📒 Files selected for processing (2)
  • tests/api-validation-contract.test.ts
  • tests/http-error-response.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/http-error-response.test.ts

📝 Walkthrough

Walkthrough

The documents API now caps query offsets at 10,000. Shared HTTP errors support optional logging suppression, and unauthorized responses use the standardized authentication error envelope with HTTP 401 and private no-store caching.

Changes

Document pagination limits

Layer / File(s) Summary
Clamp document offsets and validate the range
src/app/api/documents/route.ts, tests/api-validation-contract.test.ts
The query schema caps offset at 10,000, while contract tests cover below-cap, boundary, and over-cap values and their Supabase range windows.

Authentication error handling

Layer / File(s) Summary
Standardize silent unauthorized responses
src/lib/http.ts, src/lib/supabase/auth.ts, tests/http-error-response.test.ts
jsonError accepts an optional logging control, and unauthorizedResponse returns the shared authentication error envelope without logging; tests verify logging, status, payload, and cache headers.

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

Possibly related PRs

  • BigSimmo/Database#698: Both changes modify GET /api/documents pagination handling and PostgREST range behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: auth 401 envelope hardening and document offset capping.
Description check ✅ Passed The description matches the template with Summary, Verification, Risk and rollout, Clinical Governance Preflight, and Notes, with only minor verification gaps.
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-contract-hardening-wave-h

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

@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 ↗︎.

… H1, H3)

Two self-contained API-contract-hygiene fixes from the 2026-07-14 audit
(Wave H), each offline-verifiable with no schema or provider dependency.

H1 (S4/S5) — unify the JSON error envelope for auth failures. `unauthorizedResponse`
returned a bare `{ error }` 401 without the stable `code`/`message` every other API
failure carries. It now routes through the shared `jsonError` helper, so a 401 body
is `{ error, message, code: "authentication_required" }`. `jsonError` gains an
optional `{ log }` flag (default true); the auth helper passes `log: false` so a
routine unauthenticated request is not recorded as a server-side error.

H3 (S7) — cap the document list `offset` at 10_000 (matching the jobs list) instead
of 1_000_000, so a deep-offset request cannot force PostgREST to skip through a
million rows as a slow-query/DoS lever. `queryInteger` clamps, so oversized offsets
are pinned to the cap.

Tests: extend tests/http-error-response.test.ts (auth envelope shape + `log` opt-out
suppresses the error log without changing the payload) and
tests/api-validation-contract.test.ts (offset above 10k clamps to 10k, range pinned).

Verification: focused vitest for the touched surfaces (29) plus the auth/route-adjacent
suites (155) pass; typecheck + eslint + prettier clean on all changed files. (The
repo-wide `verify:cheap` 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.)

Deferred from Wave H with reasons: H2 (admin-route rate limits — ripples into many
test mocks that would 503 without a rate-limit row), H4 (upload Content-Length
admission — undici/proxy requests legitimately omit Content-Length, so a hard 411
would break chunked uploads and the existing tests), H5 (authed-summarize public
scope — retrieval path, gated on the live golden eval).

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-contract-hardening-wave-h branch from 75cbd53 to 6a399f9 Compare July 17, 2026 17:27
@BigSimmo
BigSimmo marked this pull request as ready for review July 17, 2026 17:29
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Note

Unit test generation is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ Unit tests committed locally. Commit: 1ad80196576e5ce9432475298e29bdcb3d7ef57c

CodeRabbit's auto-generated unit tests (commit 1ad8019) added valuable
edge-case coverage for the H3 offset cap and the H1 jsonError `log` flag, but
tests/api-validation-contract.test.ts was not prettier-formatted, failing the
CI format:check gate. Formatting only — no test logic changed; all 35 tests in
the two touched files pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zbpyexer9cLgxhrB61RCU
@BigSimmo
BigSimmo enabled auto-merge July 17, 2026 17:40
@BigSimmo
BigSimmo merged commit a6ac8c0 into main Jul 17, 2026
15 checks passed
@BigSimmo
BigSimmo deleted the claude/api-contract-hardening-wave-h branch July 17, 2026 18:11
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