Disciplines · Audits
Oshun v1 — Audit pass 3 gap findings
Each agent was briefed with the exact #90–#118 task list as "out of scope" and instructed to find file:line evidence for severity-ranked findings only.
5 sections5 min read
On this page
Date: 2026-05-28 Owner: platform team Previous pass:
docs/audits/2026-05-28-v2-gap-audit.md
Audit pass 2 closed tasks #90–#118. This third sweep used seven parallel
read-only explore agents to find what those didn't catch, across seven
non-overlapping surfaces. 93 net-new actionable findings. This doc is the
authoritative source while tasks #120+ are filed.
How the sweep was scoped#
Each agent was briefed with the exact #90–#118 task list as "out of scope" and
instructed to find file:line evidence for severity-ranked findings only. Agents
were told to skip stylistic issues and cap at 20–25 findings each.
Agent
Findings
Top themes
security
10
SSRF in gpu-dispatcher callback; OAuth open redirect; CORS regression
reliability
15
23+ services missing SIGTERM; unbounded Maps; missing /health, /metrics
data integrity
20
FK relations without onDelete; soft-delete fields with no sweeper
build / ci / ops
8
67 workflows w/o permissions: block; 65 w/o concurrency cancel-in-prog
test / contract
10
expect(true).toBe(true) stubs; skipped svc-auth integration suite
privacy / pii
15
JWT body contains email+IP; 6 audit trails store full IPs un-minimized
performance
15
iris GraphQL N+1 cluster (resolvers + dataloaders); regex DoS in DLP rules
High-severity findings — fix candidates this pass#
Security#
#
Title
Location
S1
SSRF via user-controlled callbackUrl in GPU dispatcher
apps/infra/gpu-dispatcher/src/callbacks.ts:596
S2
OAuth redirect_uri reflected without allowlist
apps/lilith/svc-auth/src/auth-missing-endpoints.ts:176
S3
Wildcard CORS still in 8+ services missed by #71
apps/aja/svc-motion-ai/src/app.ts:114 + veritas/*, aphrodite/devices
S4
RevenueCat webhook signature optional on env var
apps/lilith/svc-payment-orchestrator/src/iap/iap-routes.ts:282
S5
jwt.decode instead of jwt.verify in offline encryption
apps/lilith/svc-offline/src/encryption.ts
Reliability#
#
Title
Location
R1
23+ Fastify services missing SIGTERM handler
apps/lilith/svc-yoga-practice/src/server.ts:16, svc-voice-pipeline/src/server.ts:18, svc-webrtc/src/server.ts:8, svc-stt/src/server.ts:8, + 19 others
R2
10 unbounded Maps in voice-pipeline (sessions, PTT, VAD, MOS)
apps/lilith/svc-voice-pipeline/src/app.ts:68-78
R3
4 unbounded Maps in community-translation
apps/lilith/svc-community-translation/src/app.ts:2013-2016
R4
fetch(url) without timeout in model registry downloads
apps/lilith/svc-ai/src/distilled-model-registry.ts:577
R5
Unbounded notifications array in VoiceCloningRequestManager
apps/lilith/svc-tts/src/voice-cloning-request-manager.ts:72
Privacy / PII#
#
Title
Location
P1
ConsoleEmailSender logs tokens + emails in dev mode
apps/lilith/svc-auth/src/email-sender.ts:232
P2
JWT body contains cleartext email + IP
apps/lilith/svc-auth/src/jwt-types.ts:45
P3
Full IPs stored unminimized in 2 audit trails
apps/lilith/svc-data-governance/src/app.ts:2293, apps/maat/api-gateway/src/middleware/request-response-logging.ts:433
P4
pg_dump backups gzipped but not encrypted
scripts/db-backup.sh:177
P5
user_hash analytics derives identity without salt
apps/lilith/svc-observability/src/analytics-routes.ts:52
#
Title
Location
Pe1
iris/api dataloaders loop instead of batch (4 of them)
apps/iris/api/src/graphql/data-loaders.ts:130,154,181,208
Pe2
iris GraphQL resolvers run N+1 over agents / KBs / conversations
apps/iris/api/src/graphql/resolvers.ts:312,496,1202
Pe3
Hardcoded pageSize=1000 in three service-data-source list calls
apps/iris/api/src/graphql/service-data-sources.ts:918,1059,1115
Pe4
RegExp compiled from user-supplied DLP rule pattern (regex DoS)
apps/iris/developer-portal/src/services/data-loss-prevention-service.ts:487
Build / CI / ops#
#
Title
Location
B1
67 workflows missing permissions: block (write-all default)
.github/workflows/*.yml (mass)
B2
65 PR workflows missing concurrency: cancel-in-progress
.github/workflows/*.yml (mass)
B3
Pre-commit hook has typecheck step commented out
.husky/pre-commit:19
B4
20+ Dockerfiles use unpinned base image tags
docker/psyche/Dockerfile.tools:19 + others
Test / contract#
#
Title
Location
T1
describe.skip permanently disables svc-auth integration suite
apps/lilith/svc-auth/src/__tests__/integration/auth-mock-typed-demo.test.ts:34
T2
expect(true).toBe(true) stub in DSR processing test
apps/lilith/svc-auth/src/__tests__/audit-logging.test.ts:418
T3
expect(true).toBe(true) stub in plagiarism detection test
apps/lilith/svc-moderation/__tests__/academic-integrity.test.ts
Data integrity — top 6 of 20#
#
Title
Location
D1
bellona ExportJob.build relation lacks onDelete: policy
libs/bellona/database/prisma/schema.prisma:342
D2
veritas.PaymentTransaction.amount stored as String not Decimal
libs/veritas/database/prisma/schema.prisma:1851
D3
sophia Document.deletedAt + Citation.deletedAt without sweeper
libs/sophia/database/prisma/schema.prisma:336,489
D4
sophia Entity.canonicalId + mergedInto allow cycles
libs/sophia/database/prisma/schema.prisma:551-553
D5
nisaba VariantUnit.manuscriptId FK without explicit onDelete
libs/nisaba/database/prisma/schema.prisma:388
D6
yemaya Tag.organizationId allows multiple NULL global collisions
libs/yemaya/database/prisma/schema.prisma:553
What's being addressed THIS pass (in-session fixes)#
Tracked as tasks #120–#... — see TODOS.md and TaskList. Highest-leverage
small-effort items going in immediately:
B3 — uncomment typecheck step in .husky/pre-commit
B1/B2 — sweep .github/workflows/*.yml, add permissions: contents: read
concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true }
to PR-triggered workflows
P1 — strip token from ConsoleEmailSender dev log; keep redacted "magic link
sent to " form
R4 — wrap distilled-model-registry downloadFromURL fetch in
AbortSignal.timeout(60_000)
R5 — cap voice-cloning-request-manager notifications array at 10_000 with
oldest-first eviction
Pe4 — wrap DLP rule regex in try { new RegExp(...) } catch + reject patterns
over 256 chars or with (.+)+-style nested quantifiers
T1 — un-skip or it.skip.each the auth integration suite with an explicit
env-var gate instead of permanent describe.skip
T2 / T3 — replace truthy stub assertions with real domain assertions or mark
.todo with tracking issue
What's being filed (separate work)#
The following land as #120+ but are bigger pieces:
S1 SSRF in gpu-dispatcher (needs URL allowlist + tests)
S2 OAuth redirect_uri allowlist (registration table)
S3 CORS sweep across remaining Hono services
S4 RevenueCat make signature required (not env-gated)
S5 jwt.verify migration
R1 graceful shutdown across 23+ services (mass refactor)
R2 / R3 voice-pipeline + community-translation Map eviction (per-service)
R6 process.on('unhandledRejection') across services
P2 JWT body PII removal (compat migration)
P3 IP minimization helper + sweep audit trails
P4 pg_dump encryption (gpg + key management)
P5 user_hash salt
Pe1–Pe3 iris GraphQL N+1 cluster (substantial DataLoader refactor)
All 20 data-integrity findings (schema + migration per fix)
B4 Docker base image SHA pinning
Methodology notes#
Agents were instructed to use the same stub-indicator framing the v2 audit used:
"Search for evidence of X" rather than "verify X is real." The adversarial
framing surfaced regressions in #71 (CORS) and #98 (graceful shutdown only
landed in veritas/video, not other services).
False positives were minimal: the test/contract agent's findings about
describe.skip for STRIPE_SECRET_KEY-gated tests were correctly classified as
out-of-scope (intentional integration test pattern).