[Audit L-01] Public indexer API returns raw database error strings on 500
## Source Internal full-stack audit [`audits/audit_full_1777967937.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/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: ```json { "error": "<sqlx / DB error to_string()>" } ``` 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`): ```339:350:indexer/src/api.rs let total: i64 = match sqlx::query_scalar::<_, i64>("SELECT COUNT(*)::bigint FROM idx_timecurve_buy") .fetch_one(&state.pool) .await { Ok(n) => n, Err(e) => { return ( StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ "error": e.to_string() })), ) .into_response(); } }; ``` The same `e.to_string()` pattern appears on many routes in `indexer/src/api.rs`. ## Recommended fix - Return a **generic** public JSON body for 500 responses, e.g. `{ "error": "internal server error" }` (stable string for clients). - Log the full error with `tracing::warn!` / `tracing::error!` server-side; include a **request id** or trace correlation id when available. - Add a small helper (e.g. `internal_error_response(context: &str, err: impl std::error::Error)`) so handlers cannot drift back to raw echoes. - Add an API/unit test that a forced query failure does **not** echo raw SQL or table names in the response body. ## Acceptance criteria - No production JSON 500 response includes raw `sqlx` error text, table names, or constraint names in the `error` field. - Detailed failures remain fully logged server-side. - One centralized helper (or macro) used consistently across `api.rs` error branches for this class of failure. - Automated test asserts sanitized body on simulated DB failure. ## Verification checklist - [ ] Trigger a DB error on a test route — response body is generic; logs contain the real error. - [ ] Grep `api.rs` for `e.to_string()` in JSON error responses — eliminated or confined to non-public paths. - [ ] `cargo test` passes including new API error-redaction test.
issue