Fix audit findings: soundness, coverage reporting, docs, and review follow-ups #1

Merged
trevon merged 18 commits from fix/audit-findings into master 2026-09-25 06:48:08 +00:00
3 changed files with 73 additions and 56 deletions
Showing only changes of commit 2945e59bc2 - Show all commits

docs: trim the README and move the performance roadmap to docs/

The README listed 11 of fold's 17 detectors, never explained the
confidence tiers every finding carries, did not mention the new exit
code 2 / coverage reporting, and spent half its length on a
performance roadmap.

It now says what fold is, defines the five tiers (from
include/fold/report/finding.hpp, including concrete-witness and
latent), what "bounded" means after the loop-exit fix, a quick start,
output formats and exit codes (0 none, 1 any unsuppressed finding, 2
unwritable report or any unanalysed translation unit), and links. The
detector list defers to the complete table in docs/USAGE.md.

The performance section moves to docs/architecture/performance.md, with
one correction: the gold set was derived from fold's own reviewed output
and is matched by detector kind and file, so it catches drift rather
than measuring precision/recall independently. The lint pre-commit hook
note moves to BUILD.md.
trvon 2026-09-24 21:11:54 -06:00
Signed by: trevon
GPG key ID: 38686EA797D7A8A7

View file

@ -8,6 +8,9 @@
# tests: meson test -C build/release
```
`setup.sh` also enables the tracked lint pre-commit hook
(`.githooks/pre-commit`).
Linux users without root can validate against a clean container:
```sh

View file

@ -1,72 +1,57 @@
# fold
Bounded symbolic SAST for C++ — clone detection, dead-branch analysis,
and security vulnerability scanning via Clang LibTooling and Z3.
Bounded symbolic static analysis for C++: dead-branch and hazard detection
(null dereference, division by zero, integer overflow, out-of-bounds, and more),
clone and equivalence detection, and taint tracking, built on Clang LibTooling
and the Z3 solver. Rust (HIR), Swift and binary scan modes also exist; the Rust
detectors are heuristic today.
## Build
Every finding carries a confidence tier that says how it was established:
| Tier | Meaning |
| --- | --- |
| `concrete-witness` | A fuzzer ran the solver's witness under a sanitizer and produced a correlated crash. |
| `proved` | An exact bounded model proved it: SAT for a hazard witness, or UNSAT for a negated invariant. |
| `latent` | Proved inside the function, but only reachable if a caller supplies a value the callee never guards. |
| `heuristic` | Some part of the model was over-approximated. |
| `syntactic` | An AST pattern or hash match only. |
"Bounded" is literal: loops are unrolled to a fixed depth, and a condition that
depends on a loop's exit is never reported as proved. The contract behind each
tier is in [docs/research/soundness.md](docs/research/soundness.md).
## Quick start
```sh
./setup.sh release
```
Requires system-installed LLVM/Clang 18+ and `libz3` development
headers; Conan handles the rest (spdlog, CLI11, nlohmann_json,
sqlite3, Catch2). **See [BUILD.md](BUILD.md) for per-platform
apt/brew install lines and a Docker validation harness.** Setup also enables
the tracked lint pre-commit hook (`.githooks/pre-commit`).
## Usage
```
./setup.sh release # needs LLVM/Clang 18+ and libz3 headers; Conan handles the rest
fold scan --compile-db compile_commands.json \
--detectors null-deref --detectors div-by-zero \
--format sarif --output findings.sarif
```
Detectors: `clone`, `equiv`, `class-dup`, `dead`, `null-deref`,
`div-by-zero`, `int-overflow`, `oob`, `int-truncation`, `sign-conversion`,
`taint-track`.
Output formats: `sarif`, versioned native `findings-json`, and `markdown`.
`json` remains a deprecated compatibility alias for SARIF. Query existing Fold
reports without scanning raw source text:
```sh
fold grep "null pointer" findings.sarif --field message --ignore-case
```
See `docs/USAGE.md` for the full CLI and MCP/LSP reference, and
`docs/compatibility.md` for deprecated-name migrations and removal criteria.
Per-platform install lines and a Docker validation harness are in
[BUILD.md](BUILD.md).
## Research docs
Output formats are `sarif`, versioned `findings-json`, and `markdown`. Both
machine formats record which translation units were analysed. A scan exits `0`
with no findings, `1` with any unsuppressed finding, and `2` when a report could
not be written or any translation unit was not analysed (compile errors,
frontend failure, crash, timeout): no findings from an incomplete scan is not
evidence of no bugs.
`docs/research/` — architecture, benchmarks, prior art, frontier roadmap,
soundness proofs, and audit artifacts.
## Documentation
## Performance architecture
Optimization is measurement-first and gate-guarded. Every candidate
change runs through the manifest-driven evaluation harness
(`tests/benchmarks/projects/*.json`), which enforces finding-fingerprint
stability, gold precision/recall (`bench/gold/`), reviewed dispositions, and
wall/RSS regression baselines. A full ledger of measured wins and closed
hypotheses lives in
[`docs/research/optimization-loop-2026-07-30.md`](docs/research/optimization-loop-2026-07-30.md).
The one structural work on the roadmap is **single-pass graph building**.
Today a source scan parses each translation unit twice — once to build the
project call graph, once to run detectors — because the detector pass consumes
the graph (cross-TU candidate expansion and summary priming). The planned
change collapses these into one parse per TU that produces graph contributions
and runs the intra-TU detectors in the same pass, retaining only light per-TU
edge/signature records, and runs the cross-TU-dependent closure after every TU.
This is deliberately chosen over precompiled-header/module caching to
**preserve portability**: it introduces no clang-version-locked serialized
artifacts, no SDK/toolchain-locked module cache, and no `-std`/`-I`/`-D`
flag-matching bug surface — every machine that consumes a
`compile_commands.json` today keeps working unchanged. Correctness parity for
the deferred closure is policed by the existing finding-fingerprint and gold
gates.
- [Usage](docs/USAGE.md): the full CLI (scan modes, scopes, confirmation,
fuzzing), every detector with its CWE and method, MCP/LSP, and project
evaluation.
- [MCP tools](docs/mcp-tools.md) and [compatibility](docs/compatibility.md)
(deprecated names and removal criteria).
- [Positioning](docs/positioning.md) and [prior art](docs/research/prior-art.md):
how fold relates to other analyzers.
- [Performance](docs/architecture/performance.md): the evaluation gates and the
single-pass graph-building plan.
- [Architecture](docs/architecture/) and [research](docs/research/) notes.
## License

View file

@ -0,0 +1,29 @@
# Performance
Optimization is measurement-first and gate-guarded. Every candidate change runs
through the manifest-driven evaluation harness
(`tests/benchmarks/projects/*.json`), which enforces finding-fingerprint
stability, reviewed dispositions, wall-time and RSS regression baselines, and a
gold set (`bench/gold/`). The gold set was derived from fold's own reviewed
output and is matched by detector kind and file, so it catches drift; it is not
an independent precision or recall measure. A ledger of measured wins and
closed hypotheses is in
[the optimization loop](../research/optimization-loop-2026-07-30.md).
## Single-pass graph building
The one structural item on the roadmap is single-pass graph building. A source
scan currently parses each translation unit twice: once to build the project
call graph, and once to run detectors, because the detector pass consumes the
graph (cross-TU candidate expansion and summary priming). The planned change
collapses these into one parse per TU that produces graph contributions and runs
the intra-TU detectors in the same pass, keeping only light per-TU edge and
signature records, then runs the cross-TU-dependent closure after every TU. See
[the design](../research/single-pass-graph-design.md).
This is chosen over precompiled-header or module caching to preserve
portability: it introduces no Clang-version-locked serialized artifacts, no
SDK- or toolchain-locked module cache, and no `-std`/`-I`/`-D` flag-matching
bug surface, so every machine that consumes a `compile_commands.json` today
keeps working. Correctness parity for the deferred closure is policed by the
existing finding-fingerprint and gold gates.