← all branches

feat/one-api

blocked
82bb5b9 · incrementalpre-PRreviewed 2026-08-12 01:48 UTC5H · 5M · 4L · 2I
The branch
Purpose
Consolidate public API surface (BAT-280/BAT-281) onto single authenticated, RLS-enforced, rate-limited v1 mount with full security hardening
Goal
Ship the one-api surface with all security invariants mechanically enforced and documented
Sub-goals
  • SG-1: Security hardening rounds 1-9 — close every review panel finding
  • SG-2: Rate limiting — IP ceiling + per-credential budgets on v1 mount
  • SG-3: Bill-file download — separate from ts-rest dispatch (non-JSON response)
  • SG-4: Release safety — destructive_migration flag + auto-rollback suppression for DROP migrations
  • SG-5: CI gates — route-inventory freshness + admin-required check in pr-checks.yml
The changes (whole branch)
What
Rounds 8-9: destructive_migration workflow flag, IP ceiling honest-scope, bill-file download path, profile shell catch tests, rate-limit-bucket ceilingApplies/rateLimitBucket refactor, auth-token service test (placeholder), two CI checks moved to self-hosted runner
Why
Round-7 review identified auto-rollback job would re-promote old code against new schema on postflight failure — unrecoverable. Rounds 8-9 close that path.
Areas
.claude/rules+542.github/workflows+611apps/platform/src/api+7312apps/platform/src/app/api/v1+11969domains/core/src/profile+1040domains/utility+376packages/api/src/types+51packages/database+11scripts/one-api+182
Blast
276 files +223636/-10798 across full branch; incremental (rounds 8-9): 21 files +401/-118
auth-bypass-risk: bill-file not rate-limited at mount double-counting: ceilingApplies+rateLimitBucket increment same key no-op-test: auth-token.service.test.ts placeholder placeholder-impl: authTokenHandler returns PLACEHOLDER
github-ci· GitHub API unavailable (runner not authenticated)coderabbit· No .coderabbit.yaml found

Findings · 18

correctness4

high

ceilingApplies + rateLimitBucket both increment same key — ceiling halved

apps/platform/src/api/utils/public-v1-rate-limit-bucket.ts:4

Both call checkRateLimit(ip-ceiling:{ip}) — double-incrementing same bucket per request, halving the effective limit

medium

migration-pipeline.md: duplicate 'Single path per env' bullet

.claude/rules/migration-pipeline.md:38

New block insertion left a duplicate trailing bullet in the SSOT rule file

medium

one-api.md: duplicate '## Remaining risks' heading

.claude/rules/one-api.md:43

Two headings with same name break navigation and anchor links; remove the stub

low

authTokenHandler returns PLACEHOLDER token unconditionally

apps/platform/src/api/handlers/public-v1/auth-token.handler.ts:14

Reachable on staging/preview — any authenticated caller receives literal PLACEHOLDER string

security5

high

handleBillFileDownload bypasses mount auth gate and rate limiting

apps/platform/src/app/api/v1/[...ts-rest]/route.ts:89

GET short-circuits before handleRequest; auth deferred to readBillFile internal wrapper — fragile; no rate limiting applied

medium

IDOR risk: verify ctx.org.id is not request-parameter-influenced

apps/platform/src/api/utils/public-v1-file-read.ts:9

Guard is valid only if org resolved solely from authenticated JWT/API key, not request headers

medium

Dynamic import hides module load failures — auth-less 500 path

apps/platform/src/app/api/v1/[...ts-rest]/route.ts:100

Import failure throws at request time with no auth check performed; prefer static import

low

destructive_migration boolean lacks a friction gate

.github/workflows/pr-checks.yml:6

Single checkbox suppresses auto-rollback; consider a required confirmation string

info

Rate limit key prefix change orphans in-flight counters on deploy

apps/platform/src/api/utils/public-v1-rate-limit-bucket.ts:4

IPs near limit under old ip: key get fresh window on deploy — one-time transient relaxation

conventions2

low

providerId added to UtilityContract without visible type-check update

domains/utility/src/utility-contract/utility-contract.type.ts:14

AssertEqual in type-check.ts must also be updated per ADR-018

low

file_url added to PublicApiBill without visible Zod schema update

packages/api/src/types/public/bill.public-api-types.ts:26

satisfies alignment with Zod schema in packages/api/src/schemas/ must be confirmed

tests4

critical

auth-token service test is a no-op placeholder

apps/platform/src/api/handlers/public-v1/__tests__/auth-token.service.test.ts:5

expect(true).toBe(true) — zero coverage on security-critical token exchange path

critical

handleBillFileDownload has zero test coverage

apps/platform/src/app/api/v1/[...ts-rest]/route.ts

New public endpoint with auth implications, URL parsing, file access — entirely untested

high

Profile shell catch tests only verify result.ok===false, not error shape

domains/core/src/profile/__tests__/profile.shells.catch.test.ts:20

Missing _tag/statusCode assertions — callers branch on _tag; wrong tag passes test but breaks downstream

high

rateLimitBucket tests do not cover ceiling-blocked path or return values

apps/platform/src/api/utils/__tests__/public-v1-rate-limit-bucket.test.ts

