Cartographer is an honesty-branded tool, so its own engineering has to clear the same bar it sets for everything else. (SPEC §7.)
One pass over the six STRIDE categories. Each row points at a mitigation that already lives in the codebase; the sections below carry the detail.
| Threat (STRIDE) | Cartographer's mitigation |
|---|---|
| Spoofing | Authenticated Cloud Run (--no-allow-unauthenticated, reached via IAP / an identity token); a leaked URL cannot act as a caller. |
| Tampering | The deterministic AST gate (grounding/gate.py) re-verifies every claim's evidence tokens against real source on each run; all ingested PR / comment / docstring text is treated as data, never as a verdict. Pinned deps + lockfiles resist dependency tampering. |
| Repudiation | Per-token receipts (Verdict.checked) on every ACCEPT/REJECT, an eval gate re-derivable in CI, and "as audited on <commit>" provenance stamped on the numbers. Every claim is reproducible, not asserted. |
| Information disclosure | Contributor identities pseudonymized (SHA-256 → keeper-XXXX); the live-path caller identity is hashed, never stored or logged; keyless Vertex ADC, no API keys in code / prompts / logs; .gitignore + gitleaks / detect-secrets. |
| Denial of service / denial-of-wallet | Input length cap (MAX_QUESTION_CHARS) rejected before any provider call, a per-identity rate limit (HTTP 429), a hard hybrid live-call budget (MAX_LIVE_CALLS), and a cached + scale-to-zero deploy (--max-instances 3) with a billing-budget alert. Detail below. |
| Elevation of privilege | Runtime service account scoped to roles/aiplatform.user only; the deploy creates no IAM / SA / VPC (it reuses existing resources, least privilege); the verdict carries no model, so a prompt cannot escalate into an accepted fabrication. |
The judged path never touches a live LLM. For the curated questions the demo serves pre-baked, cached answers (CARTOGRAPHER_LLM_MODE=cached, the default), and the cached provider refuses on a miss instead of falling through to a live call. A live Gemini call happens only when an operator explicitly sets CARTOGRAPHER_LLM_MODE=live with ADC present.
The service is authenticated rather than public: Cloud Run deploys with --no-allow-unauthenticated and is reached via gcloud run services proxy or an identity token, so a leaked link cannot drive spend. Two backstops sit underneath that. A hard instance cap (--max-instances 3) with scale-to-zero (--min-instances 0), and a billing budget with an alert on the GCP project, set in the console.
- Dependencies are pinned across
requirements.txt,requirements-dev.txt, andweb/package-lock.json. pip-audit(Python advisories) andgitleaks(secret scan) run in CI on every push and PR.- There is no LiteLLM in the stack. Gemini is native in ADK, so the
litellm1.82.7/1.82.8 supply-chain advisory doesn't apply by construction.
Gemini runs keyless through Vertex ADC, on a runtime service account scoped to roles/aiplatform.user and nothing else. No API keys live in code, prompts, logs, or commits. .gitignore blocks .env, *.key, and service-account*.json, and detect-secrets / gitleaks enforce it.
Every piece of ingested text (repo, PR, issue, git history) is treated as data, not as instructions. The agent's decisions come from the deterministic AST gate (grounding/gate.py), never from text in the corpus.
The verifier has two layers. The deterministic gate is authoritative for rejection, while the Gemini LLM-judge is advisory only: it can downgrade or reject a claim but can never accept one on its own. A refusal is the safe default.
bash scripts/verify.sh blocks merge. It runs ruff, the full test suite, and the eval gate (0 fabrications by the deterministic gate, refusal pass^k, answer-coverage, gap precision/recall, and the web build).