Threat modeling
at the speed of code.

Your threat model lives in the code, so it can’t go stale.

Read the docsGitHub →
~/src/guardlink · src/mcp/server.ts
26 * Resources:
27 * guardlink://model — Full ThreatModel JSON
28 * guardlink://definitions — Assets, threats, controls
29 * guardlink://unmitigated — Unmitigated exposures list
30 *
31 * Transport: stdio (for Claude Code .mcp.json, Cursor, etc.)
32 *
33 * @comment -- "No guardlink_entitlement_accept tool exists on purpose: proposing is an agent's job, granting authority is not"
34 * @flows MCPClient -> #mcp via tool_call -- "Tool invocation input"
35 * @flows #mcp -> FileSystem via writeFile -- "Report/dashboard output"
36 * @flows #mcp -> #llm-client via generateThreatReport -- "LLM API call path"
37 * @flows #mcp -> MCPClient via resource -- "Threat model data output"
38 * @boundary #mcp and MCPClient (#mcp-tool-boundary) -- "Trust boundary at tool argument parsing"
39 * @handles internal on #mcp -- "Processes project annotations and threat model data"
40 * @feature "MCP Integration" -- "Model Context Protocol server for AI agent tooling"
41 * @entitles #mcp-agent to read-threat-model on #mcp against #data-exposure -- "By design: guardlink mcp exists to hand a connected coding agent the threat model — that disclosure is the product, not a leak…"
42 * @comment -- "Entitlement accepted by zippon on 2026-08-10 via guardlink entitle"
43 */
44
45import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
46import { z } from 'zod';
47import { parseProject, findDanglingRefs, findUnmitigatedExposures } from '../parser/index.js';
TerminalProblemsOutput zsh
~/src/guardlink main
mainguardlink 2.1.0814 annotations12 unmitigatedLn 33, Col 1Spaces: 2UTF-8LFTS TypeScript
See it in your theme

Live from GitHub and npm, refreshed every hour. Each figure links to its source.

The problem

Security knowledge lives outside the code. That is the bug.

Teams pay for all three, and all three start going stale the next time someone opens an editor.

A page in ConfluenceExample
▤Security › Paymentsedited 14 months ago
Payments — Threat Model
3. Session handling
Sessions are server-side. The token never leaves the
database, so a stolen cookie cannot be replayed off-host.
4. Rate limiting
Covered by the edge proxy. See the platform runbook.
0commits since
0pipelines read this page
A scanner's outputExample
$scan --all --format textexit 0
HIGH CWE-89 src/orders/query.ts:114
HIGH CWE-79 src/web/render.tsx:38
MEDIUM CWE-352 src/web/form.tsx:21
MEDIUM CWE-200 src/api/user.ts:73
LOW CWE-693 src/web/headers.ts:9
… 195 more
0findings
0say what already defends them
Last year's pen testExample
▣pentest-2024-final-v3.pdf1 / 48
WEB APPLICATION
PENETRATION TEST
Engagement 3–14 March 2024
Prepared for Payments Platform
Classification Confidential
0days old
0commits since
Put the same claim in the repository, and it arrives in review.
src/tui/commands.ts@@ -9,5 +9,7 @@+2
9 * @exposes #tui to #arbitrary-write [high] cwe:CWE-73 -- "/report, /sarif, /dashboard write files"
10 * @mitigates #tui against #arbitrary-write using #path-validation -- "Output paths resolved relative to project root"
+11 * @exposes #tui to #cmd-injection [high] cwe:CWE-78 -- "/annotate and /threat-report spawn child processes"
+12 * @audit #tui -- "Child process spawning delegated to agents/launcher.ts"
13 * @exposes #tui to #api-key-exposure [high] cwe:CWE-798 -- "/model handles API key input and storage"

Two lines in a pull request. The reviewer reads them next to the code they describe, the parser checks that #cmd-injection and #tui are declared, and the exposure stays open until a control answers it.

What that buys, and what it costs
The annotation language

Twenty verbs. Two of them close a finding.

GAL is a small grammar for security intent. It lives in comments, so it works in any language, and its one job is to make a claim about code checkable.

Browse the verbs by category

Or print the full reference in your terminal.

Relationships

Connect threats to assets. Record the controls and decisions that resolve them.

@exposesopens finding
Marks an asset exposed to a threat at this line. Every one creates a finding.
@mitigatescloses finding
Names the control that answers a threat on an asset, and closes the finding.
@confirmed
Records a threat as verified exploitable, with the evidence that proved it.
@acceptscloses finding
Records a decision to carry the risk instead, which also closes the finding.
@entitles
States an actor is entitled to a capability by design. An agent proposes; a person accepts.
@transfers
Moves responsibility for a threat to another asset or team.
Only @mitigates and @accepts close a finding.
What it produces

