# 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 functions; std::vector symbols; std::vector strings; }; struct SourceIndex { std::vector functions; std::vector files; }; struct RouterHints { std::vector source_files; std::vector function_names; std::vector symbols; std::vector 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 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 /compile_commands.json \ --binary /src/curl \ --detectors security \ --format sarif \ --output /tmp/fold-curl-hybrid.sarif ```