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

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|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:
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-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:

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:

  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:

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