Skip to content

Skills · solution-architect

threat-model

Produce a STRIDE threat model from a design doc, architecture description, or feature spec — assets and trust boundaries first, threats anchored to boundary crossings with concrete attack scenarios, severity-ranked with verifiable mitigations. Use when the user asks to threat-model a design, run a security review of an architecture, ask "how could this be attacked", or needs security sign-off input before build.

Pack solution-architectTier heavyJob Design the systemv0.1.0

npx skilldrop-cli install threat-model
Other ways to install

With the sibling skills it hands off to:

npx skilldrop-cli install threat-model --with-related

The whole pack it belongs to:

npx skilldrop-cli install --pack solution-architect

By hand, from a clone of the repo:

cp -R packs/solution-architect/skills/threat-model ~/.claude/skills/

In Claude Code, with the solution-architect plugin installed, invoke it as:

/solution-architect:threat-model

Try it

A realistic prompt from the skill's acceptance evals:

Threat model this design: a multi-tenant reporting service where customers upload CSVs, we process them in a worker, and results are served from an S3 bucket via signed URLs.

What a good result looks like

Example output

document-upload feature → 6 ranked findings · the skill's own worked example, source

Input

New feature: customers upload contract PDFs for e-signing. Browser gets a presigned S3 URL from upload-api (authenticated), uploads directly to the contracts bucket, then upload-api enqueues a message; doc-processor consumes it, runs OCR via a third-party API, and writes extracted text to Postgres. Support staff can view any customer's documents in the admin panel to help with signing issues. Multi-tenant SaaS, tenant ID in the JWT.

Output

System summary

