fold/docs/research/cli-router.md
2026-07-29 00:58:19 -04:00

212 lines
8.2 KiB
Markdown

# Source/Binary Router CLI
This document defines the scan-router contract for source, binary, and hybrid
analysis. The first shipped router slice supports `source`, `binary`, `auto`, and
merge-only `hybrid` mode; hint-exchange strategies remain design targets.
## Goals
- Keep source and binary analysis as separate backends with explicit provenance.
- Let source and binary inputs guide each other when both are available.
- Preserve current exit-code, reporting, suppression, baseline, and profiling
behavior.
- Avoid binary-to-Z3 lifting in the first router pass. Binary analysis remains a
fast radare2 heuristic backend; source analysis remains the Clang/Z3 backend.
## Mode Selection
`fold scan` should accept a primary mode flag:
```sh
fold scan --mode source --compile-db build/compile_commands.json src/foo.cc
fold scan --mode binary --binary build/app
fold scan --mode hybrid --compile-db build/compile_commands.json --binary build/app
fold scan --mode auto --compile-db build/compile_commands.json --binary build/app
```
Modes:
| Mode | Inputs | Behavior |
|------|--------|----------|
| `source` | `--compile-db` and/or positional source paths | Run the Clang source backend only. |
| `binary` | `--binary` | Run the radare2 binary backend only. |
| `hybrid` | source inputs and `--binary` | Run source and binary backends through the router. |
| `auto` | any valid input set | Pick `hybrid` when both source and binary inputs exist, otherwise pick `source` or `binary`. |
Default is `--mode auto`.
Compatibility:
- Keep `--input-type source|binary` as a deprecated alias during the transition.
- If `--input-type` and `--mode` are both present, `--mode` wins and the CLI
should warn once.
- Existing binary commands must continue to work:
```sh
fold scan --input-type binary --binary /usr/bin/curl --detectors security
```
## Hybrid Strategy
Hybrid mode accepts the implemented strategy explicitly:
```sh
--hybrid-strategy merge-only
```
Strategies:
| Strategy | Behavior |
|----------|----------|
| `merge-only` | Run source and binary independently, then merge and correlate findings. No backend uses the other side as a filter. |
| `source-first` | Run source first. Use source functions, findings, and strings to focus binary extraction/decompilation. |
| `binary-first` | Build a binary index first. Use binary symbols, xrefs, and strings to focus source files and detectors. |
| `bidirectional` | Build lightweight indexes on both sides, exchange hints, then run focused scans. |
| `auto` | Start with `merge-only` for correctness, then enable `binary-first` or `bidirectional` only when the hint set is small enough to reduce work without hiding source coverage. |
The current implementation supports `merge-only`. The other strategies in the
design table are rejected at the CLI and MCP boundaries until implemented; Fold
must not silently fall back from a requested strategy. Add real `binary-first`,
`source-first`, and `bidirectional` execution after evidence correlation is
stable, then expose their names.
## Hybrid Options
Recommended options:
| Flag | Description |
|------|-------------|
| `--source-detectors NAME` | Source-side detectors; repeatable and comma-separable. Defaults to compatible values from `--detectors`. |
| `--binary-detectors NAME` | Binary-side detectors; repeatable and comma-separable. Defaults to `security,binary-null-deref` when `--detectors` is empty. |
| `--hybrid-source-candidates N` | Max source files selected by binary hints before falling back to full source scan. `0` means no cap. |
| `--hybrid-binary-functions N` | Max binary functions selected by source hints. `0` means no cap. |
| `--hybrid-min-correlation-score F` | Minimum score for promoting source/binary matches into corroborated findings. |
Existing options keep their current meaning:
- `--binary-max-functions` still caps binary function extraction for binary-only
and hybrid scans unless `--hybrid-binary-functions` is set.
- `--processes` controls source workers only. Binary execution stays in-process
until the binary backend has a worker-safe protocol.
- `--profile` emits one consolidated profile with source, binary, correlation,
and total timings.
## Detector Routing
`--detectors` remains the common high-level selector. The router maps it to each
backend and drops incompatible detectors with a profile note, not a hard failure.
Recommended mappings:
| Requested detector | Source backend | Binary backend |
|--------------------|----------------|----------------|
| `security` | all source security detectors | binary security detectors |
| `null-deref` | `null-deref` | `binary-null-deref` |
| `binary-null-deref` | none | `binary-null-deref` |
| `taint-track` | `taint-track` | binary string/unsafe-call scan |
| redundancy detectors | source only | none |
If the user passes `--source-detectors` or `--binary-detectors`, those values
override the mapped detector set for that side only.
## Router Data Model
Add router-specific index types instead of forcing binary data into source IR:
```cpp
enum class ScanMode { Auto, Source, Binary, Hybrid };
enum class HybridStrategy { Auto, MergeOnly, SourceFirst, BinaryFirst, Bidirectional };
struct BinaryIndex {
std::vector<BinaryFunction> functions;
std::vector<BinarySymbol> symbols;
std::vector<BinaryString> strings;
};
struct SourceIndex {
std::vector<FunctionSummary> functions;
std::vector<std::string> files;
};
struct RouterHints {
std::vector<std::string> source_files;
std::vector<std::string> function_names;
std::vector<std::string> symbols;
std::vector<std::string> strings;
};
```
Place the router under `include/fold/cli/scan_router.hpp` and
`src/cli/scan_router.cpp`. Keep `AnalysisBackend` narrow unless both current
backends need the same primitive.
## Evidence And Reporting
Every finding should record which backend produced it:
```cpp
std::string backend; // source | binary
bool has_source_binary_corroboration = false;
std::string correlation_kind;
std::optional<std::uint64_t> correlated_binary_vaddr;
```
Correlation should not rewrite the primary location unless the match is strong.
Use SARIF `properties` for lower-confidence matches and add secondary locations
only for high-confidence function/source-line matches.
Correlation keys, in descending strength:
1. Exact demangled function name plus source file candidate.
2. Binary xref function matching a source function summary.
3. Matched binary string literal found in source text.
4. Symbol/import name matching a direct source call.
5. Section or address-only proximity. This is metadata only, never
corroboration by itself.
## Exit Codes
Exit-code semantics do not change:
- `0`: scan completed and no actionable findings remain after suppression.
- `1`: scan completed and at least one actionable finding remains.
- `2`: invalid input, configuration error, or report/profile write failure.
In hybrid mode, a recoverable failure on one backend should not hide successful
results from the other backend. Emit partial results, mark the failed side in the
profile, and return `2` only when the selected mode required that backend.
## Implementation Phases
1. Add `ScanMode`, `HybridStrategy`, CLI parsing, and mode-resolution tests.
2. Factor current source and binary scan branches out of `runScan` into a router.
3. Add `merge-only` hybrid mode and preserve existing source/binary results.
4. Add `R2Backend::buildIndex()` for functions, symbols, strings, xrefs, and
sections without decompiling every function.
5. Add binary-first source filtering guarded by `--hybrid-source-candidates`.
6. Add finding correlation and SARIF/JSON evidence fields.
7. Add source-first binary function filtering.
8. Update `bench/run_curl_ab.py` to use native hybrid mode instead of external
post-hoc correlation.
## Validation Matrix
Required checks for router changes:
```sh
ninja -C build/release
meson test -C build/release fold:scan_e2e fold:binary_backend fold:subprocess_fanout --print-errorlogs
meson test -C build/release --print-errorlogs
git diff --check
```
Run a curl smoke when touching binary or correlation behavior:
```sh
fold scan --mode hybrid \
--compile-db <curl-build>/compile_commands.json \
--binary <curl-build>/src/curl \
--detectors security \
--format sarif \
--output /tmp/fold-curl-hybrid.sarif
```