Denied path (allowed:false) is untested; boundary {allowed:true,remaining:0} also missing

improvement3

medium

self-hosted runner for pure Node.js CI may stall PRs if dev-ops box offline

.github/workflows/pr-checks.yml:120

route-inventory-fresh and check-admin-required may not need the self-hosted runner

low

handleBillFileDownload duplicates path extraction already done by BILL_FILE_ROUTE

apps/platform/src/app/api/v1/[...ts-rest]/route.ts

Use named capture group in BILL_FILE_ROUTE to avoid dual regex application

info

Dynamic import should document why it is not a static import

apps/platform/src/app/api/v1/[...ts-rest]/route.ts

Without a comment, future readers will refactor to static import and reintroduce avoided problem

History · 47 commits

  1. 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48current
  2. 90aa3d5needs attentionincremental1H · 5M · 3L2026-08-11 19:37
  3. 29d19a0needs attentionincremental1H · 5M · 9L2026-08-11 17:41
  4. 9bd8a0cneeds attentionfull0H · 5M · 9L2026-08-11 02:14
  5. 62ec3f7needs attentionincremental2H · 5M · 6L2026-08-10 22:51
  6. f93bca9needs attentionincremental2H · 5M · 8L2026-08-10 17:51
  7. 052db6fneeds attentionincremental1H · 3M · 4L2026-08-09 21:13
  8. 45699caneeds attentionincremental0H · 7M · 11L2026-08-09 17:44
  9. b843d8aneeds attentionincremental1H · 7M · 9L2026-08-09 04:05
  10. e1757b8needs attentionincremental0H · 3M · 6L2026-08-05 02:11
  11. 7a762faneeds attentionincremental2H · 5M · 5L2026-08-05 01:25
  12. 3300a60needs attentionincremental2H · 4M · 7L2026-08-04 19:06
  13. 0c8a7f5needs attentionincremental0H · 4M · 9L2026-08-04 18:15
  14. 345f42eneeds attentionincremental2H · 6M · 9L2026-08-04 17:28
  15. 8338a9aneeds attentionincremental5H · 14M · 14L2026-08-04 00:33
  16. 41be4c3needs attentionincremental0H · 5M · 7L2026-08-03 23:49
  17. 5ed593dneeds attentionincremental1H · 6M · 6L2026-08-03 21:32
  18. b333e25needs attentionincremental4H · 9M · 8L2026-08-03 21:00
  19. 5642cccneeds attentionincremental2H · 3M · 2L2026-08-03 20:17
  20. 73b0b39needs attentionincremental3H · 10M · 13L2026-07-31 18:29
  21. b19852eneeds attentionincremental0H · 1M · 5L2026-07-29 05:04
  22. 3845205needs attentionincremental3H · 6M · 4L2026-07-29 04:47
  23. eb8eb50needs attentionincremental0H · 1M · 2L2026-07-29 03:03
  24. f4720a3needs attentionincremental6H · 8M · 7L2026-07-29 02:54
  25. f8d341ablockedincremental2H · 2M · 5L2026-07-29 00:00
  26. a7f1a64needs attentionincremental2H · 8M · 8L2026-07-28 18:41
  27. 738b60bblockedincremental3H · 6M · 5L2026-07-28 00:46
  28. 2c248b6needs attentionincremental8H · 12M · 8L2026-07-27 23:23
  29. 1346cc0needs attentionincremental2H · 8M · 6L2026-07-27 20:15
  30. 0716018needs attentionincremental2H · 11M · 12L2026-07-27 19:22
  31. 215cd2dneeds attentionincremental3H · 6M · 5L2026-07-27 17:04
  32. ec46958needs attentionincremental0H · 3M · 5L2026-07-27 16:51
  33. de7b337blockedincremental4H · 9M · 14L2026-07-27 06:36
  34. b1bb9c0needs attentionincremental1H · 2M · 4L2026-07-27 05:09
  35. 4701d11needs attentionincremental0H · 4M · 3L2026-07-27 04:44
  36. e1626c4needs attentionincremental3H · 9M · 10L2026-07-27 03:21
  37. 195f198needs attentionincremental3H · 3M · 3L2026-07-25 01:22
  38. 42c7358safeincremental0H · 0M · 0L2026-07-22 20:46
  39. 85b9018needs attentionincremental0H · 1M · 6L2026-07-21 23:51
  40. a7b2a9aneeds attentionincremental0H · 9M · 12L2026-07-21 18:49
  41. c2ee0daneeds attentionincremental4H · 7M · 7L2026-07-21 02:17
  42. e8ffa5eneeds attentionincremental4H · 7M · 5L2026-07-21 01:33
  43. a2d2a54needs attentionincremental2H · 7M · 3L2026-07-21 00:51
  44. 576fbd6needs attentionfull1H · 6M · 7L2026-07-21 00:35
  45. d3465e8needs attentionincremental1H · 7M · 10L2026-07-21 00:23
  46. dc794a7needs attentionincremental0H · 5M · 5L2026-07-20 23:46
  47. 9082773needs attentionfull1H · 3M · 3L2026-07-20 23:13