A Solidity audit graph that shows its gaps instead of hiding them
DEV Community

A Solidity audit graph that shows its gaps instead of hiding them

A LABOUR OF LOVE

Most smart contract audit tooling answers one question: "what's wrong?" The trouble is that it can answer with confidence it hasn't earned. A clean scan gets read as "safe", and a long list of detector hits buries the three things that matter.

I came to AI security from Web3 auditing, and I built my own tooling along the way. The one I want to share is Forge CodeGraph. It starts from a different question: "what can I actually establish about this code, and what can't I?"

The three questions behind it

My auditing starts from three buckets, and every finding I've ever made falls into one of them: Access: who? Who has access, who should, and can someone who shouldn't reach the same privileged state through another path? Math and value: how much? Can balances, shares, prices, fees, rewards, debt, collateral, withdrawals or rounding be manipulated? Order and state: when? Was state updated before or after value moved? Can ordering, callbacks, reentrancy or stale state break the intended invariant?

Around them sit a few habits: check assumptions, check alternate paths, ask what must always be true, and expect attackers to combine everything you missed.

Forge CodeGraph is built to serve those questions. It helps most with Access and Order: who can reach a privileged function, and what happens around an external call. It helps least with Math. Following the numbers, rounding and edge cases is still human work, and the tool doesn't pretend otherwise.

What it does

Forge CodeGraph is a local, evidence-backed workspace for Solidity security review. You give it a fixed source snapshot and a local solc compiler. It builds a graph from compiler and structural evidence, and every node and relationship carries its provenance: snapshot, scope, compilation unit and source span.

Then you can ask bounded, read-only questions of that graph:

  • who calls this function, and what does it call
  • which functions touch which state
  • where the external calls are
  • which functions are privileged
  • what the upgrade paths and dependencies look like

Built for the code you actually get handed

Audits don't arrive neatly on Solidity 0.8.x. Plenty of live protocols run older compilers, so a tool that only understood modern Solidity would be useless for a lot of real work.

Forge CodeGraph ingests Solidity 0.6 and later, and its integration tests run real compilers across 0.6.0, 0.6.12, 0.7.0, 0.7.6, 0.8.0, 0.8.13, 0.8.21, 0.8.24, 0.8.28 and 0.8.36. Pre-0.8 contexts keep their unchecked arithmetic semantics instead of being silently treated as 0.8. If it can't tell which compiler built something, it records a gap rather than assuming. Versions before 0.6 are not supported yet, and the test matrix doesn't prove every language construct is covered.

Source: README.

Gaps are first-class

Anything the tool can't establish is recorded as an explicit gap. It doesn't guess. Assembly and Yul are not interpreted, and always produce a review gap. Unresolved imports, compiler failures and dynamic behaviour it can't resolve either fail loudly or show up as gaps. It also won't produce a "partial success" graph when the compiler fails.

That matters for auditors. When you know exactly where the tool stopped seeing, you know where to spend your own attention.

Humans decide, AI proposes

There is an optional AI investigation path, and it's deliberately boxed in:

  • AI output is a non-authoritative proposal. It can't change graph facts, audit coverage, review state or findings.
  • Only a named human operator can record an observation or hypothesis, and neither counts as a validated finding.
  • Local semantic search runs through Ollama and is navigation help only. Similarity is never treated as evidence.
  • Any call to an outside AI provider goes through a named profile with a fixed endpoint, allowed models and limits. Prompts and responses are kept as encrypted local transcripts, and the encryption key stays with the operator.

Source code is sensitive audit material, so everything is local by default.

Also in the repo

  • An append-only audit coverage ledger, kept separate from detectors and finding state, so "what did I review" stays a different question from "what did I find".
  • A keyboard-driven graph explorer with read-only database access.
  • Local Anvil node tooling for call traces and transaction replay. These are evidence aids and do not automatically become graph facts.

What it is not

It is not a vulnerability oracle. Per its own docs there is no automatic vulnerability or severity analysis, assembly isn't interpreted, and proxy, oracle, bridge and storage-layout analysis is not complete. An empty result or a clean graph is not proof that code is safe.

Graph candidates still need manual review and reproducible proofs of concept before they become reportable findings. I think that honesty is the useful part. A tool that is explicit about its limits is one you can reason about.

Try it

It needs Python 3.12 and a local Solidity compiler. The README walks through ingestion, analysis and your first query: https://github.com/RickDevops87/forge-codegraph

If you audit Solidity, I'd like to hear where the graph helps and where it falls short. Issues and feedback are welcome on the repo.

Rick Nieuwoudt is an engineer and security specialist at Forge Web & AI security, and a former Web3 auditor.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.