Safeguard
Concepts

Threat Modeling for Developers

Threat modeling doesn't have to be a heavyweight ceremony run by a separate security team. Here's how developers can fold lightweight, per-feature threat modeling directly into pull requests and sprint work.

Daniel Osei
Security Researcher
7 min read

Threat modeling for developers is the practice of shrinking threat modeling from a quarterly, security-team-led ceremony down to a lightweight habit that fits inside a normal feature or pull request. Instead of a two-hour workshop with a facilitator and a wall of sticky notes, a developer asks a handful of pointed questions about the change in front of them — what data does this touch, who can call it, what happens if the input is hostile — and captures the answers where the code already lives. The goal is not a perfect model; it is catching the obvious design flaw before it ships, done by the person best positioned to fix it.

Why It Matters

The traditional model of threat modeling has a scaling problem: there are far more developers shipping changes than there are security engineers to review them. If threat modeling only happens when the security team schedules it, most changes never get modeled, and the ones that do get modeled once and then drift. Meanwhile the developer who wrote the code has already moved on.

Pushing threat modeling to developers fixes the bottleneck and improves the output. The engineer implementing an endpoint knows exactly which trust assumptions they are making — that the caller is authenticated, that the upstream service validated the payload, that the queue only receives internal traffic. Those assumptions are precisely where flaws hide, and they are invisible to a reviewer who was not in the code. Developer-led threat modeling also compounds: each session builds the security intuition that makes the next feature safer by default.

How to Do It: Model the Change, Not the System

The key mental shift is scope. You are not modeling the whole application; you are modeling the change in your branch. That makes it fast enough to do routinely.

A practical per-change checklist:

  1. What am I touching? New endpoint, new data field, new dependency, new external call? If the change adds none of these, a full pass may be unnecessary.
  2. Does it cross a trust boundary? Any point where data moves between different levels of trust — internet to service, service to database, your code to a third-party API — deserves scrutiny. If your change adds or moves a boundary, model it.
  3. Run a mini-STRIDE on the new element. Spoofing (who can call this?), Tampering (can inputs be forged?), Information disclosure (what could leak?), Elevation of privilege (can this be abused to do more than intended?).
  4. Write down the decisions. In the PR description or a short threat-model note in the repo. This is the artifact auditors and future maintainers actually use.

A tiny worked example. A developer adds an endpoint that returns a user's invoices:

Change: GET /api/invoices?userId=<id>
Trust boundary crossed: internet → service → database
Threats:
  - Elevation of privilege: can user A pass user B's id? -> enforce authorization on the session, ignore client-supplied id
  - Information disclosure: does the response include internal fields? -> whitelist returned fields
  - Denial of service: unbounded result set? -> paginate

That block took two minutes and caught an insecure direct object reference — one of the most common and damaging web flaws — before code review. The flow is identify the change → find the boundary → mini-STRIDE → record → ticket.

To make this stick, embed the trigger into existing workflow. A checkbox in the PR template ("Does this change cross a trust boundary? If yes, threat notes below") turns threat modeling from a separate task into a normal part of shipping.

Common Pitfalls

  • Modeling the whole system every time. That is the ceremony you are trying to escape. Model only what the change introduces.
  • Skipping the write-down. A threat considered in your head and never recorded cannot be reviewed, reused, or shown to an auditor. Two lines in the PR is enough.
  • Only modeling "security features." The invoice endpoint above is not a security feature, yet it is where the flaw lives. Ordinary CRUD is where most real vulnerabilities hide.
  • Treating third-party code as out of scope. Adding a dependency is a design decision with a threat surface. "What can this package access, and do I trust its maintainers?" is a legitimate modeling question.
  • Waiting for the security team's permission. Developer threat modeling works because it is self-service. If it requires scheduling, it dies.

How It Connects to Supply Chain Security

For developers, the supply chain is not an abstract concern — it is the import line you just added. Every new dependency is a trust decision, and modern applications add them constantly, often pulling dozens of transitive packages behind a single direct one. A developer-led threat model that ignores dependencies misses the layer where some of the most damaging recent compromises originated: poisoned packages, hijacked maintainer accounts, and malicious post-install scripts that run with your build's privileges.

So the developer's mini-STRIDE should include the change's dependency delta. This is where tooling turns a good intention into an enforced control. Software Composition Analysis runs on your pull request and tells you exactly what a new dependency drags in and whether any of it is known-vulnerable or malicious — answering the "do I trust this package?" question with data instead of a gut feeling. When your change adds an endpoint, dynamic testing can exercise it against the very authorization and input-validation threats you flagged. And because even a per-change model produces a steady stream of findings across a team, Griffin AI prioritizes them so you act on the reachable ones rather than drowning in noise. Compare how developer-first scanning fits your stack on our comparison page, and browse related ideas in the concepts library.

Safeguard's aim is to meet developers where the change happens — in the pull request — so the trust boundaries you reason about in your head are backed by automated checks in the same workflow.

Create a free account to add supply chain checks to your pull requests, or read the documentation to wire threat modeling into your dev workflow.

Frequently Asked Questions

How long should developer threat modeling take per change?

For a typical change, two to ten minutes. You are scoping to what the change introduces — a new endpoint, field, or dependency — not the whole system. If a change genuinely needs an hour of analysis, that is a signal it is architecturally significant and probably deserves a fuller session with more people.

What if I'm not a security expert?

You do not need to be. A short mnemonic like STRIDE and a habit of asking "who can call this, and what if the input is hostile?" catches a large share of real design flaws. The engineer who wrote the code has context no external reviewer has; that context matters more than deep security expertise for catching the common issues.

Where should I record threat notes?

Wherever your team already looks: the pull request description, a threat-model markdown file next to the code, or a linked ticket. The best location is the one your team will actually read and update. Keeping notes in the repo means they version alongside the code and stay discoverable.

Does this replace a dedicated security review?

No — it front-loads it. Developer threat modeling catches the obvious and common flaws early and cheaply, so that a dedicated security review or design review can focus its limited time on the genuinely subtle, high-risk decisions rather than re-finding the basics.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.