NEWBravos is availableCreate your account

bravos

Shift-left security that runs with every commit.

Threat model, code graph, SAST and an agentic pentest on every pull request, ending in a test that guards the fix.

pipx install bravos
Add order export pinecart · #482running
AssetsThreatsControls#auth#checkout#orders-api#uploads#support-agent#broken-auth#csrf#idor#sqli#ssrf#prompt-inj…#session-rot…#csrf-token#prepared-st…#approval-gate@exposes #orders-api to #idor

+1 exposure declared on the new route: #orders-api → #idorEvery risk mapped, with 5 still open

02 · Threat modelGuardLink

Every risk, mapped where it lives.

Written next to the code, drawn four ways.

Assets 8Open 5Mitigated 7Controls 7
ASSETSTHREATSCONTROLS#auth#checkout#orders-api#uploads#webhooks#admin#support-agent#broken-auth1 exp · 0 open · 1 mit#credential-stuffing1 exp · 0 open · 1 mit#csrf1 exp · 0 open · 1 mit#idor1 exp · 1 open · 0 mit#sqli1 exp · 0 open · 1 mit#path-traversal1 exp · 0 open · 1 mit#ssrf1 exp · 1 open · 0 mit#webhook-forgery1 exp · 0 open · 1 mit#stored-xss1 exp · 1 open · 0 mit#secret-leak1 exp · 1 open · 0 mit#prompt-injection1 exp · 1 open · 0 mit#excessive-agency1 exp · 0 open · 1 mit#session-rotation#rate-limit#csrf-token#prepared-stmts#path-normalise#hmac-verify#approval-gate
12 of 12 exposures and 7 mitigations drawnopen, by severitymitigatedhover or tab to tracepinecart · sample
5 open7 mitigated8 assets
Threat graphEvery risk tied to the part of the app it threatens, and the fix that covers it.
21 flows14 cross a trust boundary
Data flowData traced from the outside world, through your app and out again.
4 zones14 crossings, each marked
Trust zonesWhere data moves from one zone of trust to another, the crossing is marked.
4 direct8 two hops out
NeighbourhoodPick one asset and see everything it talks to, two hops out.
1 / 4

03 · Code graph

Your codebase as a map. Every route to what it touches.

Lens
Where in this codebase is the risk?
API64orders/checkout/auth/admin-api/AGENT18WORKERS27refunds/exports/mail/DATA21models/migrations/WEB112storefront/admin/components/LIB41lib/TEST88tests/CONFIG6config/
5Open exposures5 files
7Controlsdeclared mitigations
21On a flowdeclared data flow
4Hotspotsby commit heat
18Entry pointsroutes and tools
8 layers · 20 folders · 377 filesopen exposure, by worst severityhover or tab to a folderpinecart · sample
377 files5 folders with an open risk
MapYour whole codebase on one screen. Risk shows up as heat.
15 pairs11 with no import between them
CouplingFiles that change together, linked, even where no import joins them.
18 ways in11 reach a risky call
Attack surfaceEvery way in, ranked by the risky calls it can reach.
2 unapproved (dashed)1 needs a person's sign-off
Agent reachWhat your AI assistant can do, and which of it nobody approved.
1 / 4

04 · SAST

Findings, sorted by what can reach them.

Gatefail
Fail the gate0
Warn0
On a path from an entry0of 61

Deterministic analysis, placed on the code graph. Nothing is sent anywhere.

Issues by evidence tier
T1 proven0, 0 reachable
T2 corroborated7, 4 reachable
T3 plausible11, 5 reachable
T4 hygiene29, 10 reachable
T5 set aside14, 4 reachable
Narrowed by reach
  1. 61all findings
  2. 23on a path from an entry point
  3. 9corroborated or plausible
  4. 2fail the gate
Issues
  • User-controlled URL reaches http.getsrc/uploads/import.ts:31via POST /uploads/importfail@exposes #uploads to #ssrf
  • No ownership check before db.orders.findAllsrc/api/orders.ts:42via GET /orders/exportfail@exposes #orders-api to #idor
  • Unescaped HTML rendered in admin notessrc/admin/notes.tsx:66via POST /admin/noteswarn@exposes #admin to #stored-xss
  • Model output passed to a tool uncheckedsrc/agent/tools.ts:19via tool: send_emailwarn@exposes #support-agent to #prompt-injection
  • Signature compared with ==src/webhooks/payments.ts:14via POST /webhooks/paywarn
  • Path built from the upload filenamesrc/uploads/store.ts:27via POST /uploads@mitigates … using #path-normalise
  • Secret read from env written to a logsrc/admin/settings.ts:18no entry reaches itwarn@exposes #admin to #secret-leak
  • Dependency with a published advisorypackage-lock.jsonno entry reaches it
  • Hard-coded key in a test fixturetests/fixtures/pay.ts:3no entry reaches it
  • Regex with a nested quantifiersrc/lib/slug.ts:9no entry reaches itreview: noise

