fix(agent-team): scrub exception text from /api/state + /api/topology errors

/sh-security-review confirmed SEC-DASH-001 (low): build_snapshot embedded str(exc)
of a sqlite/OS error into the /api/state payload, leaking the absolute DB path /
table names to the unauthenticated LAN surface. Return only type(exc).__name__
(matching the task_detail hardening); apply the same to /api/topology's error
branch (SEC-DASH-003). Full exception detail stays in server-side logs.
This commit is contained in:
Adam Moussa 2026-06-23 17:28:22 -04:00
parent 6951bf2fc6
commit a632a7df7d
2 changed files with 15 additions and 4 deletions

View file

@ -258,7 +258,14 @@ def make_dashboard_app(db_path: str | Path | None = None) -> Any:
try:
payload = build_topology()
except Exception as exc: # pragma: no cover - defensive
payload = {"trees": [], "nodes": [], "edges": [], "error": str(exc)}
# Type only, never str(exc) — keep internal LangGraph/import detail
# off the unauthenticated LAN surface (SEC-DASH-003).
payload = {
"trees": [],
"nodes": [],
"edges": [],
"error": type(exc).__name__,
}
return JSONResponse(payload, headers={"Cache-Control": "no-store"})
@app.get("/api/task/{thread_id}")

View file

@ -298,19 +298,23 @@ def build_snapshot(db_path: Path | str | None = None) -> Snapshot:
tasks = _read_tasks(resolved, thread_ids, open_by_thread)
spend, spend_total, budget_available = _read_recent_spend(conn)
except sqlite3.OperationalError as exc:
# Locked / busy / unreadable — never crash, just say so.
# Locked / busy / unreadable — never crash, just say so. Return only the
# exception TYPE, never str(exc): a SQLite message can carry the DB path /
# table names, and this payload is served unauthenticated on the LAN
# (matches the task_detail hardening). [sh-security-review SEC-DASH-001]
return Snapshot(
generated_at=generated_at,
db_path=str(resolved),
ok=False,
error=f"could not read ledger (busy or unreadable): {exc}",
error=f"could not read ledger (busy or unreadable): {type(exc).__name__}",
)
except Exception as exc: # defensive: any unexpected read failure
# Type only, never str(exc) — see SEC-DASH-001 note above.
return Snapshot(
generated_at=generated_at,
db_path=str(resolved),
ok=False,
error=f"unexpected error reading ledger: {exc}",
error=f"unexpected error reading ledger: {type(exc).__name__}",
)
finally:
if conn is not None: