Problem: a TU the front end could not parse left no trace. ClangTool::run's status was discarded, TUs with compile errors were analyzed on a partial AST and counted as done, and the isolated-worker path dropped TUs that never produced an AST. A scan of only broken files printed no findings and exited 0, the same as a clean scan. Separately, the scan logger wrote to stdout, so any warning corrupted a SARIF/JSON report written to stdout. Fix: every TU now yields an outcome (clean, compile-errors, analysis-failed); workers carry it on their file_finished record, and the fan-out parent adds timeout/crash/worker-exit for the TU a worker died on. tuCoverage() turns the requested file list plus outcomes into a ScanCoverage (requested TUs with no outcome are frontend-failed). SARIF gets a run `invocations` entry (executionSuccessful, one toolExecutionNotification per gap); findings JSON gets an additive `coverage` object. A scan with any gap, or with nothing analyzed, exits 2 after writing its report. CLI diagnostics now go to stderr, as docs/internal/cli-philosophy.md principle 12 requires. USAGE.md and AGENTS.md document the coverage fields and the exit code. Test: tests/unit/test_scan_coverage.cpp (classification, path spelling, complete/untracked controls, merge, both writers, and unchanged output without coverage); test_cli_paths "[coverage]" covers a clean TU (exit 0, complete) and a TU with a missing include (exit 2, gap in JSON, SARIF on stdout parseable with executionSuccessful=false). Before: both probe files exited 0 with no coverage information. Full suite: 139/139.
131 lines
5.4 KiB
Markdown
131 lines
5.4 KiB
Markdown
---
|
|
description: Fold repo supplement — SAST boundaries, router contract, and validation rules
|
|
argument-hint: "[TASK=<description>] [MODE=engineering] [PHASE=<start|checkpoint|complete>]"
|
|
---
|
|
|
|
# AGENTS.md (Repo Supplement)
|
|
|
|
Operating model (retrieval contract, execution loop, metadata, handoff,
|
|
ask-first list): `../yams/docs/prompts/PROMPT-eng-codex.md`. C++ design/TDD/
|
|
assertion/profiling flow: `../yams/docs/prompts/PROMPT-yams-cpp-workflow.md`.
|
|
Use `../yams/AGENTS.md` as the reference style for keeping this file lean:
|
|
generic agent behavior belongs in the shared prompt files; this file holds only
|
|
what is specific to Fold.
|
|
|
|
## Project Contract
|
|
|
|
fold is a C++ SAST with two analysis paths:
|
|
|
|
- Source path: Clang LibTooling plus bounded Z3-backed detectors.
|
|
- Binary path: radare2-backed heuristic extraction for strings, symbols, calls,
|
|
sections, xrefs, and simple pseudocode patterns.
|
|
|
|
Keep these paths separate unless a task explicitly asks for router work. Do not
|
|
try to lift binary instructions into the source symbolic IR as a shortcut.
|
|
|
|
## CLI And Router Work
|
|
|
|
Read `docs/research/cli-router.md` before changing scan-mode behavior.
|
|
|
|
The intended CLI contract is:
|
|
|
|
- `fold scan --mode source ...` runs only the source backend.
|
|
- `fold scan --mode binary --binary <path>` runs only the binary backend.
|
|
- `fold scan --mode hybrid --compile-db <db> --binary <path>` runs both through
|
|
the scan router.
|
|
- `fold scan --mode auto ...` chooses hybrid when both source and binary inputs
|
|
are present.
|
|
|
|
Compatibility requirements:
|
|
|
|
- Keep existing `--input-type source|binary` behavior until explicitly removed.
|
|
- Existing `fold scan --input-type binary --binary <path>` commands must keep
|
|
working.
|
|
- Exit codes stay stable: `0` no actionable findings, `1` actionable findings,
|
|
`2` input/config/report failure, including a scan that left any requested
|
|
translation unit unanalyzed (see `coverage` in docs/USAGE.md).
|
|
- Reports must preserve per-finding backend provenance.
|
|
|
|
Router implementation rules:
|
|
|
|
- Put orchestration in `include/fold/cli/scan_router.hpp` and
|
|
`src/cli/scan_router.cpp` when implementation starts.
|
|
- Prefer small router-specific structs over widening `AnalysisBackend`.
|
|
- Hybrid mode should begin as `merge-only`; add hint exchange only after tests
|
|
prove independent source and binary behavior is unchanged.
|
|
- Binary hints may focus source work, but must not silently hide source coverage
|
|
unless the user requested a capped hybrid candidate set.
|
|
- Source hints may focus binary decompilation, but raw binary string/symbol scans
|
|
should still run unless explicitly disabled.
|
|
|
|
## Backend Boundaries
|
|
|
|
- `include/fold/core/analysis_backend.hpp` is intentionally narrow.
|
|
- `ClangBackend` owns source analysis and delegates to the mature detector
|
|
pipeline.
|
|
- `R2Backend` owns radare2 sessions and binary heuristics.
|
|
- Keep binary findings heuristic unless there is source/Z3 corroboration.
|
|
- Keep source solver metrics and binary heuristic metrics separate in profiles.
|
|
|
|
## Validation
|
|
|
|
Run focused tests for the area you changed, then the full suite for CLI/router
|
|
or backend changes:
|
|
|
|
```sh
|
|
ninja -C build/release
|
|
meson test -C build/release --print-errorlogs
|
|
git diff --check
|
|
```
|
|
|
|
Useful focused tests:
|
|
|
|
```sh
|
|
meson test -C build/release fold:scan_e2e --print-errorlogs
|
|
meson test -C build/release fold:binary_backend --print-errorlogs
|
|
meson test -C build/release fold:subprocess_fanout --print-errorlogs
|
|
meson test -C build/release fold:cross_tu_security --print-errorlogs
|
|
```
|
|
|
|
For binary or hybrid changes, run a curl source/binary smoke if the fixture paths
|
|
are available in the workspace.
|
|
|
|
## Worktree Safety
|
|
|
|
This repository often has unrelated local edits from benchmark and experiment
|
|
runs. Do not revert or rewrite files you did not intentionally modify. Keep
|
|
changes minimal and check `git diff --check` before handing off.
|
|
|
|
## Cross-Language Wire Formats
|
|
|
|
When a production path spans Rust and C++ (fold-rust NDJSON → RustBackend
|
|
parsing, fold-swift NDJSON → SwiftBackend parsing), JSON/NDJSON key names
|
|
must be centralized in a single source of truth. Do not duplicate string
|
|
literals across Rust and C++.
|
|
|
|
Canonical key homes:
|
|
|
|
- IR expression trees: `include/fold/ir/expr_json.hpp` (`namespace json_keys`).
|
|
- Frontend record envelopes (`info`/`error`/`function`/`finding`/`summary`/
|
|
`symbolic-summary`/`interface`/`ir`) and their data keys:
|
|
`include/fold/frontend/ndjson_keys.hpp`
|
|
(`namespace fold::frontend::ndjson_keys`).
|
|
|
|
Rules:
|
|
|
|
- C++ serialization (`toJson`) and deserialization (`fromJson`, backend
|
|
parsing) MUST use these constants, not raw string literals.
|
|
- Rust/other-language producers MUST match the canonical names. Document the
|
|
expected keys in a comment block at the emitter site
|
|
(`tools/fold-rust/src/main.rs` near `FindingData`, `tools/fold-swift/
|
|
fold_swift.cpp` near `emitFinding`).
|
|
- When a new expression shape (Call, Member, ArraySubscript, etc.) or NDJSON
|
|
record shape is added, add its canonical keys to the owning header first,
|
|
then implement on both sides.
|
|
- New `finding_kind` values must also be added to `FindingKind` +
|
|
`findingKindName()` (`src/report/finding.cpp`) and to the backend parse
|
|
tables; unknown wire kinds map to `FindingKind::UnknownForeign` with a
|
|
warning, never silently relabeled.
|
|
- `fromJson` may accept legacy key aliases (read: `j.value("canonical_key",
|
|
j.value("legacy_key", default))`) for backward compatibility, but MUST
|
|
prefer the canonical form on write.
|