Threat model and mitigations (spec §12), with implementation status in this repo.
| Threat | Surface | Mitigation | Status |
|---|---|---|---|
| Prompt injection | User input → LLM | Prod structuring (Pass B) uses schema-constrained output; all extractor output is validated against the Graph Pydantic schema before the engine runs. Untrusted text never triggers tool calls. |
Implemented (schema validation in extractor/validator) |
| LLM cost exhaustion | /analyze |
max_nodes hard cap 18 (Pydantic bound + orchestrator check); 5-minute job timeout; per-request token caps in providers; per-client token-bucket rate limiting on the cost-heavy endpoints (analyze/simulate/whatif/upload/vision/video), keyed on bearer subject or IP (ZEROFORCE_RATE_LIMIT_PER_MINUTE, default 30/min in prod). |
Implemented (in-process per instance; back with Redis for a global cap across instances) |
| PII in uploads | /upload |
20 MB cap; SSN / card-number regex redaction before any text leaves the gateway. | Implemented + tested (test_upload_redacts_pii) |
| Credential exposure | Provider keys | Keys only via env / Secret Manager; never in the client bundle (all LLM calls are server-side); .env* gitignored; .env.example ships placeholders only. |
Implemented |
| Citation forgery | LLM hallucinated sources | Prod citations come from Vertex grounding metadata (Pass A), not free text. Unverified citations should be badged in the UI. | Partial (grounding metadata captured; UI badge TODO) |
| Cross-tenant data leak | Firestore | Prod: Firestore security rules enforce userId on reads; analyses user-scoped, sharing opt-in. |
TODO (rules not written; single-user dev assumed) |
| AuthN/Z | All endpoints | Optional bearer token (ZEROFORCE_API_TOKEN); when set, all protected routes require it. Prod target: Firebase Auth + IAP. |
Implemented (dev token) + tested (test_auth_required_when_token_configured) |
- No secrets are committed. Verify with
git grep -nE "(API_KEY|SECRET|TOKEN)=.+"returning nothing. - Chaos: a provider outage mid-pipeline fails the job cleanly with
llm_provider_error(tested intest_provider_outage_fails_job_gracefully) rather than hanging or crashing. - Rate limiting is per-process (token bucket). On multi-instance Cloud Run the effective
global limit is
instances × rate; back it with a shared Redis store for a hard global cap. - Before public deployment: write Firestore security rules, add the unverified-citation UI
badge, and run a dependency audit (
uv pip audit,npm audit).
This is a pre-production build. Report security issues to the repository owner.