8.2 KiB
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:
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|binaryas a deprecated alias during the transition. - If
--input-typeand--modeare both present,--modewins and the CLI should warn once. - Existing binary commands must continue to work:
fold scan --input-type binary --binary /usr/bin/curl --detectors security
Hybrid Strategy
Hybrid mode accepts the implemented strategy explicitly:
--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-functionsstill caps binary function extraction for binary-only and hybrid scans unless--hybrid-binary-functionsis set.--processescontrols source workers only. Binary execution stays in-process until the binary backend has a worker-safe protocol.--profileemits 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:
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:
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:
- Exact demangled function name plus source file candidate.
- Binary xref function matching a source function summary.
- Matched binary string literal found in source text.
- Symbol/import name matching a direct source call.
- 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
- Add
ScanMode,HybridStrategy, CLI parsing, and mode-resolution tests. - Factor current source and binary scan branches out of
runScaninto a router. - Add
merge-onlyhybrid mode and preserve existing source/binary results. - Add
R2Backend::buildIndex()for functions, symbols, strings, xrefs, and sections without decompiling every function. - Add binary-first source filtering guarded by
--hybrid-source-candidates. - Add finding correlation and SARIF/JSON evidence fields.
- Add source-first binary function filtering.
- Update
bench/run_curl_ab.pyto use native hybrid mode instead of external post-hoc correlation.
Validation Matrix
Required checks for router changes:
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:
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