showing 10 of 61 · sample

T1 provenT2 corroboratedT3 plausibleT4 hygieneT5 set asidepinecart · sample
Narrowed by reachSixty-one findings, narrowed to the two that need you today.
23 of 61 on a path from a way in
Placed on the code graphEach finding comes with the route that reaches it, and the risk it matches.
Deterministic. Nothing is sent anywhere.
The gateThe two that matter fail the gate and hold the merge. The rest warn or wait.
1 / 3

05 · Proven

Then it tests a running copy, and keeps the proof.

This run, accounted for

12 tracked · 0 reached a verdict

0proven0needs review0unverified0couldn’t test0cleared
  1. Round 1unauthenticatedqueued
    0/14 probes3 cleared
  2. Round 2customerqueued
    0/19 probes2 proven1 needs review4 cleared
  3. Round 3adminqueued
    0/14 probes1 cleared1 couldn’t test
The proof it keeps
0request and response pairs, one per proven finding
0Cert-X-Gen tests written, ready to replay
0@confirmed lines written back next to the code
0probes, every one inside the plan you approved

“Couldn’t test” is listed, not hidden. Nothing found never means not tested.

3 rounds · one lens each · 47 probesnumbers only · sample
47 probesevery one inside the plan you approved
Three rounds, three rolesIt tests a running copy of your app, signed in as each kind of user.
2 proven1 review1 couldn’t test8 cleared
A verdict for every riskEach tracked risk ends with a verdict. What couldn't be tested is listed, not hidden.
2 proofs2 tests saved
The proof it keepsEach proof is kept: the request, the response, and a test that replays it.
1 / 3

06 · Tests in CI

Each proof becomes a test that guards the fix.

The same two tests run on every pull request from now on.
Fix order ownership and import allowlist#487
src/api/orders.ts
41  const orders = await db.orders.findAll({42+   where: { customerId: session.user.id },43  })

bravos high #orders-api → #idor — confirmed (replay) — still exploitable

Some checks were not successful
  • Failed: bravos security gatefailed · 2 breaches
  • Failed: bravos replay2 stored tests · 2 still exploitable
  • Passed: guardlink validatemodel current
Job summary
severityasset → threatverdictlocationoutcome
✗ bravos security gate — failed · 2 breach(es) · 2 confirmed of 12 tracked
high#orders-api → #idorconfirmed · replaysrc/api/orders.ts:42still exploitable
high#uploads → #ssrfconfirmed · replaysrc/uploads/import.ts:31still exploitable
$ bravos replay  replaying 2 stored tests from the ledger  ✗ #orders-api → #idor      still exploitable   200 OK, not their order  ✗ #uploads → #ssrf          still exploitable   internal host reached$ bravos ci --reporter github  ✗ bravos security gate — failed · 2 breach(es)
Where it shows up
Terminalbravos ci exits non-zero on a breach
Your editorgutter marks, CodeLens and Problems, by evidence tier
GitHubannotations on the line, job summary, PR comment
Before the fixBravos replays both proofs on the pull request. They still work, so the gate fails.
After the fixThe fix lands. Both attacks are now blocked, and every check passes.
2 tests, on every pull request
On every pull requestFrom now on the same two tests run on every pull request, so the bug can't come back.
1 / 3

How it fits

What Bravos provides, and what you bring.

CONSTANTCHANGES OVER TIMEPROVIDED BY BRAVOSdrivesSURFACEreads / writesSHARED CONTEXTruns onEXECUTIONreasons withAI MODELStests · writes backCODE & TARGETSbravosORCHESTRATOR

Constant · provided by Bravos

Shared context

The state every run reads and writes back.

  • GuardLink threat model
  • Threat ledger
  • Template registry
  • Code graph

commandsannotateledgertemplategraphdiffsast

  • Constant, provided by Bravos
  • Changes over time, you bring it
  1. 01AnnotateThe threat model, written into your code.annotate
  2. 02PlanA brief becomes a scan plan you approve.intake · plan
  3. 03PentestProbes run in a disposable copy, with real logins.run · auto
  4. 04LedgerEvery result is recorded; stored probes replay.ledger · replay
  5. 05Write backVerdicts go into the code; CI gates on them.ci · diff

bravos

Secure your next pull request.

pipx install bravos
Install in your editor
VS Code
code --install-extension bravos.bravos
Cursor
cursor --install-extension bravos.bravos
Windsurf
windsurf --install-extension bravos.bravos

Or search “Bravos” in the Extensions panel of any VS Code fork. The identifier is bravos.bravos, on the VS Code Marketplace and Open VSX.

Built on GuardLink, threat model as code, and Cert-X-Gen, pentest as code. See licences