Skip to content

date: 2026-06-07 tags: [security, threat-model, integrations, prompt-injection, blast-radius] status: active graduated_to:

Threat-model an integration at full blast radius AND as a two-way boundary

Symptom — The first threat model under-scoped Google: it framed a compromised OAuth token as "task + calendar access" (the features Tempo uses) and modelled Google only as an asset we hold — missing both the true blast radius and the inbound attack vector. Caught in PR #322 review.

Root cause — Two recurring blind spots when modelling a third-party integration: (1) scoping the credential's blast radius to what we use it for instead of what the token can reach — the whole account at its granted scopes; (2) modelling the integration one-directionally (the access we hold) and forgetting it is also an ingress — content read from it (a malicious email, a calendar invite) is attacker-controlled and can smuggle a prompt-injection payload into an AI call.

Fix — When adding or reviewing a trust boundary for an integration, state both: the full granted-scope blast radius (the whole Todoist/Google account, not just the feature), and the two-way surface — inbound content is attacker-controlled (fence with @untrusted, read through a read-only seam), not just the outbound access. Applied to the asset list, B3, and B7 in docs/security/threat-model.md (commit 5bf074f).

Guard — Bake into threat-model authoring: every integration boundary covers full-reach blast radius + ingress. Candidate one-liner for .claude/rules/security.md (alongside the threat-model link), and a natural find-vulns / qa-prompts check (is every external-sourced string fenced?). Surfaced via build-pr-feedback on PR #322.