parlov (binary)
CLI binary crate — argument parsing, subcommand dispatch, adaptive probe scheduling, vector filtering, and the demo server.
Version: 0.6.0 | Files: 9 | Lines: 1,300 | Dependencies: bytes, clap, http, parlov-core, parlov-probe, parlov-analysis, parlov-elicit, parlov-output, tokio, tracing, uuid
The CLI binary crate. Composes all library crates into the command-line tool. Owns argument parsing, subcommand dispatch, the async runtime, and the adaptive probe scheduling loop. Also contains an optional demo server for integration testing.
Public API
The binary crate has a minimal library surface. Only the demo module is publicly exposed, and only when the demo feature flag is enabled:
#[cfg(feature = "demo")]
pub mod demo;All other modules are internal to the binary.
Internal Architecture
CLI Argument Parsing (cli.rs)
Uses clap with derive macros. Defines the command structure:
pub struct Cli {
pub format: OutputFormat,
pub command: Command,
}
pub enum Command {
Existence(ExistenceArgs),
Scan(ScanArgs),
}ExistenceArgs — Single-target probing:
| Flag | Type | Description |
|---|---|---|
target | String | URL with {id} placeholder |
baseline_id | String | Known-existing resource ID |
probe_id | String | Known-nonexistent resource ID |
--method | Option<String> | HTTP method override |
--header | Vec<String> | Request headers (Key: Value format) |
--body | Option<String> | Request body |
ScanArgs — Multi-strategy scanning:
Independently declared struct (no structural inheritance from ExistenceArgs). Shares similar base fields but defines them separately. The scan runner iterates all applicable strategies, executes probe plans, and aggregates ScanFinding results.
| Flag | Type | Description |
|---|---|---|
target | String | URL with {id} placeholder |
baseline_id | String | Known-existing resource ID |
probe_id | String | Known-nonexistent resource ID |
--method | Option<String> | HTTP method override |
--header | Vec<String> | Request headers (Key: Value format) |
--body | Option<String> | Request body |
--risk | Option<RiskLevel> | Max risk level (mutually exclusive with --vector) |
--vector | Option<Vector> | Filter to specific vector (mutually exclusive with --risk) |
--known-duplicate-field/value | Option<String> | For uniqueness strategies |
--state-field/value | Option<String> | For state-transition strategies |
--alt-credential | Vec<String> | Alt credential headers for scope manipulation |
OutputFormat enum: Table, Json, Sarif
Subcommand Runners
existence.rs — Direct probe execution (bypasses strategy/elicitation system):
- Parse method, headers, and probe ID directly from args
- Build two
ProbeDefinitions viabuild_probe_def(baseline + probe) - Call
collect_until_verdictdirectly withHttpProbe - Render result via selected output format
scan.rs — Multi-strategy scan pipeline:
- Parse args into
ScanContext - Call
generate_plan(&ctx)fromparlov-elicitregistry - For each
ProbeSpecin the plan:- Execute probes via
dispatch_spec - Collect all
ScanFindingresults regardless of verdict - Log and skip on error
- Execute probes via
- Render all findings via selected output format (SARIF renderer excludes
NotPresentat output time)
scan_context.rs — Builds ScanContext from CLI args. Handles header parsing, alt credential assembly, and optional field construction.
Vector Filtering (vector_filter.rs)
When --vector is specified, filters the strategy list to only strategies whose Technique.vector matches. This is an alternative to --risk filtering — the two are mutually exclusive.
Utility (util.rs)
CLI-level helpers: header parsing from Key: Value strings, error formatting, etc.
Demo Server (demo.rs)
Feature-gated (#[cfg(feature = "demo")]). An Axum-based HTTP server that simulates API endpoints with predictable differential behavior for integration testing. Not shipped in release builds.
Execution Flow
CLI args
│
├── clap::parse()
│
├── Build ScanContext
│
├── Dispatch
│ ├── existence: build_probe_def directly from args
│ └── scan: generate_plan() with risk/vector filter
│
├── For each ProbeSpec (scan) / single pair (existence):
│ ├── HttpProbe.execute(baseline)
│ ├── HttpProbe.execute(probe)
│ ├── Build DifferentialSet
│ ├── ExistenceAnalyzer.evaluate()
│ ├── If NeedMore: loop (up to max samples)
│ └── Collect OracleResult
│
└── Render output
├── table: render_table() / render_scan_table()
├── json: render_json() / render_scan_json()
└── sarif: render_sarif() / render_scan_sarif()Extension Points
Adding a new subcommand: Add a variant to the Command enum in cli.rs, define args struct, implement a runner module, and add dispatch in main.rs.
Adding a new output format: Add a variant to OutputFormat in cli.rs, implement the render function in parlov-output, and wire it into the subcommand runners.
Custom probe clients: The runners construct HttpProbe::new(). The Probe trait uses RPITIT and is not dyn-compatible, so Box<dyn Probe> does not compile. To swap implementations, use a generic parameter bounded by Probe, or wrap behind async-trait.