@bugb-technologies/bravos 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +70 -0
- package/README.md +225 -2
- package/dist/account.js +140 -0
- package/dist/artifacts.js +450 -0
- package/dist/ci.js +52 -0
- package/dist/cli.js +963 -0
- package/dist/codegraph.js +268 -0
- package/dist/cursor.js +70 -0
- package/dist/detect.js +26 -0
- package/dist/doctor.js +99 -0
- package/dist/global.js +95 -0
- package/dist/graphbin.js +136 -0
- package/dist/guardlink.js +123 -0
- package/dist/guide.js +255 -0
- package/dist/help.js +334 -0
- package/dist/hooks.js +167 -0
- package/dist/install.js +209 -0
- package/dist/licence.js +41 -0
- package/dist/lock.js +50 -0
- package/dist/mcp.js +139 -0
- package/dist/migrate.js +275 -0
- package/dist/omp.js +46 -0
- package/dist/opencode.js +118 -0
- package/dist/paths.js +33 -0
- package/dist/plugin.js +101 -0
- package/dist/report/check.js +252 -0
- package/dist/report/data.js +401 -0
- package/dist/report/export.js +105 -0
- package/dist/report/markdown.js +310 -0
- package/dist/report/meta.js +48 -0
- package/dist/report/pdf.js +96 -0
- package/dist/review-server.js +7 -0
- package/dist/review.js +274 -0
- package/dist/setup.js +133 -0
- package/dist/skills.js +81 -0
- package/dist/types.js +9 -0
- package/dist/uninstall.js +192 -0
- package/dist/versions.js +8 -0
- package/dist/watch-frame.js +593 -0
- package/dist/watch.js +873 -0
- package/dist/workspace.js +316 -0
- package/docs/GUIDE.md +323 -0
- package/hooks/autopilot.mjs +45 -0
- package/hooks/lib/autopilot.mjs +191 -0
- package/hooks/lib/briefing.mjs +750 -0
- package/hooks/lib/licence.mjs +452 -0
- package/hooks/lib/pre-pr.mjs +209 -0
- package/hooks/lib/sast.mjs +207 -0
- package/hooks/lib/setup.mjs +107 -0
- package/hooks/lib/workspace.mjs +78 -0
- package/hooks/pre-pr.mjs +54 -0
- package/hooks/session-start.mjs +82 -0
- package/package.json +49 -4
- package/skills/bravos/SKILL.md +63 -0
- package/skills/bravos-annotate/SKILL.md +56 -0
- package/skills/bravos-annotate/scripts/lib/pick.mjs +127 -0
- package/skills/bravos-annotate/scripts/slice.mjs +141 -0
- package/skills/bravos-campaign/SKILL.md +49 -0
- package/skills/bravos-cicd/SKILL.md +48 -0
- package/skills/bravos-codegraph/SKILL.md +57 -0
- package/skills/bravos-context/SKILL.md +51 -0
- package/skills/bravos-context/intent.example.md +48 -0
- package/skills/bravos-env/SKILL.md +48 -0
- package/skills/bravos-env/scope.example.yaml +50 -0
- package/skills/bravos-prove/SKILL.md +52 -0
- package/skills/bravos-recheck/SKILL.md +47 -0
- package/skills/bravos-report/SKILL.md +53 -0
- package/skills/bravos-report/assessment.md +54 -0
- package/skills/bravos-report/brief.md +23 -0
- package/skills/bravos-report/finding.md +74 -0
- package/skills/bravos-report/retest.md +29 -0
- package/skills/bravos-report/style.md +44 -0
- package/skills/bravos-sast/SKILL.md +48 -0
- package/skills/bravos-schedule/SKILL.md +44 -0
- package/skills/bravos-schedule/loop.md +16 -0
- package/skills/bravos-ship/SKILL.md +58 -0
- package/skills/bravos-templatize/SKILL.md +48 -0
- package/skills/bravos-templatize/operator-guidance.md +20 -0
- package/templates/omp/bravos.js +62 -0
- package/templates/opencode/bravos.js +33 -0
- package/templates/report/report.html +88 -0
- package/templates/review/review.html +327 -0
- package/templates/workflows/bravos-nightly-claude.yml +173 -0
- package/templates/workflows/bravos-nightly-codex.yml +172 -0
- package/templates/workflows/bravos-pr-gate.yml +142 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-campaign
|
|
3
|
+
description: "Reach a security goal over many rounds and then stop: annotation coverage to a target, every open exposure tested on loopback, or every stale claim rechecked. Plans disjoint batches, runs one subagent per batch each round, gates every batch, rests between rounds with the harness's own loop or wakeup, and reports before-and-after numbers. Use when asked to reach 100% coverage, annotate or test everything, run until done, go on autopilot, or loop and schedule rounds with rests."
|
|
4
|
+
---
|
|
5
|
+
# Bravos campaign: rounds toward a goal, gated, then stop
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- The user names a goal larger than one session's slice: "coverage 100%", "test every open exposure", "recheck everything", "until done", "autopilot".
|
|
10
|
+
- The user asks for rounds, batches, subagents per round, or a rest between rounds.
|
|
11
|
+
- The briefing shows a large worklist or many untested exposures and the user wants it cleared.
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
**Plan check first.** Campaigns are on the Team plan. Run `bravos licence --json` once per session (the briefing's `Plan:` line says the same); if `licensed` is false, tell the person in two sentences what this skill would do here and that it needs a Team licence — `bravos account activate` starts a 30-day trial with no card, or https://account.bugb.io/pricing — then stop, and do nothing else from this skill. A licence in grace still counts.
|
|
16
|
+
|
|
17
|
+
1. Read the goal, the effort, the rest interval and any spend cap from the request. Goals: `coverage <N>%` (annotate), `test` (templatize, then env, then prove, loopback only), `recheck`, or several in that order. Effort: the user's words, else a `bravos effort: <level>` line in `AGENTS.md` / `CLAUDE.md`, else high (a campaign is a big job). Rest: the user's interval, else 10 minutes. If `.guardlink/intent.md` is missing, run bravos-context first and ask its questions once, before any round.
|
|
18
|
+
2. Read where things stand: `guardlink ci -f json` (`summary.coverage`, `exposures[]`, `stale[]`) and `guardlink hypothesis next --json`. If the goal is already met, say so and stop.
|
|
19
|
+
3. Estimate and say it before the first round: files or exposures left, batch size and batches per round (default 5 × 3, a starting point, not a limit; at max, as many batches as the work splits into cleanly), rounds expected, and that every round spends model tokens. At max this is information; below max, wait for the user's go-ahead once.
|
|
20
|
+
4. Plan the round's batches: `node {{SKILLS_ROOT}}/bravos-annotate/scripts/slice.mjs --base origin/<base> --n <size> --batches <k>` gives disjoint `batches`, entry points first, generated, test and CI files last. For `test` and `recheck`, split the exposures or claims the same way, one directory per batch. Reorder by your own reading of the code when it disagrees, and say why.
|
|
21
|
+
5. Run the round: one subagent per batch, through the harness's own subagent tool (in Claude Code the Agent tool; in Codex its subagents if this session has them, otherwise the batches one after another here). Brief each with its batch only, the skill to follow (bravos-annotate, bravos-templatize, bravos-prove or bravos-recheck), the effort, and the `<start>` commit. Each subagent runs that skill's gate to 0 errors and commits its batch alone.
|
|
22
|
+
6. Check the round here: `guardlink validate .`, then `guardlink ci -f json`. Report per batch and in total: files or exposures done, coverage before and after, gate errors, anything a subagent left. A batch whose gate failed is not committed; give it back to the next round once, then report it.
|
|
23
|
+
7. Rest, then start the next round at step 2. In Claude Code, schedule the next round after the rest interval with the session's wakeup or self-paced loop tool, passing this campaign's goal; if none is available, tell the user to run `/loop` with the campaign prompt. In Codex there is no in-session timer: run the next round straight away, or hand the campaign to the nightly workflow (bravos-schedule). Say which you did.
|
|
24
|
+
8. Stop when the goal is met, a round adds nothing, the same batch fails its gate twice, or the spend cap is reached. Final report: before and after numbers, commits per round, what is left and why, and the three states (open / mitigated, untested / confirmed / refuted, verified / stale) for what changed.
|
|
25
|
+
|
|
26
|
+
## Never
|
|
27
|
+
|
|
28
|
+
- Give two subagents overlapping files, or let one write outside its batch.
|
|
29
|
+
- Commit a batch whose gate failed, or carry on past a goal that is already met.
|
|
30
|
+
- Run a live test against anything but loopback, unless the host is in a committed, reviewed `scope.yaml` and the human typed their authorization in this session; never use `--destructive-ok` in a campaign.
|
|
31
|
+
- Launch an external agent CLI (a guardlink or cxg agent flag) for a batch; subagents come from the harness this session runs in.
|
|
32
|
+
- Open or update a pull request, or push, unless the user asked for it.
|
|
33
|
+
- Write `@accepts`, `@entitles` or `@confirmed`.
|
|
34
|
+
|
|
35
|
+
## Done when
|
|
36
|
+
|
|
37
|
+
- The goal is met, or the stop rule fired and you said which one.
|
|
38
|
+
- Every committed batch passed its gate; every skipped or failed batch is listed with its reason.
|
|
39
|
+
- The user has before and after numbers, the commits per round, and what was not tested.
|
|
40
|
+
|
|
41
|
+
## What it leaves for the next skill
|
|
42
|
+
|
|
43
|
+
- The campaign's commits on the current branch and the list of what is left, for bravos-ship before a pull request.
|
|
44
|
+
|
|
45
|
+
## Go deeper
|
|
46
|
+
|
|
47
|
+
- `{{SKILLS_ROOT}}/bravos-annotate/SKILL.md` — what each annotation batch does and its gate.
|
|
48
|
+
- `{{SKILLS_ROOT}}/bravos-prove/SKILL.md` — the authorized run a `test` batch makes.
|
|
49
|
+
- `{{SKILLS_ROOT}}/bravos-schedule/SKILL.md` — the nightly option when no in-session timer exists.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-cicd
|
|
3
|
+
description: "Audit and write the repo's security CI: the bravos PR gate (validate, lint since the merge base, undisclosed new exposure, confirmed exploits on changed paths, SARIF to code scanning), the nightly annotation workflow, and optional licensed SAST, fixing broken guardlink calls and script-injection risks in existing workflows. Use when asked to add or review security CI, a PR gate, code-scanning upload or a nightly job, or when a workflow calls guardlink with flags it does not have."
|
|
4
|
+
---
|
|
5
|
+
# Bravos CI/CD: the gate and the nightly job as workflows
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- The user asks to add, fix or review security CI, a PR check or code-scanning upload.
|
|
10
|
+
- bravos-schedule chose the nightly workflow.
|
|
11
|
+
- An existing workflow calls guardlink or cxg, or interpolates pull-request text into a shell step.
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
**Plan check first.** CI and nightly workflows are on the Team plan. Run `bravos licence --json` once per session (the briefing's `Plan:` line says the same); if `licensed` is false, tell the person in two sentences what this skill would do here and that it needs a Team licence — `bravos account activate` starts a 30-day trial with no card, or https://account.bugb.io/pricing — then stop, and do nothing else from this skill. A licence in grace still counts.
|
|
16
|
+
|
|
17
|
+
1. Audit `.github/workflows/*.yml`. For each guardlink or cxg call, check every flag against that command's `--help`; a `guardlink diff` call with `--from`/`--to` flags is the known broken pattern, since diff takes one positional ref. Flag any `github.event.pull_request`, issue or comment field, or `github.head_ref`, expanded straight into a `run:` line: that text is attacker-controlled and runs as shell.
|
|
18
|
+
2. Write bravos's workflows with `bravos init --ci` (or `bravos sync --ci`): `bravos-pr-gate.yml` plus the nightly variants. Delete the nightly variant for the harness the team does not use. A workflow someone edited by hand is kept and reported; never overwrite it.
|
|
19
|
+
3. Check the PR gate does what bravos-ship does: `guardlink validate .`, `guardlink lint . --since <base>` against the merge base, `guardlink diff <base> --fail-on-new --markdown` (passing when the PR body discloses new exposure in a Security gate section), a block on confirmed claims in changed files, and `guardlink sarif . --profile github -o guardlink.sarif` uploaded to code scanning.
|
|
20
|
+
4. Fix what the audit found in workflows bravos did not write: replace a broken guardlink call with the form above, and pass event text through `env:` and quote it in the shell. Show each change to the user before committing it.
|
|
21
|
+
5. SAST in CI, only when the team has a bravos licence: add a step running `bravos-cli analyze . --ci --fail-on error -f sarif,json -o sast` with the licence from a repo secret, and upload `sast.sarif` under its own code-scanning category.
|
|
22
|
+
6. Validate the workflows: run `actionlint` on them if it is installed; otherwise parse each as YAML. Fix every error.
|
|
23
|
+
7. Name the secrets and settings the user must add (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for the nightly job, the bravos licence for SAST, permission for Actions to create pull requests) without writing any value.
|
|
24
|
+
8. Ship the workflow changes as a pull request through bravos-ship.
|
|
25
|
+
|
|
26
|
+
## Never
|
|
27
|
+
|
|
28
|
+
- Write a secret, token, licence or session into a workflow or any committed file.
|
|
29
|
+
- Interpolate pull-request, issue or comment text, or a branch name, directly into a `run:` line.
|
|
30
|
+
- Use `guardlink validate . --strict` as a PR gate on a repo with recorded legacy exposures; it fails on every one of them, not on what the PR adds.
|
|
31
|
+
- Add a job that runs a live test or scan unattended; that needs the human's authorization typed in a session.
|
|
32
|
+
- Delete or rewrite a workflow bravos did not write without the user's go-ahead.
|
|
33
|
+
- Write `@accepts`, `@entitles` or `@confirmed`.
|
|
34
|
+
|
|
35
|
+
## Done when
|
|
36
|
+
|
|
37
|
+
- Every guardlink and cxg call in the repo's workflows uses only flags that command has, and no workflow writes event text into a shell line.
|
|
38
|
+
- `actionlint` (or a YAML parse) reports no error on any workflow you wrote or changed.
|
|
39
|
+
- The user has the list of secrets and settings to add, and the change is in a pull request.
|
|
40
|
+
|
|
41
|
+
## What it leaves for the next skill
|
|
42
|
+
|
|
43
|
+
- The workflow files, the audit findings in workflows bravos did not write, and the secrets still to add, for bravos-ship.
|
|
44
|
+
|
|
45
|
+
## Go deeper
|
|
46
|
+
|
|
47
|
+
- `{{SKILLS_ROOT}}/bravos-ship/SKILL.md` — the gate these workflows run.
|
|
48
|
+
- `{{SKILLS_ROOT}}/bravos-schedule/SKILL.md` — choosing between a loop, a nightly job and a cloud task.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-codegraph
|
|
3
|
+
description: "Ask the repo's code graph (codegraph-mcp) one bounded question — routes, attack surface, sinks a symbol reaches, whether code is live, blast radius, annotation gaps in a change — instead of grepping. Use before editing security-relevant code, when judging a finding's reachability, when tracing a handler to its sinks, and when bravos-annotate or bravos-ship needs targets."
|
|
4
|
+
---
|
|
5
|
+
# Bravos codegraph: one question, one tool, one verdict
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- You are about to edit security-relevant code and need its callers or blast radius.
|
|
10
|
+
- You are judging whether a finding's code is reachable from an entry point, or live at all.
|
|
11
|
+
- You are tracing a handler to the sinks it reaches, or listing routes and attack surface.
|
|
12
|
+
- bravos-annotate or bravos-ship needs file:symbol targets, such as unannotated entry points in a change.
|
|
13
|
+
|
|
14
|
+
## Do
|
|
15
|
+
|
|
16
|
+
**Plan check first.** The code graph is on the Team plan. Run `bravos licence --json` once per session (the briefing's `Plan:` line says the same); if `licensed` is false, tell the person in two sentences what this skill would do here and that it needs a Team licence — `bravos account activate` starts a 30-day trial with no card, or https://account.bugb.io/pricing — then stop, and do nothing else from this skill. A licence in grace still counts.
|
|
17
|
+
|
|
18
|
+
1. Ask the graph one bounded question per call; a graph answer replaces reading or grepping the tree. There are two ways in, with the same answers: the MCP tools (`codegraph_attack_surface` …) when the `codegraph` server is connected, or `bravos graph <question> --json` in the shell, where the question is the tool name without `codegraph_` and with hyphens (`codegraph_attack_surface` → `bravos graph attack-surface --json`, `codegraph_callers` → `bravos graph callers '<file>::<symbol>' --json`; `bravos graph --json` lists every question and its arguments). The shell route needs no MCP reconnect and works in every harness. The briefing's `Code graph:` line says which route is there. If neither is (no codegraph-mcp), set it up yourself: `bravos setup --check` reports what is missing without changing anything; show the person `bravos setup --dry-run --codegraph` and with their go-ahead run `bravos setup --codegraph` (it fetches the signed bundle through the account portal and verifies it before anything lands). If an install fails, quote its error and give the exact command for their own terminal; until then say "code graph unavailable; the other skills read the code instead" and answer by reading. Read the effort: the user's words ("at high"), else a `bravos effort: <level>` line in `AGENTS.md` / `CLAUDE.md`, else medium. It sets a starting budget, not a limit; go beyond it when the work needs it and say why in one line. Starting budget — low: the one question asked; medium: whatever questions the task needs; high: start from `codegraph_attack_surface`; max: the full attack surface plus `codegraph_impact` on every changed symbol.
|
|
19
|
+
2. Build or refresh the graph when an answer says there is none for this repository, or its `graph_currency` is `diverged`: `bravos graph build . --scip no-exec` indexes without running the repository's build scripts (`--scip auto` and `--isolate` run them; use those only on a repo the user trusts and only when they say so). A build that reports `semantic <language>: off` lacks that language's type checker; say that calls through interfaces may not connect and that `bravos setup --indexers` (with their go-ahead) adds it. For the MCP route, `bravos sync` registers the server from the bravos install; the person reconnects MCP (`/mcp` in Claude Code) to load it. In a bravos workspace, build one graph over the app folder (`bravos graph build <app folder> --scip no-exec`, the folder the briefing's `Workspace:` line names) and ask cross-service questions there: only a graph that holds both services links one service's HTTP, gRPC, GraphQL or OpenAPI client call to the other's handler (`callees` shows `via_framework`). A graph built inside one member stops at its own repository.
|
|
20
|
+
3. Pick the one tool that answers the question:
|
|
21
|
+
find a symbol → `codegraph_search`
|
|
22
|
+
routes → `codegraph_routes`
|
|
23
|
+
ranked attack surface → `codegraph_attack_surface`
|
|
24
|
+
sinks reachable from a symbol → `codegraph_reachable_sinks`
|
|
25
|
+
is it live (reachable from an entry point) → `codegraph_entry_reach`
|
|
26
|
+
blast radius of a change → `codegraph_impact`
|
|
27
|
+
callers → `codegraph_callers`
|
|
28
|
+
unannotated entry points in a change → `codegraph_annotation_gaps`
|
|
29
|
+
who may reach a route → `codegraph_auth_boundary`
|
|
30
|
+
the call path from an entry point to a symbol → `codegraph_call_chain`
|
|
31
|
+
untested exposures ranked by reach → `codegraph_untested_exposures`
|
|
32
|
+
code no entry point reaches → `codegraph_dead_code`
|
|
33
|
+
4. Read `verdict` and `confidence` before anything else in the answer. `unknown` and `insufficient_resolution` mean the graph could not tell: report them as not determined, never as clean or unreachable.
|
|
34
|
+
5. Quote the tool's verdict with its confidence, and hand the file:symbol targets to bravos-annotate (gaps to annotate) or bravos-ship (gaps blocking a PR).
|
|
35
|
+
|
|
36
|
+
## Never
|
|
37
|
+
|
|
38
|
+
- Treat `unknown` or `insufficient_resolution` as clean, unreachable or dead.
|
|
39
|
+
- Grep or read the tree for a question the graph answers while `graph_currency` is `current`.
|
|
40
|
+
- Build with `--scip auto`, `--scip trusted` or `--isolate` (they run the repository's own build scripts) without the user's say-so.
|
|
41
|
+
|
|
42
|
+
## Done when
|
|
43
|
+
|
|
44
|
+
- You asked one question, named the tool, and quoted its `verdict` and `confidence`.
|
|
45
|
+
- If the graph was `diverged`, you rebuilt with `no-exec` and the answer you report came after the rebuild.
|
|
46
|
+
- Reachability is not exploitability: say the graph judged reach, and that no run tested the finding. An exposure the graph shows as reachable is still untested until a run confirms or refutes it.
|
|
47
|
+
|
|
48
|
+
## What it leaves for the next skill
|
|
49
|
+
|
|
50
|
+
- A short list of file:symbol targets with the verdict and confidence that produced each, for bravos-annotate or bravos-ship.
|
|
51
|
+
- The same tools serve other skills directly: bravos-templatize (the route and auth that reach a sink), bravos-recheck (callers and blast radius of a fix), bravos-context (the real public surface) and bravos-prove (test order by reach). Each falls back to reading the code when the graph is unavailable.
|
|
52
|
+
|
|
53
|
+
## Go deeper
|
|
54
|
+
|
|
55
|
+
- The tool list the `codegraph` MCP server exposes (ask your MCP client for it) — 31 tools; the nine above cover Phase 1.
|
|
56
|
+
- `docs/GUARDLINK_REFERENCE.md` — how graph targets map to annotations.
|
|
57
|
+
- `{{SKILLS_ROOT}}/guardlink-annotate-exploitable/SKILL.md` — the evidence bar for a claim built from a graph answer (written by `guardlink init --agent claude` under `.claude/skills/` when present; otherwise `guardlink annotate --stdout --playbook exploitable "<scope>"` prints the same method).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-context
|
|
3
|
+
description: "Learn what the software intends before judging its code: for a project being researched (the default) draft its intent from upstream's own docs and ask only the scope decisions — configuration baseline, deployment, attackers in scope, unclear intended behaviours; for the user's own product run the owner interview — and get answers in bravos's review page, pre-filled with suggestions the team edits and confirms. Record them as .guardlink/intent.md plus actors, assets, invariant threats and boundaries in the threat model. Use when .guardlink/intent.md is missing, when asked to learn the app or its business logic, to say what matters or what is intended, before a first annotation pass or campaign, and after a large change to the product."
|
|
4
|
+
---
|
|
5
|
+
# Bravos context: what the product intends, so threats mean something
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- `.guardlink/intent.md` is missing, and bravos-annotate, bravos-campaign or the front door is about to judge code.
|
|
10
|
+
- The user asks you to learn the app, its business logic, what matters, or whether something is intended.
|
|
11
|
+
- The product changed a lot (new roles, payments, tenants, a new surface); refresh the intent.
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
1. Decide the perspective. **Researcher** (the default): the repo is software being studied, such as an open-source project or a vendor's code; its intent is upstream's to state. **Owner**: the repo is the user's own product; it is theirs. Use what the user said, the origin remote and the README; ask in one line only if it is unclear.
|
|
16
|
+
2. Read before you suggest. Researcher: upstream's security docs, deployment and hardening guides, SECURITY.md and docs/, each with its URL or path, then the auth and role code. Owner: the README, route table, auth and role code, data models, anything touching money or other users' data. Both: `.guardlink/definitions.*`, and `.guardlink/intent.md` if it exists (you are refreshing, not starting over). Build the code graph first when there is none or it is diverged: `bravos graph build . --scip no-exec` (no tokens, a few minutes; bravos-codegraph step 2). With the code graph (its MCP tools, or `bravos graph <question> --json` — same answers; see bravos-codegraph) (briefing `Code graph:` says `current`): `codegraph_routes`, `codegraph_attack_surface` and `codegraph_auth_boundary` give the real public surface — which routes answer unauthenticated callers, which need a tenant, which are admin-only; cite "code graph" as the source of each fact, suggestion and candidate INT item they produce ("these N APIs answer unauthenticated callers; upstream documents M of them"). Without it, say "code graph unavailable; surface read from the route table" and read the routes and auth code.
|
|
17
|
+
3. Suggest every answer yourself. Researcher, the open decisions (5 to 8, each `mustTouch: true`): which configuration to rate against (shipped defaults, upstream's recommended hardened setup, or both); single organisation or multi-tenant; which attackers are in scope (`multi`: unauthenticated network, authenticated low-privilege tenant, malicious client or worker, compromised peer or replica, internal network, operator, suggesting the last two out unless upstream says otherwise); each intended-looking behaviour upstream does not clearly document ("upstream is silent"); which invariants to test first. Everything else is a fact with `folded: true` and its source: actors, crown jewels, boundaries, behaviours upstream documents as intended (cite where), and invariants worded as the properties upstream claims. Owner: purpose, users and roles, crown jewels, two to five must-never rules, intended-by-design powers and boundaries; open by default, the rule and intended items `mustTouch`. Give every question a `suggested` answer, a one-line `reason`, its `sources` and a `confidence`; mark `low` and `mustTouch` anything you are unsure of.
|
|
18
|
+
4. Write it to `.guardlink/intent-draft.json` (schema `bravos.review/v1`, `perspective` set; question types `choice`, `multi`, `item`, `text`) and run `bravos review intent`. Tell the user: "the page that just opened has my suggestions filled in; change what is wrong, make the calls marked 'needs your call', then Confirm. Teammates run `bravos review intent` after pulling; say done when everyone has." If they prefer chat, or no browser can open, ask the same open decisions in one message. If they ask to share it on claude.ai and this is Claude Code, run `bravos review intent --share`, say first that their security intent will sit on claude.ai, publish the page it writes with the Artifact tool and `capabilities: {db: {}}`, and give them the link.
|
|
19
|
+
5. Wait for "done". Run `bravos review intent --summary` (for a shared page, first copy each document in its `answers` collection to `.guardlink/intent-answers/<name>.json` with ArtifactData). Settle each DISAGREE with the people involved; take EDITED answers as given. If the user is away on an unattended campaign, record your suggestions marked `— draft` and carry on; never record a decision nobody made.
|
|
20
|
+
6. Write `.guardlink/intent.md` from `{{SKILL_DIR}}/intent.example.md`. Researcher: an `## Assumptions` section first, with the configuration baseline, deployment and attackers in scope, each ending `— set by <names>, <date>`. Then purpose, actors, crown jewels, invariants `INV-1`…, intended-by-design `INT-1`… (researcher items carry `(upstream: <url or path>)`, or `(upstream is silent)`), and boundaries. End each item with how it was decided: `— suggested by bravos, accepted by <names>`, `— edited by <names>`, `— chosen by <names>`, or `— draft`. Commit the draft and answers files with it.
|
|
21
|
+
7. Put the structured facts in the threat model. In `.guardlink/definitions.*`, reusing ids and never redefining one: `@actor` per role, `@asset` per crown jewel, and one `@threat` per invariant, e.g. `@threat Cross-tenant read (#inv-tenant-isolation) [high] -- "INV-2: a user reads only their own tenant's orders"`. Beside the code they describe: `@handles <class> on <Asset>` for each crown jewel's data and `@boundary` for each boundary.
|
|
22
|
+
8. Run `guardlink validate .` and fix what it reports in the lines you wrote.
|
|
23
|
+
9. Commit `.guardlink/intent.md` and the definitions together, message `context: intent and invariants`.
|
|
24
|
+
10. Hand the invariant threat ids, the Assumptions and the intended-by-design list to bravos-annotate.
|
|
25
|
+
|
|
26
|
+
## Never
|
|
27
|
+
|
|
28
|
+
- Mark a draft invariant or intended-by-design item as confirmed, or put words in the user's mouth.
|
|
29
|
+
- Record a must-touch decision (an assumption, an unclear intended behaviour) as made unless a named person chose it in the page or in chat; accepting a suggestion untouched never counts for those.
|
|
30
|
+
- Present an upstream behaviour as intended without citing where upstream says so; write "upstream is silent" instead.
|
|
31
|
+
- Mark an item confirmed that no named person agreed to, in the review page or in chat.
|
|
32
|
+
- Publish the review page to claude.ai unless the user asked to share it and heard that it puts their security intent there.
|
|
33
|
+
- Rewrite a confirmed line in `.guardlink/intent.md`; append a dated change beneath it instead.
|
|
34
|
+
- Treat an intended-by-design item as an accepted risk: write `@accepts`, `@entitles` or `@confirmed`, or remove an existing claim because of it.
|
|
35
|
+
- Redefine an id that already exists in `.guardlink/definitions.*`.
|
|
36
|
+
|
|
37
|
+
## Done when
|
|
38
|
+
|
|
39
|
+
- `.guardlink/intent.md` exists, and every item is marked confirmed or draft.
|
|
40
|
+
- To read it again later, nothing needs re-running: `.guardlink/intent.md` itself, `bravos review intent --summary` (answers against suggestions), or `i` in `bravos watch`. Re-run this skill only to refresh the intent after the product or scope changes.
|
|
41
|
+
- Each invariant has a threat id in the definitions, and `guardlink validate .` exits 0.
|
|
42
|
+
- You told the user which items are still drafts and what confirming them changes.
|
|
43
|
+
|
|
44
|
+
## What it leaves for the next skill
|
|
45
|
+
|
|
46
|
+
- The invariant threat ids, the intended-by-design list and the boundaries, for bravos-annotate and bravos-templatize.
|
|
47
|
+
|
|
48
|
+
## Go deeper
|
|
49
|
+
|
|
50
|
+
- `{{SKILL_DIR}}/intent.example.md` — the shape of the intent file, with examples.
|
|
51
|
+
- `{{SKILLS_ROOT}}/bravos-annotate/SKILL.md` — how intent changes what gets annotated and how it is rated.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Intent — <product name>
|
|
2
|
+
|
|
3
|
+
What the software is for and what it intends. For a researched project this comes from upstream's
|
|
4
|
+
own docs, with each source cited; for your own product, from you. bravos-annotate judges every
|
|
5
|
+
claim against this file. Each item ends with how it was decided — `— suggested by bravos,
|
|
6
|
+
accepted by <names>`, `— edited by <names>`, `— chosen by <names>` or `— draft`; change a decided
|
|
7
|
+
line by appending a dated note beneath it, never by rewriting it.
|
|
8
|
+
|
|
9
|
+
## Assumptions (researched projects: what severities are rated against)
|
|
10
|
+
|
|
11
|
+
- Configuration: <shipped defaults | upstream's recommended hardened setup | both, rated separately> — set by <names>, <date>
|
|
12
|
+
- Deployment: <single organisation | multi-tenant | both> — set by <names>, <date>
|
|
13
|
+
- Attackers in scope: <e.g. unauthenticated network, authenticated tenant, malicious worker, compromised peer>; out of scope: <e.g. internal network, operator> — set by <names>, <date>
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
|
|
17
|
+
- <One or two sentences: what it does, for whom, who pays.> — draft
|
|
18
|
+
|
|
19
|
+
## Actors
|
|
20
|
+
|
|
21
|
+
- <Role, e.g. Customer: signs up, places orders, sees only their own account.> — draft
|
|
22
|
+
- <Role, e.g. Tenant admin: manages users inside one organisation.> — draft
|
|
23
|
+
- <Role, e.g. Operator: internal staff with support tooling.> — draft
|
|
24
|
+
|
|
25
|
+
## Crown jewels
|
|
26
|
+
|
|
27
|
+
- <Asset and data class, e.g. Payment records (financial).> — draft
|
|
28
|
+
- <e.g. Customer profiles (pii).> — draft
|
|
29
|
+
|
|
30
|
+
## Invariants (must never happen)
|
|
31
|
+
|
|
32
|
+
- INV-1: <e.g. A user never reads or changes another tenant's data.> — draft
|
|
33
|
+
- INV-2: <e.g. A refund is never issued without an approved order.> — draft
|
|
34
|
+
- INV-3: <e.g. No role can grant a role above its own.> — draft
|
|
35
|
+
|
|
36
|
+
## Intended by design (looks dangerous, is the product)
|
|
37
|
+
|
|
38
|
+
- INT-1: <e.g. Operators can impersonate a customer for support; it is logged.> (upstream: <url or path>, or: upstream is silent) — draft
|
|
39
|
+
- INT-2: <e.g. The CLI runs user-supplied shell commands on the user's own machine.> — draft
|
|
40
|
+
|
|
41
|
+
## Trust boundaries
|
|
42
|
+
|
|
43
|
+
- <e.g. Public internet → API gateway (unauthenticated).> — draft
|
|
44
|
+
- <e.g. API → billing provider (third party).> — draft
|
|
45
|
+
|
|
46
|
+
## Changes
|
|
47
|
+
|
|
48
|
+
- <YYYY-MM-DD: what changed and who confirmed it.>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-env
|
|
3
|
+
description: "Bring up a test target the user owns and prepare it for an authorized cxg run, under bravos's governance: loopback by default, a committed and reviewed scope.yaml, the human's own typed authorization, and a real session captured or imported, never invented. Use before bravos-prove or a template replay, when asked to stand up a test environment, seed test users or roles, or capture an auth session."
|
|
4
|
+
---
|
|
5
|
+
# Bravos env: a target the user owns, ready for an authorized run
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- bravos-prove or bravos-recheck needs a running target and none is up.
|
|
10
|
+
- The user asks to start the app for testing, seed test users, roles or orgs, or capture a login session.
|
|
11
|
+
- A run was refused at pre-flight (cxg exit 4) or stopped on a dead session (exit 5).
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
1. Decide the target with the user. The default is the app running on this machine at a loopback address (`127.0.0.1`, `localhost`, `[::1]`). Start it with the repo's own tooling (its compose file, dev server script or desktop build) and wait until it answers. In a bravos workspace each service has its own target: take the URLs from `bravos workspace show` (set a missing one with `bravos workspace target <name> <service>=http://127.0.0.1:<port>`), bring up every service the test crosses, and list every service host in the reviewed scope's `host_allowlist`.
|
|
16
|
+
2. On loopback, a recorded proving preference (`.guardlink/proving.md`, `choice: auto`) is the person's go-ahead for its target: start it with the recorded command and do not ask again. If the user names a non-loopback target, stop and get two things before going on: the host listed in a committed `scope.yaml` that has been through a reviewed PR, and the human's authorization typed by them in this session in their own words. Without both, the target is out of scope; say so and stay on loopback.
|
|
17
|
+
3. Write the scope. Copy `{{SKILL_DIR}}/scope.example.yaml` to `.guardlink/scope.yaml`, or run `cxg pentest scope-init -o .guardlink/scope.yaml` for the full field reference, then narrow `url_allowlist` to the routes under test. `method_allowlist` is a strict bound: cxg refuses every method it does not list, even with `--destructive-ok`, so a test that needs PUT, PATCH or DELETE needs the target's owner to add them in the reviewed PR; never add them yourself. scope-init pre-fills `authorization_attestation` with an example sentence: blank it. Leave that field for the human to fill. Commit it and ship it as a PR for review before any non-loopback run.
|
|
18
|
+
4. Seed test data with the repo's own fixtures, seed scripts or admin endpoints: the roles and users the threats need, such as two users of different tenants for an ownership check. Name any test that needs production-like data as such, and run it only where that data is synthetic.
|
|
19
|
+
5. Get a session. On loopback, do it yourself: create the test accounts through the app's own signup, seed script or admin endpoint, write their email and password to a gitignored file under `.bravos/`, and log in with `cxg pentest auth --target <url> --profile <name> --creds-file <file> --login-path <login route>`, one profile per identity a check needs. If the app's login is not a JSON POST, drive the signup and login in your own browser tool and capture the session interactively with `cxg pentest auth --target <url> --profile <name>`. For an authorized non-loopback target, ask the human to capture the session in their own browser with the same command, or to paste a storage state you import with `cxg pentest auth import --storage-state <file> --profile <name> --target <url>`; CI uses the import with a session the human exported once.
|
|
20
|
+
6. Confirm each session is live with `cxg pentest auth verify --profile <name>` before handing the target on, so a run never spends its budget unauthenticated.
|
|
21
|
+
7. Record the target URL, the profile names, the scope path and whether the target is loopback, and hand them to bravos-prove.
|
|
22
|
+
|
|
23
|
+
## Never
|
|
24
|
+
|
|
25
|
+
- Invent, guess or brute-force a credential, token or session. Use accounts you created through the app on loopback, or a session the human captured or pasted for any other target.
|
|
26
|
+
- Create accounts or log in yourself on a non-loopback target, or commit the test-account credentials file.
|
|
27
|
+
- Point any run at a host that is not loopback unless it is in a committed, reviewed `scope.yaml` and the human typed their authorization in this session.
|
|
28
|
+
- Write the `authorization_attestation` text yourself or paraphrase the human's words into it.
|
|
29
|
+
- Record a proving preference the person did not state; `.guardlink/proving.md` quotes their words and never covers a non-loopback target.
|
|
30
|
+
- Enable destructive probe classes against anything but a disposable loopback target, or add a `disposable_host_grants` entry.
|
|
31
|
+
- Seed or test against real user data.
|
|
32
|
+
- Write `@accepts`, `@entitles` or `@confirmed`; this skill prepares a target and judges nothing.
|
|
33
|
+
|
|
34
|
+
## Done when
|
|
35
|
+
|
|
36
|
+
- A target answers, and it is loopback or it is covered by a reviewed scope plus the human's typed authorization.
|
|
37
|
+
- `.guardlink/scope.yaml` exists, is committed and leaves `authorization_attestation` to the human.
|
|
38
|
+
- Each profile bravos-prove needs passes `cxg pentest auth verify --profile <name>`.
|
|
39
|
+
- You told the user what you seeded and which tests need data that is not synthetic.
|
|
40
|
+
|
|
41
|
+
## What it leaves for the next skill
|
|
42
|
+
|
|
43
|
+
- The target URL, profile names, scope path and the loopback verdict, for bravos-prove and bravos-recheck.
|
|
44
|
+
|
|
45
|
+
## Go deeper
|
|
46
|
+
|
|
47
|
+
- `{{SKILL_DIR}}/scope.example.yaml` — the loopback starter and what each field limits.
|
|
48
|
+
- `{{SKILLS_ROOT}}/bravos-prove/SKILL.md` — what happens with the target next.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# bravos starter scope for cxg pentest — loopback only.
|
|
2
|
+
#
|
|
3
|
+
# Copy to .guardlink/scope.yaml, commit it, and get it reviewed in a PR before any run.
|
|
4
|
+
# Full field reference: `cxg pentest scope-init -o /tmp/reference-scope.yaml`.
|
|
5
|
+
#
|
|
6
|
+
# This starter allows only the read-mostly methods, a small budget, and no disposable-host
|
|
7
|
+
# grants, so destructive probe classes stay limited to a loopback target. Widening any of
|
|
8
|
+
# it is a decision for the person who owns the target, made in the reviewed PR.
|
|
9
|
+
|
|
10
|
+
# The human's written authorization, in their own words, typed in the session that runs
|
|
11
|
+
# the test. bravos never writes this line for them. Leave it empty until they do.
|
|
12
|
+
authorization_attestation: ""
|
|
13
|
+
|
|
14
|
+
# A strict bound: cxg refuses every method not listed, even with --destructive-ok. A test that
|
|
15
|
+
# needs PUT, PATCH or DELETE needs them added here, in the reviewed PR, by the target's owner.
|
|
16
|
+
method_allowlist: [GET, POST, HEAD, OPTIONS]
|
|
17
|
+
|
|
18
|
+
# Empty means every path under --target. Narrow it to the routes under test.
|
|
19
|
+
url_allowlist: []
|
|
20
|
+
|
|
21
|
+
# Paths no run may touch, whatever the allowlist says.
|
|
22
|
+
url_blocklist: []
|
|
23
|
+
|
|
24
|
+
# Paths no run may touch without --destructive-ok. cxg replaces its built-in list with this one,
|
|
25
|
+
# so it carries all nine of cxg's defaults; add to it, do not trim it. Single quotes keep each
|
|
26
|
+
# backslash literal: in double quotes YAML reads \b as a backspace and the pattern matches nothing.
|
|
27
|
+
destructive_patterns:
|
|
28
|
+
- '/delete[-_]?all\b'
|
|
29
|
+
- '/wipe\b'
|
|
30
|
+
- '/purge\b'
|
|
31
|
+
- '/drop[-_]?(?:db|database|table|all)\b'
|
|
32
|
+
- '/reset[-_]?(?:all|db|database)\b'
|
|
33
|
+
- '/destroy[-_]?all\b'
|
|
34
|
+
- '/admin/(?:teardown|format|nuke)\b'
|
|
35
|
+
- '/__truncate__\b'
|
|
36
|
+
- '/factory[-_]?reset\b'
|
|
37
|
+
|
|
38
|
+
# Target host only.
|
|
39
|
+
host_allowlist: []
|
|
40
|
+
|
|
41
|
+
max_requests_per_endpoint: 20
|
|
42
|
+
max_requests_total: 500
|
|
43
|
+
request_interval_seconds: 0.2
|
|
44
|
+
max_socket_connections: 5
|
|
45
|
+
five_xx_streak_threshold: 5
|
|
46
|
+
|
|
47
|
+
# Empty: destructive classes run against a loopback target only.
|
|
48
|
+
disposable_host_grants: []
|
|
49
|
+
|
|
50
|
+
brute_force_min_attempts: 20
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-prove
|
|
3
|
+
description: "Run the authorized test against a target the user owns and record what it showed: export the claims for cxg, replay the kits written in this session, let cxg generate probes for the rest through a model API, read the report's buckets and exit code, and write outcomes back only through guardlink's hypothesis ledger. Use when asked to prove, test or verify a finding, to check whether an exposure is exploitable, or after bravos-templatize and bravos-env have prepared a run."
|
|
4
|
+
---
|
|
5
|
+
# Bravos prove: an authorized run, and only a run decides
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- The user asks whether an exposure is exploitable, or to prove, test or verify a finding.
|
|
10
|
+
- bravos-templatize wrote templates and bravos-env has a target ready.
|
|
11
|
+
- `guardlink hypothesis next` puts an untested exposure at the top.
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
1. Check the preconditions from bravos-env: a target that answers, `.guardlink/scope.yaml` committed, each profile passing `cxg pentest auth verify --profile <name>`, and for any non-loopback target both a reviewed scope and the human's authorization typed in this session. If one is missing, stop and hand back to bravos-env. Read the effort: the user's words ("at high"), else a `bravos effort: <level>` line in `AGENTS.md` / `CLAUDE.md`, else medium. It sets a starting budget, not a limit; go beyond it when the work needs it and say why in one line. Starting budget — low: replay existing kits only; medium: also cxg generation for exposures without a kit, `--max-templates 8`, after asking about the spend; high: `--max-templates 32` with `--enable-chaining`; max: every testable class, spend pre-approved (no question).
|
|
16
|
+
2. Order what to test, then read how the person wants proving handled. With the code graph (its MCP tools, or `bravos graph <question> --json` — same answers; see bravos-codegraph) (briefing `Code graph:` says `current`): `codegraph_untested_exposures` and `codegraph_untested_reach` rank untested exposures by reach; test the ones intent.md's Assumptions put in scope first, least privileged entry point first, and report exposures only an out-of-scope attacker reaches instead of running them. Without the graph, say "code graph unavailable; using guardlink hypothesis next order" and use that queue. Within that order, an exposure with a reachable SAST finding joined to it (`bravos sast candidates`) goes before one without, and its route goes into the run notes; with no SAST report or SAST unavailable, skip this. Then, for how the person wants proving handled: the briefing's `Proving:` line, from `.guardlink/proving.md`. If it is not set, ask them once, with a suggestion drawn from the repo (for example, "you have a docker-compose; I'd prove automatically on 127.0.0.1:8080"): prove automatically on their loopback target whenever new or changed exposures appear, ask each time, on a schedule (bravos-schedule's nightly), or not now; and whether cxg may generate probes through a model API, which spends money. Record their answer in `.guardlink/proving.md`: lines `choice: auto|ask|schedule|none`, `target: <loopback url>`, `start: <start command>`, `generation: yes|no`, their words quoted verbatim, and `— confirmed by <their name>, <date>`. Then follow it: `auto` proceeds on that target with their quoted words as the attestation and, with `generation: yes`, the spend approved; `ask` asks now; `schedule` hands to bravos-schedule; `none` stops here and says so. When they change their mind, rewrite the file with their new words.
|
|
17
|
+
3. Export the claims cxg reads: `guardlink sarif . --profile pentest -o whitebox/findings.sarif`.
|
|
18
|
+
4. Web or Electron target: replay the kits written in this session with `cxg pentest run --codebase . --target <url> --auth <profiles> --no-author --template-dir .guardlink/cxg-replay/ --scope-file .guardlink/scope.yaml --attestation "<the human's words>" --session-dir .guardlink/pentest-findings/<run> --ci`, adding `--target-type electron` for an Electron app. `<profiles>` is every login the kits' `@requires_identities` lines name, comma-separated (`victim,attacker` for an IDOR kit), each verified in bravos-env; a kit whose login is missing is skipped. `--no-author` keeps a replay to zero model calls: without it cxg can rewrite an ambiguous probe through the claude CLI. Copy the attestation verbatim from what the human typed; leave the flag off on loopback if they gave none. In a bravos workspace, run each kit against the URL of the service that owns its file (`bravos workspace show` lists the services, their URLs and the calls between them); a chain that crosses services is run one hop at a time, each hop against its own service's URL under that service's scope and authorization, with the previous hop's result carried as the next hop's input — never one run pointed at several hosts.
|
|
19
|
+
5. Exposures with no kit yet: let cxg generate probes from the exported claims, steered by the operator guidance. First tell the user the run calls a model API (up to the template budget) and get their go-ahead, unless they already approved the spend (effort max, or a standing line saying so); then run `cxg pentest run --codebase . --target <url> --auth <profile> --ai --ai-provider anthropic --max-templates 8 --skills-file .guardlink/operator-guidance.md --scope-file .guardlink/scope.yaml --attestation "<the human's words>" --session-dir .guardlink/pentest-findings/<run> --ci` (use `openai` if that is the key the user has). Copy every generated template that confirmed or refuted into `.guardlink/cxg-replay/` so the next run replays it.
|
|
20
|
+
6. Network or CLI templates from the translate method: run `cxg scan --scope <url> --template-dir .guardlink/cxg-templates/ --output .guardlink/pentest-findings/<run> --output-format json,sarif`, with a loopback URL or `cli:///absolute/path/to/binary` as the scope. That folder holds scanner templates only (bravos-templatize keeps probes in `.guardlink/cxg-replay/`); if a JavaScript probe is in it, cxg runs it under its default scope, so move the probe out, or add `--scope-file .guardlink/scope.yaml --auth <profiles> --session-dir .guardlink/pentest-findings/<run>` before running.
|
|
21
|
+
7. Read the exit code before the report. For `cxg pentest run`: 0 none confirmed, 1 no templates ran, 2 confirmed findings, 3 hard-killed (5xx streak, scope violation, target died), 4 refused (at pre-flight, or a supplied probe was skipped for a missing login or refused), 5 session dead in CI. On 3 or 5 stop: nothing was proven, so report the cause and hand back to bravos-env. On 4 read the report before concluding: its `status`, `auth_skipped` and `probe_refusals` say what was skipped and why, and any probe that did run keeps its confirmed or refuted result. Write back those results, report the skipped ones as untested with their cause, and hand a missing login to bravos-env. For `cxg scan` with probes: 2 means confirmed, 4 a probe refused.
|
|
22
|
+
8. Read the report's buckets: confirmed, refuted (a mitigation held), ambiguous, and not run with its cause. Ambiguous and not-run results prove nothing either way; report them as untested.
|
|
23
|
+
9. Write back the confirmations: `guardlink hypothesis confirm --from-scan .guardlink/pentest-findings/<run>/report.json --write`. It records only findings whose claim key resolves, and writes the source line only for uncontested ones.
|
|
24
|
+
10. Record each refuted result against its claim: `guardlink hypothesis refute <file:line> --evidence "<request sent and response received>"`, quoting the report entry.
|
|
25
|
+
11. Run `guardlink hypothesis list --state confirmed --json` and report each new confirmation with its threat, asset and file:line, plus the refutations and every result left untested.
|
|
26
|
+
12. Hand confirmed threats to the user as fix-first, and to bravos-recheck once a fix lands.
|
|
27
|
+
|
|
28
|
+
## Never
|
|
29
|
+
|
|
30
|
+
- Pass `--ai-provider claude`, `codex`, `gemini`, `crush` or `auto` to `cxg pentest run`; those launch another agent CLI. Only the API providers `anthropic` and `openai` generate probes, and only with the spend approved.
|
|
31
|
+
- Run against a host outside the committed scope, or without the human's typed authorization when the target is not loopback.
|
|
32
|
+
- Write `--attestation` text the human did not type, here or in `.guardlink/proving.md`.
|
|
33
|
+
- Record a proving preference the person did not state, or read one as covering more than it says: it never covers a non-loopback target or a destructive class.
|
|
34
|
+
- Use `--destructive-ok` unless the target is a disposable loopback instance and the human asked for it.
|
|
35
|
+
- Write `@confirmed` yourself, or treat reading code as a confirmation; only `guardlink hypothesis confirm --from-scan` from a real report writes it.
|
|
36
|
+
- Call an exposure refuted because a run found nothing, or because the run was killed, refused or never ran its template.
|
|
37
|
+
- Write `@accepts` or `@entitles`.
|
|
38
|
+
|
|
39
|
+
## Done when
|
|
40
|
+
|
|
41
|
+
- The run exited 0 or 2 and its report was read bucket by bucket, or you reported why it stopped.
|
|
42
|
+
- Every confirmed finding went through `guardlink hypothesis confirm --from-scan`, and every refutation cites its request and response.
|
|
43
|
+
- For each claim you reported its three states: open or mitigated, confirmed, refuted or untested, and verified or stale, plus what was not tested.
|
|
44
|
+
|
|
45
|
+
## What it leaves for the next skill
|
|
46
|
+
|
|
47
|
+
- The run directory, the confirmed and refuted claim keys, and the untested ones, for bravos-recheck and bravos-ship, and for bravos-report to write up what was proven.
|
|
48
|
+
|
|
49
|
+
## Go deeper
|
|
50
|
+
|
|
51
|
+
- `{{SKILLS_ROOT}}/bravos-env/SKILL.md` — the target, scope and session this run depends on.
|
|
52
|
+
- `{{SKILLS_ROOT}}/bravos-recheck/SKILL.md` — replaying these kits after a fix.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-recheck
|
|
3
|
+
description: "Keep findings from going stale after code changes: list the claims a change made stale, re-read and re-verify them, replay the retest kits of confirmed threats against an authorized target, check the fix added no new exposure, and re-read what to test next. Use after a security fix lands, when the briefing lists stale claims, when asked whether a fix held, and before closing a security ticket."
|
|
4
|
+
---
|
|
5
|
+
# Bravos recheck: a fix is not done until the claim is current
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- A fix for a confirmed or open exposure has been written.
|
|
10
|
+
- The briefing's `Went stale in files you changed since <base>` line is not empty.
|
|
11
|
+
- The user asks whether a fix held, or wants to close a security ticket.
|
|
12
|
+
|
|
13
|
+
## Do
|
|
14
|
+
|
|
15
|
+
1. Find the base: `git symbolic-ref -q --short refs/remotes/origin/HEAD` prints `origin/<base>` (if empty, use `main`), then `git merge-base HEAD origin/<base>`. Read the effort: the user's words ("at high"), else a `bravos effort: <level>` line in `AGENTS.md` / `CLAUDE.md`, else medium. It sets a starting budget, not a limit; go beyond it when the work needs it and say why in one line. Starting budget — low: stale claims in the change only; medium: also confirmed threats on changed files; high: every confirmed threat; max: everything, then send annotation gaps to bravos-annotate.
|
|
16
|
+
2. List what went stale: run `guardlink ci -f json` and take each `stale[]` entry whose file is in `git diff --name-only <base>...HEAD`. Then widen it past the changed files. With the code graph (its MCP tools, or `bravos graph <question> --json` — same answers; see bravos-codegraph) (briefing `Code graph:` says `current`; rebuild with `no-exec` when `diverged`): `codegraph_impact` on each changed symbol, and `codegraph_callers` where a control moved, give the code whose path runs through the change; add the claims in those files to the list. A fix that removes the only route to a sink (`codegraph_entry_reach` now says unreachable) is a reason to replay its kit and refute it with a run, never a refutation by itself. Read `verdict` and `confidence` first. Without the graph, say "code graph unavailable; rechecking the changed files only". When SAST is available and a run exists, rerun it on the changed files (bravos-sast, `--scope <file>` for each) and compare with `bravos-cli diff <new>.json <old>.json`: a finding that is gone is a reason to replay its kit and refute it with a run, never a refutation by itself. With SAST unavailable, say so and skip it.
|
|
17
|
+
3. For each stale claim, re-read the code beneath it and decide whether the claim still says what the code does. If it does not, fix the annotation in this session with bravos-annotate's rules (write an `@audit` beside a claim you did not write rather than editing it).
|
|
18
|
+
4. Re-lock only the claims you re-read and still hold: preview with `guardlink verify <file:line> --dry-run`, then run `guardlink verify <file:line>`. Never re-lock a claim you did not read.
|
|
19
|
+
5. List confirmed threats on the changed files: `guardlink hypothesis list --state confirmed --json`, keeping those in the diff.
|
|
20
|
+
6. Replay each one's retest kit against the authorized target from bravos-env: `cxg pentest run --codebase . --target <url> --auth <profile> --template-dir .guardlink/cxg-replay/ --scope-file .guardlink/scope.yaml --attestation "<the human's words>" --session-dir .guardlink/pentest-findings/<run> --ci`. Copy the attestation verbatim from what the human typed; leave the flag off on loopback if they gave none. No kit yet: hand the threat to bravos-templatize first.
|
|
21
|
+
7. Read the result as bravos-prove does. Still confirmed: the fix did not hold; say so. Refuted with the same request: record it with `guardlink hypothesis refute <file:line> --evidence "<request sent and response received>"`, and tell the user the confirmation line in the source is theirs to remove. Killed, refused or not run: nothing was rechecked.
|
|
22
|
+
8. Check the fix added nothing: `guardlink diff <base> --fail-on-new --markdown`, then `guardlink lint . --since <base> --json`, applying each `fix` only to lines this session wrote.
|
|
23
|
+
9. Re-read the queue with `guardlink hypothesis next --json` and name the next item.
|
|
24
|
+
|
|
25
|
+
## Never
|
|
26
|
+
|
|
27
|
+
- Re-lock a stale claim with `guardlink verify --stale` or `--all` in bulk; each re-lock asserts a control still holds, so it needs the code read first.
|
|
28
|
+
- Remove or edit a `@confirmed` line; after a replay shows the exploit no longer works, the human removes it.
|
|
29
|
+
- Call a fix verified because the code changed, because a scan found nothing, or because a run was killed or refused.
|
|
30
|
+
- Replay against a target outside the committed scope, or without the human's typed authorization when it is not loopback.
|
|
31
|
+
- Write `@accepts` or `@entitles`.
|
|
32
|
+
|
|
33
|
+
## Done when
|
|
34
|
+
|
|
35
|
+
- Every stale claim in the change was re-read and either re-locked, corrected, or reported.
|
|
36
|
+
- Every confirmed threat on a changed path was replayed, or you said why it was not.
|
|
37
|
+
- `guardlink diff <base> --fail-on-new --markdown` exits 0, or each new exposure it lists is reported as open.
|
|
38
|
+
- You named the three states for each claim you touched and what was not tested.
|
|
39
|
+
|
|
40
|
+
## What it leaves for the next skill
|
|
41
|
+
|
|
42
|
+
- The re-locked, corrected and still-confirmed claims, and the replay run directory, for bravos-ship, and for a bravos-report retest report that records the outcome beside the original finding.
|
|
43
|
+
|
|
44
|
+
## Go deeper
|
|
45
|
+
|
|
46
|
+
- `{{SKILLS_ROOT}}/bravos-prove/SKILL.md` — how a run's report is read.
|
|
47
|
+
- `{{SKILLS_ROOT}}/bravos-annotate/SKILL.md` — the rules for correcting a claim.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bravos-report
|
|
3
|
+
description: "Write a crisp, evidence-bound white-box VAPT write-up in this session: a full assessment, a single finding, a retest report or an executive brief, built only from the facts bravos report data gathers (threat model, hypothesis ledger, cxg runs, SAST, code graph, intent, scope), with every claim of proof citing its evidence and its evidence level computed rather than asserted; check it with bravos report check, then export it to md, txt, html or pdf. Use when asked to write up, report, summarise for leadership, document a finding, or record a retest."
|
|
4
|
+
---
|
|
5
|
+
# Bravos report: decisions first, evidence underneath, nothing claimed that was not shown
|
|
6
|
+
|
|
7
|
+
## When
|
|
8
|
+
|
|
9
|
+
- The user asks for an assessment report, a write-up, a summary for leadership, or the findings documented.
|
|
10
|
+
- bravos-prove confirmed or refuted something, and the result should reach someone who was not in the session.
|
|
11
|
+
- bravos-recheck replayed a fix, and the retest should be recorded next to the original finding.
|
|
12
|
+
- An engineer needs one finding written so they can reproduce it, fix its cause and show closure.
|
|
13
|
+
|
|
14
|
+
## Do
|
|
15
|
+
|
|
16
|
+
1. Pick the kind from the request: `assessment` (the default: the full eight-section report), `finding` (one self-contained write-up; needs `--finding <claim key prefix | file:line | EXP id>`), `retest` (original result, replay, residual exposure) or `brief` (the decision summary alone). Read the effort as the other skills do; it sets how deep the system context and narratives go, never which facts are included.
|
|
17
|
+
2. Gather the facts: `bravos report data --kind <kind>` (add `--finding …` for one finding, `--no-graph` when the graph is slow or absent). It writes `.bravos/reports/<id>/evidence.json` and prints the report id and folder. Read the bundle: `candidates` are the exposures with their computed `level`, `items` are every citable fact by id, `runs` are the cxg results, `gaps` are what could not be gathered. If `runs` is empty, say in the report that nothing was exercised at runtime; no finding may then read as reproduced.
|
|
18
|
+
3. Read `.guardlink/intent.md` (in the bundle as `intent.text`): its Assumptions name the attacker starting positions, the configuration baseline and the scope decisions. Keep the tester's access (source code, admin credentials, the loopback target) apart from what an attacker needs: every finding states the attacker's prerequisites, never the tester's. Then collect what a reader needs to rebuild the test, from the run folders the bundle lists, `.guardlink/proving.md` (target and start command), `.guardlink/scope.yaml` and the session's own history: how the product was built; every config and feature-flag change; how keys and accounts were made and which permission each held; what was seeded; and every helper written to test it (a stub handler, a gRPC client or worker), with how to install and run it. Copy the config files and scripts, never keys or tokens, into the run folder so the report can point at them.
|
|
19
|
+
4. Consolidate before you write: group exposures that share one root cause into one finding (cite every EXP id it covers); order findings by remediation priority (exposure, asset sensitivity, prerequisites, compensating controls), kept apart from technical severity; build an attack narrative only from steps the evidence links (a run's chained steps, flows in the model, a code-graph call chain), and cite each step.
|
|
20
|
+
5. Write `.bravos/reports/<id>/report.md` from the template for the kind: `{{SKILL_DIR}}/assessment.md`, `{{SKILL_DIR}}/finding.md`, `{{SKILL_DIR}}/retest.md` or `{{SKILL_DIR}}/brief.md`, in the voice `{{SKILL_DIR}}/style.md` sets. Write for the team that has to act on it: plain words first; what the attacker wants and what they need; why it is a vulnerability; the weaknesses one by one and how they chain step by step; the environment and the roles table; a cxg reproduction (`cargo install cert-x-gen`, the template, the exact `cxg scan` command and its expected output); granular manual steps; what held; and the fix with how to check it. Evidence ids go in the closing Evidence section, not in the prose. When the proof is a script rather than a cxg template, hand it to bravos-templatize to become one, and have bravos-prove replay it under the person's authorization before you quote its output; otherwise say the cxg command was not run.
|
|
21
|
+
6. Give each finding a `**Status:**` line with the bundle's computed level for what it cites, word for word: `reproduced at runtime`, `control held at runtime`, `established by code or configuration review`, or `suspected, awaiting validation`. Repeat it in the Evidence section with the evidence ids in parentheses (the top line may say "see Evidence"). A weaker label than the evidence supports is allowed; a stronger one is not. "Not reproduced" names the conditions tested, from the run's not-run causes.
|
|
22
|
+
7. Write coverage with its denominator ("cross-tenant reads exercised on 3 of 11 routes"; "83% of files, 1,417 of 1,712"), list the controls that held next to the weaknesses, and keep raw tool output in the appendices. Quote requests and responses from the run evidence with tokens and credentials redacted; the bundle already redacts what it recognises.
|
|
23
|
+
8. Severity: give the rating and its rationale in this application's context. Use CVSS only when you can state the version and the full vector from facts in the bundle; never a bare score.
|
|
24
|
+
9. Check: `bravos report check <id>`. Fix every error and rerun until it passes; read the warnings (long paragraphs, vague titles) and fix the ones that would slow a reader down.
|
|
25
|
+
10. Export: `bravos report export <id> --format md,html` by default, or the formats the user asked for (`txt`, `pdf`; `--open` to open the html or pdf). PDF needs an installed Chrome, Edge, Brave or Chromium; without one, the html stands and the command says so.
|
|
26
|
+
11. Tell the user where the files are, how many findings are reproduced at runtime, established by review, and suspected, and what remained untested (the bundle's gaps and not-run causes). The report stays in `.bravos/reports/`, which git ignores; sharing it is the person's decision.
|
|
27
|
+
|
|
28
|
+
## Never
|
|
29
|
+
|
|
30
|
+
- Call a finding reproduced, or a control held, without a ledger record or a run result the bundle lists; the check refuses it, and so should you.
|
|
31
|
+
- Write `@confirmed`, `@accepts` or `@entitles`, or change a claim's tested state from a report; reports describe the ledger, they never write it.
|
|
32
|
+
- Print a live secret, token, password or session cookie; cite the rule and file:line instead, as the SAST secrets list does.
|
|
33
|
+
- Send traffic, start a target, or run a probe; this skill only reads the evidence and writes.
|
|
34
|
+
- Turn a dependency advisory into an exploit claim when the vulnerable function is not shown reached.
|
|
35
|
+
- Commit the report folder, or copy evidence into a tracked file, unless the person asks and has redacted it.
|
|
36
|
+
|
|
37
|
+
## Done when
|
|
38
|
+
|
|
39
|
+
- `bravos report check <id>` passes for the report.
|
|
40
|
+
- The exports the user asked for exist beside `report.md`, and you named them.
|
|
41
|
+
- The user knows what is reproduced, what is code-review only, what is suspected, and what was not tested.
|
|
42
|
+
|
|
43
|
+
## What it leaves for the next skill
|
|
44
|
+
|
|
45
|
+
- Each finding's F-id, its EXP ids and claim keys: bravos-recheck replays the reproduced ones after a fix, and a retest report records the outcome next to the original.
|
|
46
|
+
- The remediation roadmap, with acceptance criteria per item, for tickets (`@ticket` is the developer's to add).
|
|
47
|
+
|
|
48
|
+
## Go deeper
|
|
49
|
+
|
|
50
|
+
- `{{SKILL_DIR}}/assessment.md` — the eight sections in reading order, and what each gives the reader.
|
|
51
|
+
- `{{SKILL_DIR}}/finding.md` — the fields every finding carries.
|
|
52
|
+
- `{{SKILL_DIR}}/retest.md` and `{{SKILL_DIR}}/brief.md` — the other two kinds.
|
|
53
|
+
- `{{SKILL_DIR}}/style.md` — how to write it so an engineer can act without asking the author.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Assessment template
|
|
2
|
+
|
|
3
|
+
The full white-box assessment, in reading order: decisions first, then progressively deeper evidence. Keep the eight `##` headings as written (the numbers are optional); `bravos report check` reads them in this order.
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
# <Application> security assessment — <month year>
|
|
7
|
+
|
|
8
|
+
## 1. Executive assessment
|
|
9
|
+
|
|
10
|
+
<One to two pages. The main risks in plain words, what was demonstrated (and what was only reviewed), the strengths that held, and the decisions needed from the reader. Lead with the single most important sentence.>
|
|
11
|
+
|
|
12
|
+
## 2. Scope and limitations
|
|
13
|
+
|
|
14
|
+
<Applications, repositories, environments, roles and workflows assessed; exclusions; the access provided to the tester; dates; the reviewed commit and the tested build; the configuration baseline from intent.md; and what was unavailable (the bundle's gaps).>
|
|
15
|
+
|
|
16
|
+
## 3. System and threat context
|
|
17
|
+
|
|
18
|
+
<A compact diagram of the real boundary or flow (a fenced mermaid block or a short list), the sensitive assets, the trust boundaries, and the attacker starting positions in scope (intent.md's Assumptions). Separate the tester's access from the attacker's.>
|
|
19
|
+
|
|
20
|
+
<For an app made of several services (a workspace bundle, `bravos report data --workspace`): a short "Services and the calls between them" part — each service, where it answers, and each cross-service call (EDGE-n) that an attack can cross. Cite a service's evidence with its prefix, e.g. `orders:EXP-3`.>
|
|
21
|
+
|
|
22
|
+
## 4. Coverage and approach
|
|
23
|
+
|
|
24
|
+
<What was examined and how deeply, with denominators: annotated files out of source files, exposures tested out of declared, routes exercised out of routes known, roles used. Name the controls that held. Name what was not tested and why (not-run causes, missing logins, out-of-scope attackers).>
|
|
25
|
+
|
|
26
|
+
## 5. Principal attack narratives
|
|
27
|
+
|
|
28
|
+
<How the most consequential weaknesses work together, step by step, each step citing its evidence id. Only chains the evidence links; a plausible chain nobody exercised is labelled as such.>
|
|
29
|
+
|
|
30
|
+
## 6. Findings
|
|
31
|
+
|
|
32
|
+
<One block per finding, ordered by remediation priority: a "### F-01 — <who can do what to what>" heading, then the finding template's lines and sections (finding.md) one heading level down — **Status:** and **Severity:** lines, then #### headings for what the attacker needs, what we found, how it chains, impact, how to reproduce (cxg and by hand), what held, and the fix with how to check it.>
|
|
33
|
+
|
|
34
|
+
## 7. Remediation roadmap
|
|
35
|
+
|
|
36
|
+
<A table: action, finding ids, owner, depends on, target date (agreed, or "to agree"), acceptance criterion. Separate immediate containment from the durable fix.>
|
|
37
|
+
|
|
38
|
+
## 8. Retest results and appendices
|
|
39
|
+
|
|
40
|
+
<Retest outcomes so far (or "no retest yet"), then appendices: the test environment once for every finding (build, config changes, accounts and permissions, seeded data, helpers written), the evidence index (id → what it is), raw tool output summaries, tool versions, and the full list of exposures with their tested states.>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What each section gives the reader
|
|
44
|
+
|
|
45
|
+
| Section | The reader can then |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Executive assessment | decide what to fund and what to stop |
|
|
48
|
+
| Scope and limitations | know exactly what the conclusions cover |
|
|
49
|
+
| System and threat context | see where the boundary is and who is outside it |
|
|
50
|
+
| Coverage and approach | judge how much weight an absence of findings carries |
|
|
51
|
+
| Principal attack narratives | understand the worst realistic outcome |
|
|
52
|
+
| Findings | reproduce, fix and verify each issue |
|
|
53
|
+
| Remediation roadmap | plan the work and know when it is done |
|
|
54
|
+
| Retest and appendices | check closure and trace every claim to its evidence |
|