Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Bravos — threat modeling that proves itselfNew to Visual Studio Code? Get it now.
Bravos — threat modeling that proves itself

Bravos — threat modeling that proves itself

Bravos

|
2 installs
| (0) | Free
Threats are annotated in your code, then attacked until each one is confirmed, ruled out, or shown to be by design.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Bravos

Your codebase already knows what can go wrong with it. Bravos writes that down, then proves which parts are real.

AI made it cheap to generate security findings. It did not make them true. Teams now drown in reports that are plausible, unverified, and mostly not worth fixing.

Bravos does the opposite. It finds fewer things and proves each one — by attacking your running application until the threat either succeeds or is ruled out. What comes back is not a list of maybes. It is a short list of what is actually exploitable, what is working as designed, and why.

The Bravos sidebar — Overview, Findings and Code graph — beside the Map page, which shows a repository as layers marked by open exposures

The Agent reach tab: each AI agent and principal, the capabilities it can reach and the assets behind them, with each line marked entitled or not and gated or not


1. Your threat model lives in your code

Most threat models are a document someone wrote once. It was accurate for about a week.

Bravos keeps the threat model in the source, as code. An AI reads your repository and writes what each part exposes, what mitigates it, which assets and trust boundaries exist, and which feature the code belongs to — as ordinary comments, right above the lines they describe. Your codebase becomes the single source of truth for its own risk.

Because it is code, it is reviewed in pull requests, diffed, and versioned like everything else. And because every change gets read and annotated again, it cannot quietly go out of date. That is what continuous threat modeling means here — not "runs on a schedule," but "cannot go stale."

Every threat is recorded as a hypothesis. Not a bug. Not a claim. Something that looks dangerous and has not been tested yet:

# @exposes db.users to #sql-injection [high] cwe:CWE-89 -- "the query is built by
#   concatenating a caller-supplied name"
def find_user(name):
    return db.execute("SELECT * FROM users WHERE name = '" + name + "'")

That is honest, and it is where most tools stop and start emailing you.

2. A polyglot exploitation framework

A hypothesis is worth very little. That SQL injection may be unreachable — perhaps nothing user-controlled ever arrives at that function. Real weakness, zero exploitability. Any tool that reports it as a vulnerability has just wasted your afternoon.

So Bravos tries to exploit it, using cert-x-gen — an exploitation framework driven by templates written in whatever language suits the target. JavaScript for a web application, something else for a protocol or a binary. Use what fits.

The templates are not guessed in a vacuum. Cert-x-gen reads the annotations, parses them, and works from the same understanding of your codebase that produced them. Then an AI writes an exploit for that specific threat — and if the first attempt fails, that is not the answer. It mutates the approach and tries again, working toward the goal, the way a person would.

Getting that to work is mostly orchestration, which is the unglamorous part that decides whether any of it is real: standing up an environment, running Docker, capturing credentials and refreshing them when they expire, choosing which identity makes a given exploit meaningful, rotating through attack perspectives, and recording everything it tried.

What you get is a record, not a verdict handed down: which attack was attempted, under which identity or anonymously, which layers of your code it got through, and exactly where a control stopped it or where it went all the way.

3. Triage: the question nobody automated

Proving something is exploitable does not make it a vulnerability.

Everyone in your company can read everyone's chat. A probe will prove it. It is also exactly how the product is supposed to work. That is a business decision, not a breach.

This is the wall bug bounty programs are hitting. AI made it cheap to write a convincing report, so reports arrive by the thousand, and triage — done by humans, one at a time — became the bottleneck. Programs get paused not because the findings are all worthless, but because nobody can work through the queue fast enough to find the ones that aren't.

Bravos validates against the code itself. The threat model and the means to test it already live in your repository, so an incoming report becomes something you pipe in and get an answer to: does it reproduce, which privilege did it need, and was that privilege already entitled to that effect?

