Resource
The audit report template
A report has one job: let your engineers fix the right things in the right order, and let your investors and users see what was actually reviewed. This is the structure I use.
Markdown, no dependencies. Free to use and adapt; attribution is welcome but not required. When a client agrees to publication, the finished report is published in the same format.
What is in it
Executive summary
Scope, dates, result counts, the overall call, and the three things to fix first. Written for the person who will not read the rest.
Scope and assumptions
Repositories, commit, paths, what is out of scope, and every trusted role or external dependency the findings depend on.
Severity classification
Impact times likelihood, with a definition per level. The same matrix the report is graded against.
Findings summary
One table, ordered by severity, with a status per finding. The index the engineering team works from.
Detailed findings
Per finding: location, description, impact, proof of concept, recommendation, client response and verification.
Fix review
The commit each fix landed in, whether it was verified, and what changed. A fix is not marked fixed on trust.
Coverage statement
What was reviewed, what was not, and how much time was spent. The honest boundary of the assessment.
How a finding reads
The example below is written for illustration, not taken from a client audit. It shows the shape: the mechanism in the title, the impact in numbers, a proof that runs, and a fix at the level of the invariant.
### [H-01] Swap path has no minimum output
| | |
|---|---|
| **Severity** | High (Impact: High, Likelihood: Medium) |
| **Location** | src/TaxDistributor.sol:88, _swapToEth() |
| **Status** | Open |
**Description**
The tax distribution swaps the contract's token balance for ETH with
amountOutMin set to 0:
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
balance, 0, path, address(this), block.timestamp
);
The swap runs on every taxable transfer, so the price impact is paid
repeatedly, not once.
**Impact**
A searcher buys ahead of the distribution, lets it sell at the moved
price, and sells behind it. At the current pool depth a 2 ETH
distribution loses roughly 4% to slippage, and the loss compounds on
every transfer that triggers it.
**Proof of concept**
1. Fork mainnet at the deployment block and seed the pool with the
production liquidity.
2. From a second account, buy the token in the same block before the
distribution transaction.
3. Execute the distribution, then sell.
4. The attacker's balance is positive after gas; the distributor's ETH
output is below the pre-trade quote.
**Recommendation**
Compute a minimum output from a manipulation-resistant reference (a
TWAP with enough observations, or a signed quote) and pass it as
amountOutMin. If the reference is unavailable, skip the swap rather
than execute it unbounded. Add a deadline supplied by the caller; do
not use block.timestamp.
**Client response**
No response received.
**Verification**
Not yet reviewed. Want this report written against your code?
Fixed fee, fixed date, one reviewer, fix verification included.