Files
routstr-core/routstr
9qeklajcand9qeklajc 7f44389b59 fix(certification): harden against adversarial inputs found by tester agents
Three independent tester subagents found 12 defects (1 critical, 2 high,
9 medium/low). This commit fixes all of them and adds 56 regression tests.

Critical (CLI dead on arrival):
- certify_upstream_url() called sats_usd_price() which raises ValueError
  in any fresh process (the module global is only set by the app lifespan
  task). Now resolves via _resolve_sats_usd_price(): module global → BTC
  global → exchange feed → None (warn row, not a crash). Adds
  --sats-usd-price CLI flag for explicit override.

High (non-finite tokens crash the billing path):
- parse_token_count() crashed on Infinity/NaN (json.loads accepts both).
  Fixed to reject non-finite values → 0. This was a shared-code bug in
  routstr/payment/usage.py, reachable from the main billing path too.
- usage_capture_row and cost_prompt_completion_row now guard normalize_usage
  in try/except via safe_row(), so a raising check becomes a fail row
  instead of a 500.

Medium:
- certification_row now coerces non-dict evidence to {} (was stored verbatim)
- endpoint_validity_row checks .hostname not .netloc (http://:8080 rejected)
- models_payload_row rejects empty-string ids (agrees with CLI discovery)
- models_payload_row / usage_capture_row guard against non-dict payloads
- _reported_usd_cost uses coerce_rate for parity with the engine
- _expected_token_msats raises ValueError on non-finite rates (not OverflowError)
- get_candidates() call in admin endpoint wrapped in try/except
- Admin timeout clamped to [1, 60] seconds
- Explicit --prompt-price validated via coerce_rate (negatives rejected)
- CLI --json-out flag writes strictly parseable JSON to a file
- CLI logs routed to stderr so stdout is the report's channel

Test plan: 1749 passed, 1 skipped (no regressions); ruff clean; mypy clean.
2026-09-21 22:53:33 +02:00
..
2026-09-07 00:30:05 +02:00
2026-09-16 13:15:32 +02:00