Guide
How to assess an incoming vulnerability report
The method below is what a careful engineer does with a report, whether or not any tool is involved. It is written for the case where nobody on the team does this full time.
A report arrives. Someone read the code, formed a theory, and wrote it down with enough detail to be worth your time. Reports like that are worth taking seriously, and reports like that are still frequently wrong — sometimes because the researcher misread the code, and sometimes because the code changed after they looked.
The work of assessing a report is not reading it carefully. It is deciding which piece of code the claim is about, then establishing what that code does.
1. Restate the claim as an assertion about code
Reports are written in the language of users and endpoints. Rewrite each one as a statement about your code, because that is the only version you can check.
“Users can read other customers' invoices” becomes: the invoice export path selects rows using an account identifier supplied by the caller, and no check binds that identifier to the authenticated session. A report often contains three or four claims of different strengths. Split them, and note which ones you could not check at all.
2. Find the entry point
Locate the handler the report describes. Note what the middleware chain actually is, not what you assume it is — the interesting bugs live in the difference between those two things. Then write down what the chain does establish: authentication, a permission scope, a rate limit, a feature flag.
3. Follow attacker-controlled input to the sensitive operation
Take the value the report says the attacker controls, theaccount_id or the object identifier, and follow it forward until it reaches something that matters: a query, a file read, a write, a cross-service call. The question at each hop is narrow. Does anything between the entry point and the operation compare this value against the identity in the request?
This is the step most worth doing slowly. The check you are looking for is often three files away from the handler, frequently in a store or repository function that takes a bare identifier and trusts its caller.
4. Check the control, then check what it covers
When you find an authorization check, do not stop. Read what it actually compares. A check that confirms “this session may call invoices:read” is not a check that the session may readthese invoices, and a codebase can contain both kinds confidently.
5. Look for the reason it might not be exploitable
Do this on purpose, and take it seriously. Reachability: is the route registered, is the feature enabled, does the attacker hold the required scope. Representation: does the data layer enforce anything the handler does not, such as row-level security or a filtered view. Deployment: an edge proxy or gateway can reject a request the repository would accept.
If you find a real defense, that is the finding — a rejection needs to name what disproves the claim, not just fail to reproduce it.
6. Decide, and separate the decision from the severity
Four honest answers: the code supports the claim; the code contradicts it; the evidence is not sufficient to decide; or the report is real but not a vulnerability in your product. Anything that needs a deployed environment to settle — does the gateway block it, does the database role have the access, is the feature on — is not something code review settles, and saying so is a finding, not a gap.
Severity is a separate judgement, made by someone who knows how the feature is deployed and used.
7. Write down what you could not resolve
This is the part that pays off twice. The researcher will follow up, and an auditor or a colleague will ask in eight months. Record the claims, the files and line ranges you relied on, the counterevidence you considered, the open questions, and the decision with the name of the person who made it.
What “inconclusive” should mean
Inconclusive means the evidence was not enough. It is not a polite version of false, and it should never be filed as one: the report keeps its evidence and its place in the history, and a human decides whether to widen access, ask the researcher a question, or close it.
Treat it as a normal result. If your process only has two buckets, a meaningful share of real reports will end up in the wrong one, and the cost lands on the researchers who were right.
Where the time goes
Most of it is not reading the report. It is finding the right code, remembering which layer enforces what, and reconstructing a conclusion you can defend later. Those are exactly the parts that are tedious rather than skilled, which is why they are worth automating and the judgement is worth keeping.
Run it on a report you already have.
The same seven steps, run as a bounded read-only investigation on an Agent you host, with the evidence attached to the report. One workspace is $99 USD a month, with no trial.