That last question is the one that gets findings rejected. Bravos asks it explicitly, and when the answer means "this is by design," you say so in the code, where it stays — an @entitles line declaring that a role is meant to have a capability, citing where that intent is written down.

An entitlement argues about reporting only. It never hides anything from the exploitation run: the probe still fires, the evidence is still collected, and the threat still appears in the model. It changes what the finding is called, not whether it was tested.

Some threats can never be waved away this way — anything about one user reaching another user's data, tenant isolation, ownership. Bravos refuses to downgrade those on entitlement grounds, because "the admin is allowed to" is not an answer to "user A read user B's messages."

4. It does not stop

Fix the issue and push. The new code is read and annotated again.

If your fix worked, the threat is marked mitigated. If your fix introduced a new one, that is annotated too — immediately, in the same pass. Then the cycle runs again. Threat model, prove, triage, fix, push, re-annotate. Continuous threat modeling and continuous exploitation, running wherever your CI/CD does.

This matters more than it sounds, because the annotations are ordinary comments in your source. Any AI agent working in your codebase reads them. It sees a threat marked on the function it is about to touch, and it has the context to fix it rather than route around it — and if that fix opens something new, the next pass catches it.

The threat model is never a document that went stale. It is the current state of your code.

Where this is going

Not built yet — described here so you know the shape of the thing:

Because guardlink annotates by feature, and features map to the people who build them, a proven finding can be routed to the developer who owns that code — with integrations that open the ticket in whatever system you already use. Triage that ends in the right person's queue instead of a security backlog.


What you see in the editor

This extension is where you watch the loop and drive it. It is a thin client: it detects a locally installed bravos engine and drives it, and never embeds Python, guardlink, cxg or Docker.

The sidebar — the Bravos icon in the activity bar opens three views, top to bottom:

  • Overview — the flow, in the order you would take it: set up, map the code, model the threats, analyse the code, prove what is real, decide and gate. Each stage says where this repository stands and offers its next step as a row you click. Nothing in it sends traffic except the one row under Prove, and that row says so.
  • Findings — every repository the engine has scanned. Under each one: every exposure in the threat model, each marked tested or not tested with the reason the engine recorded; and the advisories, split into verified (a probe proved it exploitable) and closed (a probe ran and the exploit failed, or a control held). Select any of them to jump to the line it is about. When the code embeds an AI agent, an Agent reach section follows — see Agent reach.
  • Code graph — whether this repository is indexed, the file you have open (what depends on it, who owns it), and every question the graph can answer. See the code graph.

In your code — annotated lines are marked as you read them.

  • @exposes — a hypothesis, recorded but never tested. Hollow gutter mark, amber badge.
  • @confirmed — a probe proved it exploitable. Filled gutter mark, red badge.

Hovering gives you the asset, the threat, the severity, the verdict and the reason. Each @exposes line also offers verify this is exploitable →, which is locked in the model tier and unlocks with the verify tier, behind an explicit attestation.