What comes out the other end.

One command turns the model into a self-contained HTML dashboard. The screens below are real output, and the dashboard itself is hosted on this site.

GuardLink dashboard executive summary: risk grade D, open threats, mitigation coverage, and a what-to-do-next list

Executive summary. A risk grade, the counts behind it, and what to do next — each item carrying the command that does it. This is the page a security lead opens.

Open this view
Coding agents

Your coding agent writes the annotations.

guardlink init detects your coding agent and sets up two things: an MCP server it can query, and a rule in its instruction file that tells it to annotate security-relevant code as it writes it.

Agents that guardlink init sets up
  • Claude Code
    CLAUDE.md + .mcp.json
    MCP + rule
  • Cursor
    .cursorrules + .cursor/mcp.json
    MCP + rule
  • Windsurf
    .windsurfrules + .windsurf/mcp.json
    MCP + rule
  • Cline
    .clinerules + .cline/mcp.json
    MCP + rule
  • Codex
    AGENTS.md
    rule only
  • GitHub Copilot
    .github/copilot-instructions.md
    rule only

28 tools the agent can call

Read the model, validate it, suggest annotations for a snippet, query threats by keyword, generate a report or a dashboard, export SARIF, or diff against a git ref. The agent asks “what threatens #api?” before it writes code that touches the API.

Two things it cannot do alone

An agent can find an exposure and propose an entitlement. It cannot accept a risk, or decide that a caller was always meant to hold a power: those two statements close a finding or excuse one. The MCP server’s annotate tools reject @accepts and @entitles. An entitlement goes to a person as a proposal, and an acceptance is written only with the name of the person who made the call.

In CI

A threat model that can fail a build.

GuardLink runs as a check. A new route with no annotations, or a control removed from under an exposure, shows up in the diff and can block the merge.

.github/workflows/guardlink.yml
- name: Install GuardLink
  run: npm install -g guardlink

- name: Validate annotations
  run: guardlink validate .

- name: Threat model diff
  run: guardlink diff --from origin/main --to HEAD

- name: Export SARIF
  run: guardlink sarif . -o guardlink.sarif

- uses: github/codeql-action/upload-sarif@v3
  with: { sarif_file: guardlink.sarif }

The full workflow, with PR comments and SARIF upload, ships as examples/github-action.yml. Multi-repo setups get two more in examples/ci/.

guardlink ci .
Unmitigated exposures: 14 (high 3, medium 6, low 5)
Anchor drift: 0 (no anchored @source blocks to check)
 
⚠ 14 unmitigated exposure(s):
#mcp → #cmd-injection [high] (src/mcp/index.ts:6)
#tui → #cmd-injection [high] (src/tui/commands.ts:11)
#agent-launcher → #prompt-injection [high] (src/agents/prompts.ts:6)
#mcp → #prompt-injection [medium] (src/mcp/server.ts:37)
#suggest → #dos [low] (src/mcp/suggest.ts:16)
…

Run on GuardLink’s own repository; nine more lines are cut here. It is advisory by default and exits 0; --strict makes it a gate.

New route, no annotations
The diff shows the gap before a reviewer has to spot it.
Control removed
The exposure reopens, and --fail-on-new blocks the PR.
Exposure accepted
Recorded under a person's name, not deleted.
Every command and flag
Get started

Model your first threat
in about ten minutes.

Node 18 or newer is the only requirement. init detects your agent and writes the config; everything after that is three commands.

Source
Prove it

A threat model says what's exposed. Testing says what's reachable.

GuardLink stops at the code: it never builds or runs your app. When you need to know which exposures an attacker can actually reach, that is what Bravos, Bugb's pentesting product, is for.

GuardLink, open source

What's exposed

Reads the annotations in your source and lists every exposure: the asset, the threat, and whether anything mitigates it.

Bravos, from Bugb

What's reachable

Attacks a running copy of your app with real logins, and ends every threat with a verdict and the reason for it:

  • confirmed
  • refuted
  • not tested

Not sure what GuardLink would find in your code? Send a public GitHub repository and the Bugb team runs GuardLink on it, then emails you the threat model it finds. Nothing is pushed to your repository.

Community

GuardLink is open source. A star helps it travel.

It costs nothing and takes one click, and it does two things for a project this size.

Other teams find it
GitHub search, topic pages and trending lists all weigh stars. Each one makes GuardLink easier for the next team looking for a threat model they can keep in their code.
The maintainers know it is used
Stars tell us people rely on it. Issues tell us what to build next, so if something is missing, an issue is worth even more than a star.

Other ways to help

19stars on GitHub today

Help us reach 25 stars.

19 of 25. The goal is the next round number, and moves when we pass it.

Star on GitHub

Opens the repository. The Star button is at the top right.