# Oshun v1 — Audit pass 5 gap findings

Date: 2026-05-28 Owner: platform team Previous passes:
[v2](./2026-05-28-v2-gap-audit.md), [v3](./2026-05-28-v3-gap-audit.md),
[v4](./2026-05-28-v4-gap-audit.md)

Audit pass 4 closed regressions + 11 high-impact items (#147-#157) and filed 24
remaining v4 findings as discrete tasks (#158-#181). The stop hook on the "find
all" goal flagged that pass 4 hadn't gone broad enough; this fifth sweep used
**five** more parallel read-only explore agents over surface area that passes
1-4 didn't touch:

| Agent                       | Findings | Top themes                                                                                                  |
| --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| schema / migrations / pools | 8        | nullable user_id on payments/api-keys; FK no ON DELETE; pool size drift; status string-not-enum             |
| test coverage / gating      | 10       | MFA/Stripe/AWS/LLM/TTS all describe.skip on never-set env; wide-range assertions; 16+ control-room E2E skip |
| secrets / crypto / env      | 9        | JWT_SECRET hardcoded fallbacks in 3 sites; AUDIT/CDN/WATERMARK key defaults; raw-concat KDF                 |
| concurrency / races         | 15       | check-then-act in svc-auth registration + OAuth; EventEmitter leaks; non-atomic counter increments          |
| error handling / null       | 8        | 20 silent catches in 3 services; double-! on Map.get; parseInt NaN flowing to DB; 8 process.exit(1)         |

**50 net-new actionable findings, all filed as discrete tasks #183-#232.**

## In-session closure

| #   | Task fix                                  | Area                |
| --- | ----------------------------------------- | ------------------- |
| 207 | iris/api parseInt → parsePositiveIntParam | Err-7 (this commit) |

The rest are tracked in the task list. They're not bundled in-session because
each finding lives in a different service and applying them in one commit would
create a giant change-set with no per-service review boundary.

## Top tier — recommended next-up

1. **Sec5-1 (#224)** — JWT_SECRET hardcoded fallbacks in 3 svc-auth sites.
   Production deploy without env var lands on a literal string an attacker can
   read in the source repo and forge tokens with. Must close before anything
   ships.
2. **Sec5-3 (#226)** — AUDIT_SECRET_KEY hardcoded fallback compromises audit-log
   tamper detection. The HMAC can be re-computed from source.
3. **Con-3 / Con-4 (#211, #212)** — svc-auth user registration + OAuth callback
   have check-then-act races that allow duplicate users on the same email.
4. **Con-11 (#219)** — yemaya BullMQ workers have idempotencyKey declared but no
   consumer enforces it. Retries double-bill.
5. **Sch-2 / Sch-3 (#184, #185)** — nullable userId on PaymentSubscription
   - ApiKey lets orphan rows accumulate with no revocation path.

## Out of scope for this audit

Six surfaces were intentionally NOT covered this pass; flagged for a later pass
6 if needed:

- Container-image security beyond the SHA pin (#145) — Trivy results, CIS docker
  bench, image-as-root, USER directive sweep
- Infrastructure-as-code (Terraform / Pulumi) drift
- Runtime SBOM completeness
- Policy / RLS (row-level security) on Postgres
- Multi-tenant isolation correctness across the 14 prisma schemas
- E2E test coverage of the v6-egbe game-server flow