This works however your repository is annotated — inline comments and out-of-source .guardlink/*.gal files both decorate the code line they describe.

Code analysis (Bravos: Code analysis findings) opens a stored analysis as attack surface → handler → finding: the path from something an attacker can send to the line that mishandles it. Findings the graph looked at and could not reach are a group of their own, and findings the graph had no answer for are a third — never folded into the second, because "nothing reaches this" and "we could not tell" are different claims. Producing an analysis needs the licensed analyzer; reading one does not, so a report your CI produced opens here on any machine.

Results — one editor tab with five tabs across the top: Findings, Report (the whole threat model, diagrams included), Dashboard, Code analysis and Agent reach. The Findings view's title bar has one Results button; Bravos: Open report, Bravos: Open dashboard, Bravos: Code analysis findings and Bravos: Agent reach still work from the palette, and each opens its own tab of the same panel, so you never have five tabs that might each be showing a different run. Guardlink's own dashboard stays a panel of its own, because it needs no bravos run at all. Advisories open the entire finding: the write-up, its CWEs and CVSS, a Reproduce panel with the exact cxg template that tested it so you can rerun it deliberately, and the full evidence trail — every request fired, what came back, and what it proved.

Agent reach

Code that embeds an AI agent hands it capabilities: a tool that runs SQL, a refund it can issue, a file system it can write. guardlink records that in the same annotations as the rest of the threat model — @agents and @reaches for who can invoke what, @effects for what that code does to an asset, @entitles for what an actor is meant to have, @gates for a human approval in front of a write — and answers two questions about it: which reaches no entitlement covers, and which writes no gate stands in front of.

The Findings view lists each agent and principal with its reaches, then the unentitled reaches and ungated mutations, each with guardlink's own reason; every row opens its annotation. The Agent reach tab (Bravos: Agent reach) is the dashboard's panel, with the reach map: who → what they can reach → which asset, each line drawn as entitled or not, gated or not. These are review items, never findings — an unentitled reach by an agent is OWASP LLM06 Excessive Agency to look at, not a vulnerability anybody proved, and nothing here joins a finding count.

It needs bravos 0.4.0 or newer and a threat model built with guardlink 2.2.0 or newer. With an older engine, or a model an older guardlink exported, the tab says which and what to upgrade.

The code graph, in the editor

Where to find it. The Code graph view in the Bravos sidebar, ⌥⌘G (Ctrl+Alt+G on Windows and Linux) to jump to it, the graph button on the Findings view's title bar, and right-click › Bravos in the editor and the explorer. ⌥⌘U (Ctrl+Alt+U) asks what depends on the file you are editing. Both keys were checked against VS Code's own default keymap, and the check runs again every time the extension is exercised in an editor. On Windows and Linux, Ctrl+Alt is AltGr, and some keyboard layouts type a character with AltGr+U or AltGr+G; if yours does, rebind the shortcut in Keyboard Shortcuts (search for Bravos) — like every VS Code shortcut, it is yours to change.

The code graph ships in the bravos wheel. It needs no licence and no annotations — index once with Bravos: Index this repository for code search (or bravos graph build .) and ask it questions. On this repository that is 13,732 nodes and 52,679 edges across 475 files, in 31 seconds.

A graph is addressed by the commit it was built from, so editing does not disturb it and committing does. When that happens the panel says so and offers to re-index — it does not tell you that you have never indexed the repository.

There is no show me the call graph. A picture of thirteen thousand symbols is not a picture. Every symbol-level command opens on a question, seeded by something you already named, and the canvas grows only where you click it. The one view of the whole repository is the Map (below), and it is a map of layers, folders and files that opens on the layers — not a picture of every function.

Command The question
Bravos: Blast radius — what depends on this file I am about to edit this; what does it touch? Seeded from the file you have open. One button flips it to what the file is built on.
Bravos: What calls this, and what it reaches Callers on the left, callees on the right, the symbol under your cursor in the middle.
Bravos: What nothing reaches Dead code, contradictions first — anything unreferenced that the threat model speaks about.
Bravos: What this branch touches Every file changed against the default branch, and what depends on each.

Breadth folds; it is never trimmed. src/bravos/paths.py has 146 direct dependents here. The first screen draws nine directory groups and one loose file — twelve boxes, all labelled, all on screen — and each group opens on a click with no further query, because its members travelled with it. Press Open all 9 groups and you get all 148, still labelled, and you pan. A file you named yourself is never folded away.

Doubt is drawn, not omitted. The graph filters call edges at confidence 0.5 and tells you how many recorded callers that floor excluded; it also publishes how much of its own edge table never resolved to a target (52% of this repository's reference edges). So the canvas has three strokes and they never look alike:

  • solid — the graph returned this edge.
  • dashed — recorded, and below the confidence floor. Counted beside the node; the graph publishes the count, not the rows, so these are never given names they do not have.
  • dotted — a call or import site the indexer could not place at all.

Where the graph publishes a per-row confidence — the dead-code detector does, 0.1 to 0.9 — it is on the node as a chip. Where it publishes none, there is no chip. An empty answer says which kind of empty it is: nothing calls this and the threshold excluded four things are different sentences.

The Workbench's pages

The Bravos Workbench's code-graph pages, in the editor — from the Code graph view's Pages, the Overview, or Bravos: Open a code graph page…. One panel, a strip across the top, and each page prints the bravos graph commands it asked, so you can ask the same questions in a terminal.

Page The question it answers Asks
Map Where in this codebase is the risk? The whole repository as layers › folders › files, marked by open exposures, churn, ownership or data flow. Double-click to descend; a file offers open, what depends on it and who owns it. codemap
Owners & History Which security-relevant files change fast and have one keeper? Heat × bus factor, who to ask about a file, the controls nobody still committing has touched — and what kind of work the team does: the commit mix, month by month, and a contribution calendar. hotspots, ownership, commit-mix, codemap
Coupling Which files change together in git with no import or call between them — coupling the code does not show, and the pairs that touch an open exposure. co-change, codemap
Trust Can I believe this map? What the graph drew, what it refused to draw, what it could not read, and whether it still matches your working tree. codemap, threat-model

The pages draw the Workbench's own models, their code copied unchanged, so the two products agree about every number; nothing is trimmed (the Map asks for every file, and folds as you zoom — on a 9,234-file repository the first screen draws 9 cards). A Findings lens is shown unavailable rather than as a clean map, because the graph is not given scan findings yet; the Workbench's Architecture, Triage and Third-party pages need engine work first.

Driving it from a coding agent

Ask your agent a question about the code, and the picture in your editor can become that query's answer — one surface instead of two. Your agent writes {tool, arguments, note} to .bravos/graph-question.json, or calls the bravos.showGraphQuestion command.

It names the question; it never supplies the answer. The editor runs that question against your own graph, through the same bravos graph your own commands use, and draws what comes back. There is no field by which a caller can hand the page nodes, edges or a result. The panel says an agent chose the view, quotes what it said it was doing, and shows the exact query in one click.

The questions are whatever the graph publishes — sixteen today, asked live at the moment the intent arrives, so one the graph learns is answerable the same day. An ask that needs a join across two of them, a filter on a field no argument exposes, or more than one seed is refused with that live list rather than approximated: there is no free-form query tool.

Start here

  1. Install the engine and guardlink (see Requirements).
  2. Run Bravos: Check environment from the command palette. It reports which tiers this machine can run and exactly what to install for the rest.
  3. Map the code: Bravos: Index this repository for code search, or the first row of the Code graph view. Free, about half a minute, and it needs no annotations.
  4. Get the threat model built:
    • Bravos: Annotate this repo has an agent write the annotations into a repository that has none.
    • Bravos: Analyse this repository turns those annotations into findings, a report and a dashboard — no target, no probes, no traffic.
  5. Open the Bravos view and read the findings. The Overview view says what comes next.

What building the threat model writes. It sends nothing anywhere, but it is not read-only: it publishes the model into .guardlink/ (which the code graph reads) and refreshes guardlink's agent instruction files — CLAUDE.md, AGENTS.md, .github/copilot-instructions.md and the others guardlink keeps, created where missing — so the next coding agent opened in the repository reads the model as it stands. The first time in each repository, Bravos asks, and offers to build without writing anything; when it finishes it lists every path it wrote, with a button to the changes. It stays on your branch and commits nothing. (The bravos model CLI verb, left to itself, moves the checkout onto a bravos/run-<id> branch; the editor always passes --no-checkpoint.)

Four capabilities

Capability What it does Needs
Model annotate, threat model, report, dashboard — sends no traffic at anything guardlink + the bravos engine
Verify live probes that prove exploitability — real traffic, behind explicit consent the above, plus cxg, Docker, and a coding-agent CLI
Graph index a repository and ask it questions — who calls this, what a change would touch, what is dead nothing extra: it ships inside the bravos wheel, needs no licence and no annotations
Code analysis deterministic analysis joined to the graph, with its own report, policy file and gate the licensed analyzer. Reading a stored analysis needs nothing

The model tier is safe to run against anything you can open. The verify tier attacks a running target, so it asks you to name that target and type an attestation saying who authorised the test.

Bravos: Check environment prints this table as your machine answers it, with the exact command for anything that is missing. It is the engine's own answer, not the extension's guess — so a capability added to the engine shows up here without the extension being updated.

The code graph

Free, and the thing most likely to earn its place in a working day. Index once — Bravos: Index this repository for code search — and then ask it things:

  • Bravos: Ask the code graph… lists what this repository's graph can be asked, with the graph's own description of each question. Today that is twenty: callers, callees, impact, dependencies, call chains, dead code, hotspots, ownership, co-change, commit mix, the code map, structure, search, and the security-model questions. The Code graph view lists the same questions, read from the graph each time it refreshes.
  • Bravos: Ask the code graph about this symbol… — also under right-click › Bravos — starts from the symbol under your cursor. Who owns this file asks who has worked on the file, how much of it each of them carries, and whether they are still active in the repository.
  • Bravos: Open a code graph page… opens the Workbench's Map, Owners & History, Coupling and Trust pages over the same answers — also from the Code graph view's Pages and the Overview's Open the Map.

The list is read from the graph every time it is opened, so a question the graph learns is in the picker the day it lands. A name several symbols carry is refused with its candidates, and you pick which one you meant rather than getting a confident answer about the wrong function.

Bravos: Who signed off which risk, and when it lapses is the acceptance register: every @accepts, the human who signed it, and how long that signature has left.

Your code stays yours

The extension runs everything locally, talks to the engine over 127.0.0.1, and has no telemetry and no account. Its panels load no fonts, scripts, or assets from the network — everything ships inside the extension. Run state is read through the engine's read-only API rather than off disk.

Probe evidence is captured traffic from a system under attack, so the extension treats all of it as hostile: every URL, payload and response is displayed as inert text. A payload containing markup is shown exactly as it was sent, and does nothing.

The extension sends your code nowhere. The only thing that generates traffic is the verify tier, at the target you name, after you attest.

Requirements

This extension needs the Bravos engine on your PATH. It is a thin client and does nothing without one:

pipx install bravos          # or: pip install bravos
npm install -g guardlink   # maintains the annotations

The engine is free to install and needs Python 3.11 or newer. If it lives somewhere unusual, point bravos.enginePath at it. The verify tier additionally needs cxg and Docker, plus one coding-agent CLI — claude, codex or gemini — or your editor's own model, which Bravos will borrow if it is available. No other AI extension is required.

Bravos: Check environment tells you what this machine has and what to install for the rest.

Settings

Setting Default Meaning
bravos.enginePath bravos Path to the Bravos engine.
bravos.model (ask once) The model used for annotation and for the judgment inside a scan. Set it with Bravos: Select model.
bravos.providerMode auto Which providers that picker may offer — this editor's models, installed CLIs, or both.
bravos.targetUrl (empty) Pre-fills the target for a verify run. Verify always asks and always shows the target before it runs.

The annotation colours are contributed as theme colours (bravos.exposesBackground, bravos.exposesForeground, bravos.confirmedBackground, bravos.confirmedForeground), so workbench.colorCustomizations can tone them down like any other.

Built on

Two open-source tools, both public:

  • guardlink — the annotation language and parser that keeps the threat model in your codebase.
  • cert-x-gen — the polyglot exploitation framework (cxg) that proves a hypothesis is real.

Support

Documentation: docs.bugb.io/bravos. Issues, questions and feedback: bugb.io.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft