Protect affiliate settlement cron route#158
Conversation
Greptile SummaryThis PR fixes a fail-open security bug in the affiliate settlement cron route: the previous guard
Confidence Score: 5/5Safe to merge — the change correctly closes a fail-open auth gap and is backed by targeted regression tests. The two-gate auth refactor is minimal, easy to reason about, and the tests directly cover all three rejection conditions. No prior authenticated path is removed; the only behavioral change is that a misconfigured deployment now returns 401 instead of processing requests without any secret check. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Cron as Cron Job / Caller
participant Route as POST /api/affiliates/settle
participant Env as process.env
participant SC as createServiceClient
participant Settle as settleCommissions
Cron->>Route: POST (Authorization: Bearer secret)
Route->>Env: read CRON_SECRET
alt CRON_SECRET missing or empty
Route-->>Cron: 401 Unauthorized (fail-closed)
else CRON_SECRET present
Route->>Route: "compare authHeader to Bearer {cronSecret}"
alt Token mismatch or missing
Route-->>Cron: 401 Unauthorized
else Token matches
Route->>SC: createServiceClient()
SC-->>Route: admin client
Route->>Settle: "settleCommissions(admin, { limit: 100 })"
Settle-->>Route: "{ settled, failed, total_sats }"
Route-->>Cron: "200 { settled, failed, total_sats }"
end
end
Reviews (3): Last reviewed commit: "Protect affiliate settlement cron route" | Re-trigger Greptile |
| return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } | ||
|
|
||
| if (authHeader !== `Bearer ${cronSecret}`) { |
There was a problem hiding this comment.
Timing-sensitive bearer token comparison
The check authHeader !== \Bearer ${cronSecret}`uses a non-constant-time string comparison. An attacker who can make many rapid requests and measure response latency can theoretically brute-force the secret one character at a time. In practice this risk is low for a cron endpoint, but for secrets guarding financial settlement operations it is worth switching to a timing-safe compare such as Node'scrypto.timingSafeEqualor thesafe-compare` package to eliminate the side-channel entirely.
Summary
POST /api/affiliates/settleis deployed withoutCRON_SECRETFixes #156
Verification
corepack pnpm vitest run src/app/api/affiliates/settle/route.test.tscorepack pnpm type-checkcorepack pnpm prettier --check src/app/api/affiliates/settle/route.ts src/app/api/affiliates/settle/route.test.tsgit diff --check