# Usage ## CLI ``` fold scan [options] [--compile-db | ] fold scan --mode hybrid --compile-db --binary [options] fold scan --input-type binary --binary [options] fold confirm [options] fold quality [--compile-db ] [--manifest ] [options] ``` `--compile-db` is optional. If omitted, pass source files as positional arguments and fold infers compile flags: ```sh fold scan src/*.cpp --detectors null-deref # Or with explicit C++ standard: fold scan --std c++20 file.cpp --detectors dead ``` If a `compile_flags.txt` exists in the source tree, fold reads it automatically (clang-tidy convention). ### quality command `fold quality` is the local quality gate. It checks formatting and diff hygiene, runs the focused evaluator/source-structure tests, evaluates a manifest policy, and, when `--compile-db` is supplied, scans changed C/C++ files. It writes an aggregated `summary.json` under `build/quality` by default. ```sh fold quality --compile-db build/release/compile_commands.json fold quality --skip-policy --allow-findings ``` Use `--out-dir`, `--manifest`, and the individual `--skip-*` switches to adapt the gate for a focused local iteration. Exit status `1` means a completed quality gate failed; `2` means setup or execution failed. ### scan options | Flag | Description | |------|-------------| | `--detectors NAME ...` | Which detectors to run (default: all) | | `--format sarif\|findings-json\|markdown\|json` | Output format (default: SARIF). `findings-json` emits the versioned native envelope; `json` is a deprecated SARIF alias. | | `--output PATH` | Write output to file | | `--processes N` | Worker processes (default: auto). Ordinary source scans use an isolated worker even at `1`; profile/incremental scans currently use the in-process serial path. | | `--budget-seconds N` | Per-TU time budget | | `--per-query-ms N` | Per-Z3-query timeout | | `--loop-unroll N` | Loop unrolling depth (default: 3) | | `--exclude-glob PATTERN` | Skip files matching glob. Relative patterns also match absolute compile-database paths at component boundaries. | | `--trace` | Log Z3 query traces | | `--scope security\|quality\|redundancy\|binary\|all` | High-level scan scope; expands to the matching detector category. Composes with `--detectors` | | `--profile` | JSON performance profile (metrics). A value is a deprecated alias for `--preset` | | `--profile-output PATH` | Atomically write profile JSON and imply `--profile`; must differ from `--output`. | | `--preset NAME` | Apply a modeling preset (currently `freestanding`, the [embedded-firmware bundle](#freestanding-preset)) | | `--complexity-threshold N` | Cyclomatic-complexity threshold for the `complexity` detector (default 15) | | `--profile-solver` | Per-detector Z3 statistics | | `--profile-reach` | Encoder coverage report | | `--incremental` | Skip functions unchanged since last scan | | `--build-summaries` | Build leaf-function symbolic summaries | | `--config-profile PATH` | Load a strict versioned runtime-configuration profile and persist privacy-safe guarded callsites (source/hybrid only) | | `--portfolio` | Use parallel Z3 tactic portfolio | | `--nullable-allocations` | Opt in to heuristic null-deref findings for unchecked `malloc`/`calloc`/`new` results | | `--suppressions PATH` | Suppress findings by detector, `file:line`, or finding-id prefix | | `--baseline PATH` | Apply previous-scan baseline JSON (`accepted`/`dismissed` findings) | | `--mode auto\|source\|binary\|hybrid\|rust\|swift` | Select scan router mode (default: `auto`) | | `--input-type source\|binary` | *(hidden)* Deprecated compatibility alias for source/binary mode | | `--binary PATH` | Binary, object file, or firmware image for binary mode | | `--binary-max-functions N` | Limit binary function extraction (`0` = all) | | `--hybrid-strategy merge-only` | Hybrid routing strategy. Unimplemented hint-exchange strategies are rejected rather than silently falling back. | | `--source-detectors NAME ...` | Source-side detector override for hybrid scans | | `--binary-detectors NAME ...` | Binary-side detector override for hybrid scans | | `--havoc-external-symbols` | Model external/linker symbols (`_edata`, weak, `[[gnu::section]]`) as opaque addresses instead of concrete constants | | `--havoc-statics-at-entry` | Havoc mutable static/global state to an unknown value at function entry unless provably single-assignment | | `--volatile-read-havoc` | Treat each volatile/MMIO read as a fresh unknown (reserved; volatile derefs already return fresh unknowns) | | `--suppress-loop-bound-always-conditions` | Don't report a loop's own controlling predicate as always-true when the proof leans on the bounded-unroll loop bound | Use `fold scan --advanced-help` for the generated detector-tuning, profiling, cache, and quarantine option table. These flags remain callable but are omitted from normal help to keep the common scan workflow concise. Native finding JSON uses the stable envelope `{"output_version":1,"tool":{"name":"fold","version":"..."},"findings":[...]}`. SARIF remains the interoperability format; Markdown remains the human-readable report. Requested report/profile paths are written atomically; failures return exit `2`. Console counts and both machine formats use the same filtered and deduplicated finding set. Both machine formats record which C/C++ translation units were analyzed: the native JSON gains a `coverage` object (`tus_attempted`, `tus_analyzed`, and one `gaps` entry per unanalyzed TU with a reason: `compile-errors`, `frontend-failed`, `analysis-failed`, `timeout`, `crash`, `worker-exit`, or `quarantined` for a TU a prior run's quarantine skipped), and SARIF carries the same data as an `invocations` entry with `executionSuccessful` and one `toolExecutionNotifications` entry per gap. A scan with any gap, or one that analyzed no TU, exits `2` after writing its report: no findings from an incomplete scan is not evidence of no bugs. Diagnostics go to stderr, so a report written to stdout stays parseable. `fold --version` reports the Meson semantic version plus Git revision and a `.dirty` suffix for builds configured from modified trees. SARIF, native JSON, profiles, and benchmark run artifacts retain the same provenance. ### Query existing finding reports `fold grep` searches Fold SARIF and native findings-json reports, not arbitrary source text. Matching is bounded fixed-string matching; regex input is not accepted. ```sh fold grep "null pointer" findings.sarif --field message --ignore-case fold grep "fold.null-deref" findings.sarif --field rule \ --backend source --path-glob 'src/*.cpp' --actionable-only fold grep "token" source.sarif binary.json --format json --output matches.json ``` Searchable fields are `message`, `id`, `rule`, `path`, `function`, and `evidence`; omitting `--field` searches all of them. Typed filters cover rule, confidence, backend provenance, path glob, baseline status, and suppression. `--offset` and `--limit` provide deterministic ID/location-ordered pagination. JSON output includes totals, actionable totals, next-offset, has-more, and truncation metadata while retaining complete finding records on the selected page. Exit status is `1` when the query matches an unsuppressed finding, `0` when it does not, and `2` for malformed reports or other input/output failures. ### Configuration profiles `--config-profile` accepts a strict version-1 JSON artifact for source and hybrid scans. It records normalized guards on callsites without pruning graph edges; whole-program reachability classifications are a separate analysis phase. Profiles support typed Boolean or signed-integer globals, entry arguments, zero-argument accessors, and `getenv` set/unset state: ```json { "version": 1, "entrypoints": ["main"], "assumptions": [ {"kind": "global", "selector": "feature_enabled", "value": false}, {"kind": "entry_arg", "entrypoint": "main", "index": 1, "value": 4}, {"kind": "getenv", "selector": "FEATURE_X", "state": "unset"}, {"kind": "accessor", "selector": "Config::enabled", "value": true} ] } ``` Unknown fields, duplicate selectors, type mismatches, unsupported versions, and invalid entrypoint references fail with exit `2`. Persisted guards contain only opaque SHA-256 symbol IDs; performance telemetry exposes the profile hash, version, and aggregate projected/unconditional/unknown counts, never selectors or source expressions. ### freestanding preset `fold scan --preset freestanding ...` bundles the four embedded-firmware modeling toggles plus secondary-section rendering. On freestanding/bare-metal code fold's bounded symbolic engine otherwise over-reports: it concretizes values it cannot soundly know (linker symbols, volatile/MMIO reads, mutable statics) and reports the bounded-unroll in-bound loop guard as a universal truth. Each shortcut produces a false `Proved` finding, which is why fold is advisory rather than gating on firmware. The preset is equivalent to: ``` --havoc-external-symbols --havoc-statics-at-entry --volatile-read-havoc \ --suppress-loop-bound-always-conditions # + redundancy-lane-secondary ``` | Gap | Symptom | Fix | |-----|---------|-----| | A — bounded unroll | `i < 6` / `i >= 0` loop guard reported always-true | The loop induction variable is pinned with a `LoopBoundUnroll` tracked assumption; when a proof's Z3 UNSAT core leans on it the finding is downgraded `Proved → Heuristic` or suppressed | | B — linker symbols | `dst < &_edata` always-true | External/linker symbol addresses become opaque unknowns (soundness fix — the real bug also becomes findable) | | C — volatile reads | busy-wait exit edge pruned | Volatile/MMIO loads return a fresh unknown per read | | D — mutable statics | `heap + n > limit` always-false (`_sbrk`) | Mutable static locals are havoc'd at entry instead of re-applying the initializer each call | The four modeling toggles are gated and default off, so a normal `fold scan` (no preset) behaves exactly as before. Under the preset the syntactic redundancy lane (clone/equiv/dead/always-condition) renders as a clearly-secondary report section (markdown) or carries a `properties.category: "redundancy"` tag (SARIF/JSON) so it doesn't bury the security findings. ### binary backend The binary backend is a prototype that uses radare2 to recover functions, sections, calls, strings, and pseudocode-like text. It reuses fold's `Finding` and reporting pipeline, but it does not yet run the Clang/Z3 source detectors on a lifted IR. Current binary findings are heuristic and focus on: * hardcoded secrets, API keys, URLs, and private-key markers in binary strings * calls to known unsafe C routines such as `strcpy`, `sprintf`, and `gets` * simple null-deref patterns in radare2 pseudocode Example: ```sh fold scan --input-type binary --binary /usr/bin/curl \ --binary-max-functions 64 --detectors security --format sarif ``` `--binary-max-functions` is useful for smoke tests and large system binaries. Use `0` (the default) for a full binary scan. Rust programs can use the same binary path after compilation: ```sh rustc -C opt-level=0 src/main.rs -o target/fold-rust-smoke fold scan --mode binary --binary target/fold-rust-smoke --detectors security ``` ### rust frontend Rust source inputs route through `fold-rust` when `--mode rust` is selected or when auto mode sees `.rs` files / Cargo crate directories. Use binary mode for compiled Rust artifacts; source and binary provenance stay separate. ```sh fold scan --mode rust path/to/crate --detectors rust-security --format sarif ``` `fold-rust` itself requires a nightly `rustc_private` analyzer at Rust 1.98 or newer with `rustc-dev`, `rustfmt`, and `llvm-tools-preview`. The authoritative analyzer pin is `tools/fold-rust/rust-toolchain.toml`; Meson does not duplicate it. A crate may select a different Cargo target nightly: the workspace wrapper keeps using the analyzer binary's linked compiler while preserving Cargo's crate graph and arguments. Inspect the compiled analyzer with: ```sh fold-rust toolchain-info python3 scripts/check_rust_toolchain_skew.py \ --output build/release/meson-logs/rust-toolchain-skew.json ``` The check dynamically uses installed toolchains, exercises same-version and newer-target scans, records exact releases/components, and emits an explicit skip when no newer nightly is installed. Stable toolchains are listed with the actionable reason they cannot be used to build the `rustc_private` analyzer. The check never downloads or silently substitutes a toolchain. Cargo workspace scans refuse to execute `build.rs` scripts or proc macros by default. Scan only trusted workspaces with the explicit opt-in: ```sh fold scan --mode rust path/to/trusted-crate --rust-allow-build-code ``` `fold fuzz prep` also accepts Rust scans and writes the normal `findings.predicted.json` file plus the shared confirmation sidecar `confirmation.tasks.json`. Current Rust detectors are HIR heuristics and do not yet emit Z3 path witnesses, so Rust fuzz-prep runs may produce an empty prediction set until Rust-specific witness extraction / cargo-fuzz harness emission lands. ### swift frontend Swift source inputs route through `fold-swift` when `--mode swift` is selected or when auto mode sees `.swift` files. `fold-swift` invokes `swiftc -emit-sil` on each input and emits NDJSON with function metadata, findings (heuristic), and interface records. Use binary mode for compiled Swift artifacts; source and binary provenance stay separate. ```sh fold scan --mode swift path/to/file.swift --detectors rust-unsafe-api,null-deref --format sarif ``` Current Swift detectors are SIL heuristics: | Detector | Hazard | |----------|--------| | `rust-unsafe-api` | `unchecked_ref_cast`, `unchecked_addr_cast`, `unchecked_trivial_bit_cast` bypass Swift type safety | | `null-deref` | `load` through raw `UnsafePointer` without provenance check | Swift findings are heuristic today; they preserve backend provenance and stable rule IDs, but do not yet perform interprocedural taint proof or SIL-level path feasibility. ### hybrid router Hybrid mode runs the source and binary backends in one in-memory router pass and emits a single report. The initial implementation is merge-only: source findings and binary findings are produced independently, tagged with backend provenance, and merged without writing intermediate SARIF files. ```sh fold scan --mode hybrid \ --compile-db build/compile_commands.json \ --binary build/app \ --detectors security \ --format sarif \ --output fold-hybrid.sarif ``` `--mode auto` selects hybrid when both source inputs and `--binary` are present. `--input-type binary --binary ` remains supported for existing scripts. ### suppressions and baselines Suppression files are plain text, one pattern per line. Blank lines and `#` comments are ignored. Patterns match either detector name, exact `file:line`, or finding-id prefix: ```text # suppress all integer-overflow findings int-overflow # suppress one location src/foo.cpp:42 # suppress an ID family dead: ``` Baselines are JSON files with current finding decisions: ```json { "version": 1, "tool": "fold", "findings": [ {"id": "null-deref:src/foo.cpp:42:10", "status": "accepted"}, {"id": "int-overflow:src/bar.cpp:12:7", "status": "dismissed"} ] } ``` Current scans mark unmatched findings as `new`; `dismissed` baseline entries are reported as suppressed. SARIF encodes suppressed findings with `level: "none"` and an external suppression justification. ### confirmation and fuzz witness output Fold has two confirmation surfaces: * `fold confirm`: deeper bounded symbolic rerun for selected source findings. * `fold fuzz prep` / `fold fuzz run`: libFuzzer + ASan confirmation that promotes matched crashes to `confidence: "concrete-witness"`. `fold fuzz prep` preserves legacy `findings.predicted.json` and also emits `confirmation.tasks.json`. `fold fuzz run` preserves the augmented findings JSON and also writes `confirmation.results.json` in the prep directory. An optional KLEE smoke adapter exists for development validation: ```sh tests/confirmation/klee/run_klee_smoke.sh ``` It skips cleanly when KLEE is not installed. ### confirm options | Flag | Description | |------|-------------| | `--finding ID` | Confirm a specific finding | | `--rule DETECTOR` | Confirm all findings of a detector | | `--at FILE:LINE` | Confirm findings at a location | | `--unwind N` | Loop unrolling depth (default: 10) | | `--budget N` | Per-function solver budget (ms) | | `--witness DIR` | Write SV-COMP GraphML witnesses | | `--output PATH` | Write confirmation report | ## Detectors | Detector | CWE | Method | |----------|-----|--------| | `clone` | — | Structural hash + Levenshtein (Type 2/3) | | `equiv` | — | Z3-proved semantic equivalence (Type 4) | | `class-dup` | — | Cross-class member equivalence | | `complexity` | — | McCabe-style complexity; available for C++ and Rust HIR functions | | `dead` | 561 | Path predicate UNSAT for branch | | `null-deref` | 476 | SAT-check pointer==0 under path | | `div-by-zero` | 369 | SAT-check divisor==0 under path | | `int-overflow` | 190 | BV overflow predicates at source width | | `oob` | 121 | Array subscript bounds + stdlib buffer size | | `int-truncation` | 197 | Implicit integer truncation | | `sign-conversion` | 195 | Value-changing signed/unsigned casts | | `taint-track` | — | Untrusted-input-to-sink flow | | `rust-unsafe-api` | — | HIR scan for unsafe Rust APIs such as `transmute`, `from_raw_parts`, and `set_len` | | `rust-unchecked-index` | — | HIR scan for `get_unchecked` / `get_unchecked_mut` bounds bypasses | | `rust-path-traversal` | 22 | Heuristic Rust filesystem path sink scan | | `rust-command-injection` | 78 | Heuristic Rust `std::process::Command` argument sink scan | | `rust-crypto-misuse` | 327 | Heuristic weak-hash / crypto misuse scan | Rust detector findings are heuristic today: they preserve backend provenance and stable rule IDs, but they do not yet perform interprocedural taint proof or MIR-level path feasibility. ## Project Evaluation Use the manifest-driven runner to profile Fold and evaluate finding drift on a real C++ source tree, Rust workspace, or compiled binary. It writes SARIF, profile JSON, immutable run artifacts, and a `summary.json`/`summary.md` with stability, resource, disposition, gold, and baseline-comparison results. ```sh python3 scripts/benchmarks/run_project_eval.py \ --manifest tests/benchmarks/projects/yams_source.json \ --out-dir /tmp/fold-yams-eval python3 scripts/benchmarks/run_project_eval.py \ --manifest tests/benchmarks/projects/sage_rust.json \ --out-dir /tmp/fold-sage-eval ``` Manifest schema version 1 is validated by `fold_eval.project`. A manifest pins the lane, corpus/build input, detector scope, budgets, repetitions, and optional gold/disposition/performance-baseline inputs. Relative paths resolve from the manifest directory. Rust manifests automatically use the frontend's pinned toolchain unless `analysis.rust_toolchain` overrides it. Set `evaluation.min_functions` for a production Rust or binary target to fail closed when frontend/toolchain failure produces an empty scan. For binary projects, set `lane` to `binary` and provide one or more `corpus.binaries` paths. Binary observations remain heuristic, but retain artifact/function identity for review even when no source line is available. `fold scan --profile --profile-output profile.json` emits comparable top-level timings across lanes. C++ source profiles provide source-phase and detector/Z3 metrics; Rust/Swift profiles add `frontend_extraction_ms`; binary profiles add `binary-heuristic`, `binary-ir`, and `binary-pcode` rows, including P-Code Z3 query results. ## Output SARIF 2.1.0 with `properties.confidence`: `concrete-witness` (runtime ASan/libFuzzer confirmation), `proved` (Z3 proof under Fold's bounded model), `heuristic` (over-approx in path), `syntactic` (structural only). ## MCP ``` fold mcp ``` JSON-RPC 2.0 over stdio. Protocol version `2024-11-05`. Advertised tools: `fold.scan` (router-backed source/binary/hybrid/rust scan), `fold.scan_start` / `fold.scan_status` / `fold.scan_jobs` for queued scans, `fold.scan_job_findings` / `fold.scan_activate` for completed jobs, `fold.get_scan_context`, `fold.get_findings`, `fold.query_findings`, `fold.suggest_merge`, and `fold.explain`. `fold.query_findings` applies the same fixed-string fields, typed filters, and pagination engine as `fold grep` to either the current context or a completed `job_id`. Session-scoped finding/context cache. Use `fold.scan` with `background: true`, or `fold.scan_start`, for long-running scans. Deprecated scan and detector wrapper names remain callable but are hidden from `tools/list`. Rust inputs are passed as `paths` and route to the Rust frontend when `mode` is `rust` or when `mode` is omitted and the paths are `.rs` files or Cargo crate directories. ## LSP ``` fold lsp ``` Content-Length-framed JSON-RPC. Diagnostics through `textDocument/didOpen` and `textDocument/didChange`. Code actions for fold findings.