Assets (ranked): customer contracts (legal PII — top asset), extracted contract text in Postgres, S3 credentials / presigned-URL minting capability, OCR API key. Entry points: upload-api (authn'd HTTP), direct browser→S3 upload, SQS queue into doc-processor, third-party OCR API responses, admin panel. Trust boundaries: internet→upload-api, internet→S3 (presigned), upload-api→queue→doc-processor, doc-processor→OCR vendor (data leaves our trust domain), doc-processor→Postgres, support-staff→admin panel, tenant→tenant throughout. [assumption] Presigned URLs are PUT-only. [assumption] OCR vendor is contractually approved for PII.

Findings

🟥 TM-1 — Cross-tenant document read via unscoped presigned URLs (boundary: internet→S3; STRIDE: I, E) Scenario: a tenant inspects their presigned URL, finds object keys are contracts/{uuid}.pdf with no tenant prefix; any later GET-capable URL or a key-guessing bug exposes other tenants' contracts — compromise of the top asset. Existing mitigation: none stated in the design. Recommended: keys become contracts/{tenant_id}/{uuid}.pdf, minted only from the JWT's tenant claim, GET URLs scoped to prefix and ≤5 min. Verify: CI test fetches tenant B's key with tenant A's session — must 403.

🟥 TM-2 — Malicious file detonates in doc-processor (boundary: queue→doc-processor; STRIDE: T, E) Scenario: attacker uploads a crafted PDF (parser exploit, zip bomb, SSRF-triggering embedded URL); doc-processor parses it with vendor-library defaults and processor credentials. RCE here reaches Postgres and the OCR key. Existing mitigation: none stated. Recommended: validate content-type + size at presign and on consume; parse inside a sandboxed, least-privilege worker (no Postgres write role until post-parse); pin and patch the parser. Verify: corpus of hostile PDFs in CI lands in DLQ, not crash-loop or exec.

🟧 TM-3 — Support staff can browse any tenant's contracts with no audit trail (boundary: human→system; STRIDE: I, R; insider pass) Scenario: a support login (insider or phished) bulk-reads contracts across tenants; the design grants "view any customer's documents" with no purpose-binding or logging — undetectable until external disclosure. Existing mitigation: admin panel is authenticated. Recommended: case-bound access (support sees a document only via an open ticket reference), immutable audit log of every view, MFA on support accounts. Verify: admin-action integration test asserts the audit row; access without ticket reference → denied.

🟧 TM-4 — Queue accepts unauthenticated/poisoned messages (boundary: upload-api→queue; STRIDE: S, T) Scenario: any internal workload (or attacker post-TM-2) enqueues {tenant_id: victim, s3_key: attacker_file}; doc-processor trusts the message and writes attacker text into the victim tenant's Postgres rows. Existing mitigation: queue is network-internal [assumption: flat internal trust]. Recommended: doc-processor re-derives tenant from the S3 key prefix (post-TM-1) instead of trusting the message field; schema-validate on consume; producer allow-list via IAM. Verify: message with mismatched tenant/key → rejected with alert.

🟨 TM-5 — Contract PII leaves trust domain to OCR vendor unminimized (boundary: doc-processor→vendor; STRIDE: I) Scenario: full contracts ship to the vendor; a vendor breach exposes the top asset with no detection on our side. Requires the vendor's failure, hence 🟨 not 🟧. Existing mitigation: [assumption] contractual DPA. Recommended: confirm the DPA covers this data class; disable vendor-side retention; log per-document egress so exposure is enumerable after a vendor incident. Verify: vendor account settings reviewed + egress log row asserted in tests.

🟨 TM-6 — Unbounded uploads as cost/availability attack (boundary: internet→S3; STRIDE: D) Scenario: a scripted tenant requests thousands of presigned URLs and uploads max-size files; S3 cost spike and OCR-queue saturation delay every tenant's processing (noisy-neighbor DoS). Existing mitigation: authn required. Recommended: per-tenant presign rate limit + size cap at presign time; queue depth alarm with per-tenant fairness. Verify: load test on the presign endpoint shows throttling before queue saturation.

n/a ledger

Repudiation at internet→S3: n/a beyond TM-3's audit log — S3 access logging assumed on. DoS at admin panel: internal user base, marginal.

Authz / insider pass

Authz: TM-1, TM-4 (tenant→tenant). Insider: TM-3. Sweeps produced findings — no all-clear claimed.

Questions for the team

  1. Are presigned URLs PUT-only, and is there any GET path today? (confirms or escalates TM-1)
  2. Does the OCR vendor contract cover contract PII, and is vendor-side retention off? (resolves TM-5's assumptions)

Out of scope

E-signing flow itself (separate design); S3 bucket-policy review (assumed account-standard); browser-side threats (XSS) — covered by the web app's existing model.

Revisit trigger

New entry point (e.g. email-in ingestion), new document consumer, or any change to support-access rules.

How it's checked

Measured against plain Claude Code (2026-10-23): it met 86% of its checks, against 57% without it, and a blind judge preferred it. How skills are checked

21 assertions across 3 acceptance evals, and 8 trigger queries. Every skill's checks

What the output must do
  • Assets, entry points, and trust boundaries are established before any threat is listed
  • Every threat is anchored to a named trust-boundary crossing or asset — unanchored checklist residue is cut
  • Every threat narrates a scenario with actor, path, and result; 'X is possible' is not accepted as a finding
  • STRIDE is applied per boundary crossing rather than as a flat checklist
  • Severity is calibrated to this system's assets rather than CVSS theater — not everything is critical
  • Existing mitigations are credited as verified or assumed, and every recommendation carries a verification step
  • The authorization sweep (tenant-to-tenant, authn is not authz) and the insider/compromised-credential sweep are both run
  • Lists no more than 10 threats, ordered with the most severe first
  • Flags that proof-of-address documents are public-read through the CDN before the nightly scan runs, contradicting the claim that documents are scanned before anyone can see them
  • Rejects the assumption that the handhelds are on the internal network: they reach a public endpoint over 4G, so the unauthenticated plates API is open to anyone
  • Identifies that a forged call to the payment callback could activate a permit without payment, and calls for the callback to be verified
  • Does not spend threats on the payment provider's own internals, which the design puts out of scope, while still covering the callback into the council's app
  • Does not confirm that the design is secure to build; it states what must be fixed or decided first
  • Does not invent components, data or controls that are not in the design doc; anything assumed is marked as such
  • Gives a direct answer to 'is that ok' (it is risky as described) rather than only hedging or asking questions
  • Identifies that anyone who gets the link, including if it is forwarded or posted publicly, can see volunteers' names and mobile numbers
  • Points out that anyone with the link can change or delete entries, not just read them
  • Points out that the rota shows when named people will be at a known place, which is a personal safety risk
  • Suggests practical, non-technical fixes (for example limiting access to named people, or keeping phone numbers out of the shared sheet)
  • Does not invent details about the charity or its tools that are not in the message
  • Keeps to a few bullets in plain language
When it should and shouldn't activate

Should activate:

  • Threat model this design
  • Run STRIDE on this architecture before we build it
  • What could an attacker do to this system as designed?
  • Security review of this design doc, with trust boundaries and attack scenarios

Should leave to another skill:

  • Review this diff for exploitable security bugs
  • Threat model my AI agent's tool and data access
  • Audit this UI for accessibility barriers
  • Convene a council to deliberate this architecture

Related skills

design-doc, devils-advocate, postmortem-generator, reverse-architecture, sonar-review

Source

SKILL.md · the whole folder on GitHub.