[Audit L-01] Public indexer API returns raw database error strings on 500 #157
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#157
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Source
Internal full-stack audit
audits/audit_full_1777967937.md(2026-05-05), finding L-01 (Low). Commit reviewed:3b3bd0c6c501f9cf908667d304146b7b63302b87.Problem
Many public indexer API handlers return HTTP 500 with:
Raw database error strings can expose table names, column names, SQL fragments, constraint names, migration drift hints, or infrastructure details. Browser clients do not need this detail; it increases reconnaissance value of the public API.
Relevant code
Pattern repeated across handlers (example from
timecurve_buys):The same
e.to_string()pattern appears on many routes inindexer/src/api.rs.Recommended fix
{ "error": "internal server error" }(stable string for clients).tracing::warn!/tracing::error!server-side; include a request id or trace correlation id when available.internal_error_response(context: &str, err: impl std::error::Error)) so handlers cannot drift back to raw echoes.Acceptance criteria
sqlxerror text, table names, or constraint names in theerrorfield.api.rserror branches for this class of failure.Verification checklist
api.rsfore.to_string()in JSON error responses — eliminated or confined to non-public paths.cargo testpasses including new API error-redaction test.mentioned in commit
4fd5041fb4mentioned in commit
b0f95405a2mentioned in commit
b3e1daa186Implemented (L-01 / #157)
Merged to `main` (see `internal_db_error_response` in `indexer/src/api.rs`).
Summary
@brouie — please verify on a staging or local indexer (optional: temporarily break a query / revoke DB perms) that the HTTP body stays generic while logs show the real error.
Checklist for verification
Issue left open per request.
mentioned in commit
955228fd7f@PlasticDigits — verification PASS on
b3e1daa.checklist walked:
grep -n 'Json(json!({ "error": e\.to_string' /srv/qa/repos/yieldomega/indexer/src/api.rs→ zero matches. all stale raw-echo patterns eliminated.cargo clippy --all-targets -- -D warnings→ clean, no warningscargo test→ 40 unit + 0 doc + 2 integration = 42/42 PASS at HEAD390f9be(up from 31/31 baseline; new tests for #156 + #157 included)cargo test --lib internal_db_error_response_tests -- --nocapture→ 1/1 PASS oninternal_db_error_response_does_not_echo_sqlx_in_json_body(syntheticsqlx::Error::Protocol("idx_timecurve_buy relation does not exist")redacted toPUBLIC_INTERNAL_DB_ERROR, neitheridx_timecurvenorrelationappear in body)grep -c "internal_db_error_response" indexer/src/api.rs→ 28 references (1 definition + 27 call sites)/v1/timecurve/buys,/v1/timecurve/warbow/battle-feed,/v1/timecurve/warbow/leaderboard,/v1/timecurve/warbow/steals-by-victim-day,/v1/timecurve/warbow/guard-latest,/v1/timecurve/warbow/pending-revenge,/v1/timecurve/warbow/refresh-candidates,/v1/timecurve/buyer-stats, etc. — all use the helper with route-specificcontextstrings for log correlationlive induce-DB-failure walk (item 3) not run — would require breaking postgres or corrupting
DATABASE_URLmid-session, which has collateral damage on the live stack supporting other concurrent verifications. helper unit test + 27 wired call sites + zero stalee.to_stringraw-echo grep matches give strong source-side coverage of the same surface. happy to walk the live induce explicitly if you want it.ready for close on your side.
mentioned in issue #168
mentioned in commit
a94f6c36bdmentioned in commit
278c631bd2mentioned in commit
8de912705dmentioned in commit
f43ac77e85mentioned in issue #262
mentioned in issue #263
mentioned in issue #267
mentioned in issue #282
mentioned in issue #283
mentioned in issue #341