Run SAST
bravos sast run analyzes a repository with bravos-cli, the deterministic
analyzer bravos setup staged. It runs Semgrep rules, request-to-sink taint
over the code graph, dependency advisories with whether the vulnerable function
is reached, secret scanning and IaC checks. It reads the guardlink threat model
as input, so each finding is joined to the claim it supports, or listed as a
lead the model is missing.
SAST is paid, with a 30-day no-card trial.
Before you start
Section titled “Before you start”- A licence on this machine, and the analyzer staged:
bravos setup --checkshows✔ analyzer. - GuardLink on
PATH:bravos setup --checkshows✔ guardlink · in-session probe translation. Bravos 1.5.3 checks it before starting the analyzer. If it is missing, runbravos setup --tools -yand retry. - Annotate first. The analyzer’s recall comes from the threat model. See Annotate a repository.
- Build the code graph for this commit, so findings carry reach. See Ask the code graph. Without a graph the scan still runs, without reachability, and says how to add it.
Run it
Section titled “Run it”bravos sast run --timeout 90SAST run 20261112-065035: bravos-cli exited 0 (0 clean at the level, 1 findings, 3 licence refused or analysis failed, 4 a stage failed)Truncated to the analyzer exit line. The parenthesis is the analyzer’s legend;
this run exited 0 and still produced findings to inspect. Results are written to
.bravos/sast/<run>/, which is gitignored. The full analyzer stderr remains
in analyzer.stderr.log, and failures keep their diagnostic cause in the CLI
output. Inspect those diagnostics before interpreting an empty candidate
list. --scope and --exclude take comma-separated paths; .bravos/ is excluded.
The Bravos 1.5.3 capture uses a separate audit-notes fixture with two Flask
routes, declared SQL/shell exposures and flask==2.0.1, using the signed
analyzer 0.6.2 bundle. The timeout bounds this documentation check.
One line for the newest run
Section titled “One line for the newest run”bravos sast statusSAST: 4 findings (0 reachable, 0 not in the threat model) at 9ee7e12f4, current — bravos sast candidatesThe agent’s briefing and bravos watch show the same line.
Read the candidates
Section titled “Read the candidates”bravos sast candidatesSAST run 20261112-065035 at 9ee7e12f4 (reachable only; --all for the rest)
Joined to a claim (0) — taint evidence for bravos-templatize and bravos-prove:
Not in the threat model yet (0) — leads for bravos-annotate:
Dependencies (0):
Secrets (0) — rotate; never pentest candidates:By default only reachable findings are listed. This capture’s default list is empty while the run contains four findings. Reachability that is not established must remain unknown; an empty filtered list is not a clean scan.
--all adds findings whose reach is unknown or false:
bravos sast candidates --allSAST run 20261112-065035 at 9ee7e12f4
Joined to a claim (2) — taint evidence for bravos-templatize and bravos-prove: warning semgrep.python.sql.python_sql_rule-hardcoded-sql-expression app/server.py:15 [#api → #sqli at app/server.py:11] error semgrep.python.exec.python_exec_rule-subprocess-popen-shell-true app/server.py:21 [#api → #cmdi at app/server.py:17]
Not in the threat model yet (0) — leads for bravos-annotate:
Dependencies (2): error bravos-deps-osv-PYSEC-2023-62 requirements.txt:? vulnerable function reached: undetermined (no affected symbols) warning bravos-deps-osv-PYSEC-2026-2151 requirements.txt:? vulnerable function reached: undetermined (core path)
Secrets (0) — rotate; never pentest candidates:With --all, two findings join to the declared exposures and two dependency
advisories appear with undetermined vulnerable-function reach. The output does
not establish a live exploit or a verified control. A joined finding is taint
evidence; reproduction needs a separate authorized run.
--json prints the candidates as bravos.sast-candidates/v1.
Let the agent do it
Section titled “Let the agent do it”Ask “run SAST”, or run /bravos-sast. If the files in scope are unannotated,
the skill runs bravos-annotate on them first. It then runs the scan and sorts
the candidates: a finding joined to an open exposure goes to
bravos-templatize, whose template can target its route and taint path; a
reachable finding not in the model goes to bravos-annotate; an unreachable one
is reported as such; a secret is reported for rotation.
Without a licence
Section titled “Without a licence”bravos sast run needs a licence — it is on the Team plan: bravos account activate starts a 30-day trial (no card), or https://account.bugb.io/pricing- Run Bravos in CI/CD
- Reading a finding: what each state word means.

