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_review or under_investigation mean nobody has decided yet — never this is fine.
  • Your decisions live in files, not in the engine. reviews.json, vulnerability_reviews.json, waivers.json and approvals.json are the only things that can change a finding's disposition or unblock a gate.

1. See what needs you

bash
sbomflow review ./evidence

The queue separates three questions you answer differently:

Queue sectionThe questionWhere your answer lands
Evidence awaiting reviewIs this observed signal really evidence?reviews.json
Advisory findings awaiting triageDoes this vulnerability affect our product?vulnerability_reviews.json
Unresolved gapsWhat evidence is missing entirely?reviews.json (or a waiver)

On a busy release, narrow it:

bash
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:

bash
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:

bash
sbomflow explain ./evidence --warning

Before you spend time on individual items, check whether the release is even blocked, and by what:

bash
sbomflow explain ./evidence --gate

3. 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:

bash
sbomflow review ./evidence --start secure_boot_configured --as product-security --reviewer you@example.com

This 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.

bash
sbomflow review ./evidence --accept secure_boot_configured --reviewer you@example.com --note "Verified against the signed manifest" --evidence-ref "PR-123"
bash
sbomflow review ./evidence --needs-more-evidence update_mechanism_documented --reviewer you@example.com --note "Need the signed-update procedure, not the marketing page"
bash
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.

bash
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:

bash
sbomflow review ./evidence --template vulnerable_code_not_present

The 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:

bash
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.com

A 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:

bash
sbomflow approve ./evidence --role product-security --reviewer alice@example.com --author bob@example.com

Check where the release stands against the roles it requires:

bash
sbomflow approve ./evidence --require-role product-security --require-role release-manager

If 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:

bash
sbomflow approve ./evidence --role product-security --reviewer carol@example.com \
  --delegate-of alice@example.com --expires-at 2026-08-01T00:00:00Z

The 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:

bash
sbomflow review ./evidence --digest

Read-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:

bash
sbomflow review-view ./evidence

reviewer.html — a static, no-JavaScript console showing every item, who decided it, when, and on what basis.

bash
sbomflow bundle ./evidence

evidence-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_affected or fixed on 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