Reviewer runbook
You have been named the product-security reviewer for a release. This page takes you from an untouched evidence pack to a signed-off one, in the order you will actually do it, and shows what an auditor sees afterwards.
Every command on this page is executed against a real evidence pack in CI, so a recipe here cannot rot into something that no longer runs.
Two things are true of everything below:
- SBOMFlow observed; you decide. Nothing the engine writes is a review decision. Statuses like
needs_revieworunder_investigationmean nobody has decided yet — never this is fine. - Your decisions live in files, not in the engine.
reviews.json,vulnerability_reviews.json,waivers.jsonandapprovals.jsonare the only things that can change a finding's disposition or unblock a gate.
1. See what needs you
sbomflow review ./evidenceThe queue separates three questions you answer differently:
| Queue section | The question | Where your answer lands |
|---|---|---|
| Evidence awaiting review | Is this observed signal really evidence? | reviews.json |
| Advisory findings awaiting triage | Does this vulnerability affect our product? | vulnerability_reviews.json |
| Unresolved gaps | What evidence is missing entirely? | reviews.json (or a waiver) |
On a busy release, narrow it:
sbomflow review ./evidence --filter "severity = high"2. Understand one item before deciding it
Never decide from the queue line alone. Ask the engine what it saw and why:
sbomflow explain ./evidence --gap <gap-id>
sbomflow explain ./evidence --finding <finding-id>explain prints the requirement, the official reference, the source file the signal came from, and how the advisory matched the component. If a scan warning is bothering you, it can explain that too — and list the codes this run actually produced:
sbomflow explain ./evidence --warningBefore you spend time on individual items, check whether the release is even blocked, and by what:
sbomflow explain ./evidence --gate3. Claim the item (coordination, not a decision)
On a team, say you have picked something up so two people do not triage the same CVE:
sbomflow review ./evidence --start secure_boot_configured --as product-security --reviewer you@example.comThis records in_review in review-claims.json. It is bookkeeping only: it changes no status, unblocks no gate, and is not evidence of anything.
4. Decide the evidence items
Three verbs, and they mean different things. Always pass --reviewer — an unattributed decision is recorded, but it warns, and an auditor will ask who made it.
sbomflow review ./evidence --accept secure_boot_configured --reviewer you@example.com --note "Verified against the signed manifest" --evidence-ref "PR-123"sbomflow review ./evidence --needs-more-evidence update_mechanism_documented --reviewer you@example.com --note "Need the signed-update procedure, not the marketing page"sbomflow review ./evidence --reject secure_boot_configured --reviewer you@example.com --note "Signal came from a stale build config"--evidence-ref is where you point at the thing that convinced you: a PR, a test run, a document. It is the difference between a decision an auditor can follow and one they have to take on trust.
5. Triage a finding
A vulnerability finding needs a VEX status: affected, fixed, not_affected, or under_investigation.
sbomflow review ./evidence --triage <finding-id> --status not_affected --justification vulnerable_code_not_present --reviewer you@example.com --note "TLS stack is compiled out of this image"not_affected is the one that gets audited
not_affected is a claim about your product, and SBOMFlow will not let you make it casually. Running --status not_affected without --justification does not quietly downgrade — it refuses, exits non-zero, and prints the five valid CISA categories:
error: 'not_affected' requires a valid CISA justification. Valid: component_not_present,
vulnerable_code_not_present, vulnerable_code_not_in_execute_path,
vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist.A category on its own is not an explanation. Start from the scaffold and write the real one:
sbomflow review ./evidence --template vulnerable_code_not_presentThe scaffold tells you what a reader will need — which function or module, which version removed it, and how you confirmed that. Nothing is ever submitted anywhere; this is text for the humans who read your evidence.
6. Waive what you cannot fix yet
A waiver is an honest, time-boxed statement that you know about a finding and are shipping anyway. The reason and the expiry are mandatory, because a waiver without an end date is just a silenced alarm:
sbomflow review ./evidence --waive-finding <finding-id> --reason "Mitigated by network isolation; upstream fix lands in 2.5.0" --expires-at 2026-12-31 --reviewer you@example.comA waiver excuses that finding, on that release, until that date. It is not a VEX status and it does not claim the product is unaffected.
7. Approve the release
Approvals are per-role, and separation of duties is enforced:
sbomflow approve ./evidence --role product-security --reviewer alice@example.com --author bob@example.comCheck where the release stands against the roles it requires:
sbomflow approve ./evidence --require-role product-security --require-role release-managerIf the declared release author tries to approve their own release, SBOMFlow refuses and exits non-zero:
error: separation of duties: approver 'bob@example.com' is the declared release
author and cannot approve their own release.A release manager on leave? Someone else can approve as their delegate, which always requires a mandatory expiry — delegation is deliberately time-boxed, never open-ended:
sbomflow approve ./evidence --role product-security --reviewer carol@example.com \
--delegate-of alice@example.com --expires-at 2026-08-01T00:00:00ZThe status report shows exactly who acted: product-security by carol@example.com (delegate of alice@example.com). It satisfies the role under the same rules as any approval — separation of duties still applies (a delegate can't be the author or cover a second role), and it reopens the role automatically the moment it expires. Delegating on behalf of someone who is themselves currently delegating is refused: delegation is one hop only, so a report can always trace a chain back to a real approver.
8. Digest the session for standup
Instead of reading audit-log.jsonl by eye, ask for a summary of what was just decided:
sbomflow review ./evidence --digestRead-only — it records nothing, and refuses over a tampered audit chain like every other review write. It groups the decisions you recorded above (accepted/rejected/needs-more-evidence, findings triaged, waivers, approvals) into a Markdown summary with event_hash references, prints it, and writes session-digest.md next to the evidence. Add --since <timestamp> to narrow it to just this session when the audit log spans more than one.
9. What the auditor sees afterwards
Three surfaces, produced from the decisions you just recorded:
sbomflow review-view ./evidencereviewer.html — a static, no-JavaScript console showing every item, who decided it, when, and on what basis.
sbomflow bundle ./evidenceevidence-bundle.html / evidence-bundle.json — the portable handoff. It composes existing artifacts; it adds no authority and concludes nothing.
And audit-log.jsonl — an append-only, hash-chained record of every decision event, including the ones you later changed your mind about. Each event carries an event_hash linking it to the one before, so a decision cannot be quietly removed from the history after the fact.
What SBOMFlow will never do for you
- It will never set
not_affectedorfixedon your behalf. Only a review file does that. - It will never treat an observed signal as accepted evidence.
- It will never file, transmit, or sign a regulatory report. CRA Article 14 output is an unsigned draft, marked
submitted: false. - It will never tell you the product is compliant or conformant. The reports are engineering gap assessments, not legal conclusions.
If a finding is genuinely exploitable in your product and you cannot fix it before release, that is an escalation to your release owner — not a status you can pick in this tool.
Next
- Observed vs reviewed — the boundary, in depth.
- Read the bundle — what an auditor does with your handoff.