Report writing lessons: why submissions don't clear review
Every disclosure we ingest is read and critiqued before it is indexed. The ones that don't make it fail for a surprisingly small number of repeated reasons. This page collects those patterns, with real anonymized examples pulled straight from our review notes — framed as what would have made each one publication-ready.
Case studies are anonymized. We never name the researcher, the programme, or link the original submission. The point is the writing, not the person.
Severity claimed, not demonstrated
A critical or high rating asserted in prose with no impact path, no funds-at-risk figure and no preconditions.
Severity is the first thing a triager sanity-checks, and it is the easiest claim to falsify. When the write-up states an impact tier but the body only describes a code smell, the reviewer has to downgrade or reject rather than do the impact analysis on the researcher's behalf.
What to do instead
- State the concrete loss: which asset, whose balance, and an upper bound in tokens or USD.
- List the preconditions honestly — required roles, market state, ordering, or capital needed.
- Map the impact to the programme's own severity matrix and quote the row you're claiming.
- If the realistic outcome is griefing or a temporary DoS, say so; an accurate medium beats a rejected critical.
Thin or boilerplate remediation
"Use a reentrancy guard" or "add proper validation" with no reference to the actual function being fixed.
Generic remediation reads as template filler and tells the maintainer nothing they didn't already know. It also signals the researcher didn't read enough of the codebase to know where the fix belongs.
What to do instead
- Name the function and the exact lines that change.
- Show the corrected ordering or check, ideally as a small diff.
- Note the trade-offs — gas, upgrade path, existing callers that would break.
- Call out the other call sites that share the same flaw, if any.
Attack path that describes the fix, not the exploit
The exploit section explains what the code should have done instead of walking an attacker through what they actually do.
The attack path is the section that proves exploitability. Restating the remediation there leaves the reviewer with an assertion of impact and no sequence to verify, so the report reads as theoretical.
What to do instead
- Write it as ordered steps from the attacker's position: initial state, each call, the state after it.
- Include the attacker's starting capital and any flash-loaned amount.
- Finish on the measurable outcome — balance delta, drained pool, forged authorization.
- Keep the remediation strictly in the remediation section.
No proof of concept
A plausible bug described entirely in prose, with no test, script or transaction trace that anyone can run.
A runnable PoC converts a claim into a reproducible fact. Without one the maintainer has to rebuild the scenario themselves before they can even confirm the finding exists, which is where most submissions stall.
What to do instead
- Ship a Foundry/Hardhat test, an Anchor test, or a simulated transaction against a fork.
- Pin the fork block or the deployed addresses used, so the run is deterministic.
- Assert on the outcome (`assertEq`, balance delta) rather than printing logs.
- Keep it minimal — one file, no scaffolding the reviewer has to install.
Copied source text instead of analysis
Sections pasted from the codebase, docs or an earlier report, with no original reasoning added.
Pasted material tells the reviewer what the code says, not what's wrong with it. It also makes duplicate detection fire, because near-identical text often already exists in the programme's history.
What to do instead
- Quote only the few lines that matter, then explain the invariant they break.
- Write the analysis in your own words even when the vulnerable pattern is well known.
- Cite prior art explicitly instead of absorbing it silently.
Missing or placeholder sections
Empty summary, a one-line technical section, or headings left as TODO in a submitted report.
Incomplete structure gets triaged last and rejected first. Each missing section is a question the reviewer has to ask, and a round trip that a busy programme often doesn't make.
What to do instead
- Fill all four: summary, technical analysis, attack path, remediation.
- Write the summary last, as a standalone paragraph that survives being read alone.
- Delete headings you can't populate rather than leaving them empty.
Likely duplicate of a known issue
A finding that matches a previously disclosed report, an audit finding, or a known-issue entry in scope.
Duplicates are the single largest source of zero-payout submissions. Most are avoidable with fifteen minutes of searching before writing anything.
What to do instead
- Search prior disclosures, audit reports and the programme's known-issues list first.
- If it's adjacent to a known issue, lead with what is genuinely new about your variant.
- Include the date and commit you tested against — a fix landing mid-review is common.