What Bravos is made of
Two engines sit under Bravos, and neither is hidden.
- guardlink holds the threat model. It is the annotation language the model is written in, the parser that resolves it, and the SARIF export a probe is generated from.
- cert-x-gen executes the probes. Every request Bravos sends, it
sends through
cxg pentest.
Both are documented on this site, both work on their own, and wiring them together is one file: the SARIF that passes between them.
So the useful question is not “why Bravos” but “what does the handoff not answer”. This page is that list, and it starts with the case for not using it at all.
When running them yourself is right
Section titled “When running them yourself is right”- You want one answer about one thing. A single exposure, a single probe.
cxg pentestagainst a hand-written scope is faster and you can read every step of it. - You do not have identities. Much of what Bravos adds is about who is attacking, and a target with one login or none gets little of it.
- You cannot let an agent edit the repository. Bravos’s annotate and write-back phases are agent-driven by design. If that is not acceptable, guardlink’s own workflow annotates without one.
- You need to understand the tools before you trust the loop. Run both by hand first. The loop is easier to read once you have seen its parts fail.
Four questions the handoff cannot answer
Section titled “Four questions the handoff cannot answer”What did this round actually add?
Section titled “What did this round actually add?”guardlink identifies an exposure by asset::threat. Two different weaknesses in
the same pair are one identity, so guardlink diff cannot see the second one
and the round reads “nothing added”.
bravos inspect reports the collision rather than leaving you to discover it.
On a small repository with three exposures across two pairs:
PAIR COLLISIONS (1 groups, 1 findings maskable) Distinct exposures sharing one asset::threat pair. `guardlink diff` keys on that pair alone, so discovering another member of a group reports 0 added — a false dry round. orders-dao::sqli ×2 critical app/data/orders-dao.js:4 critical app/data/orders-dao.js:8Bravos keys its own ledger on asset, threat, file and a hash of the message, which is what lets a round say what it actually added.
What did a @mitigates delete?
Section titled “What did a @mitigates delete?”One @mitigates or @accepts anywhere removes every exposure sharing its
pair from the export. cxg never sees them, so no probe is generated, so nothing
can confirm or refute them. Nothing warns you.
SUPPRESSED FROM EXPORT (1) A @mitigates or @accepts anywhere in the repo removes every exposure sharing that pair from the SARIF export. cxg never sees these and cannot test them. high orders::xss ×1 via @mitigatesBravos seeds those into the ledger as suppressed so they are counted rather
than absent, and the mitigation-audit lens
restates them into the export so the control itself gets attacked. A control
that is 90% correct is more dangerous than none, because it ends the
investigation silently.
Was anything tested at all?
Section titled “Was anything tested at all?”cxg exits 0 for a clean scan, for a scan that skipped every probe for lack of
identities, and for a scan aborted partway. A scan that stopped early writes a
report whose findings list is indistinguishable from a clean one’s.
Bravos reads the report body as well as the exit code, and the three places cxg says its own scan did not finish. Only a conclusion may move a verdict, so a threat whose probe never reached the target stays in the backlog instead of being counted as fine. That distinction is the whole of Verdicts and the ledger.
Who was logged in, and are they still?
Section titled “Who was logged in, and are they still?”cxg’s own login flow assumes a shape many real targets do not have. Bravos
captures sessions with its own headless login, driven by an
environment recipe: one target
authenticates at POST /login with {userName, password}, another at
POST /api/auth with a JWT in the body and a CSRF token in a response header.
When a session expires mid-run it re-authenticates itself, and pauses only when
a login genuinely needs a human.
It also keeps the state that makes a second run cheaper: which threats are already confirmed, which probe proved each one, and which lenses have stopped producing.
What it costs
Section titled “What it costs”- Wall-clock. A verify run is measured in tens of minutes per round, most of it generating and running probes.
- Agent calls. Annotation runs every round, and enrichment and write-back are agent calls too. Running the tools by hand costs you none of that.
- A dependency on both products at once. A run is only as current as the cxg and guardlink it drives. It pins their orchestrator files and refuses to resume across drift, which is protection that shows up as friction.
- More output to read. A
cxg pentestreport is one artifact. A run leaves a ledger, a summary, per-finding advisories, a triage file, an event log and the probes themselves.
It notices when they move
Section titled “It notices when they move”guardlink and cert-x-gen ship on their own schedules, and every reader Bravos has of their artifacts is a hand-written pick of named keys. A key that grew, or a block written in a new shape, is not an error: it is never seen at all, and the nothing that comes back reads as a clean result.
So Bravos declares what it needs of each artifact and takes a census against the one that actually arrived. A field nothing consumes, a value it has no meaning for, a value it refused, or a question the artifact never answered is listed in the run’s health banner in the dashboard, and the counts are marked as a floor rather than a failure. A tool that grew a field has not made the findings wrong, only Bravos’s reading of it incomplete.
Related
Section titled “Related”- The verification loop: what the loop is doing with those two tools.
- Feeding findings into cxg: the handoff, from guardlink’s side.
- Authorization and blast radius: what you take on by automating it.

