1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! CLI argument definitions for parlov.
//!
//! Structured via `clap` derive macros. Each oracle class maps to a subcommand.
use clap::{Parser, Subcommand};
/// Output format for oracle results.
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum OutputFormat {
/// Human-readable terminal table with ANSI color.
Table,
/// Pretty-printed JSON — raw `OracleResult` or `ScanFinding` array.
Json,
/// SARIF v2.1.0 suitable for GitHub Advanced Security / Code Scanning integration.
Sarif,
}
/// HTTP oracle detection tool.
///
/// Probes web applications and APIs to identify information leakage through RFC-compliant server
/// behavior.
#[derive(Debug, Parser)]
#[command(name = "parlov", version, about, long_about = None)]
pub struct Cli {
/// Output format: table (default), json, or sarif.
#[arg(long = "format", default_value = "table", global = true)]
pub format: OutputFormat,
/// Subcommand selecting which oracle class to probe.
#[command(subcommand)]
pub command: Command,
}
/// Top-level subcommands, one per oracle class.
#[derive(Debug, Subcommand)]
pub enum Command {
/// Deprecated: use `scan --strategy` instead.
Existence(ExistenceArgs),
/// Automated elicitation scan: run all applicable strategies against a target endpoint.
Scan(ScanArgs),
}
/// Arguments for the existence oracle subcommand.
#[derive(Debug, clap::Args)]
pub struct ExistenceArgs {
/// Target URL template. `{id}` is substituted with the resource ID wherever it appears — in the URL, body, or both. The URL may be static if the ID is body-only.
#[arg(long)]
pub target: String,
/// Resource ID to use as the baseline (known-existing resource).
#[arg(long)]
pub baseline_id: String,
/// Resource ID to probe. Defaults to a randomly generated `UUIDv4` (nonexistent by design).
#[arg(long)]
pub probe_id: Option<String>,
/// Additional request headers in `Name: Value` format. Repeatable.
#[arg(long = "header", value_name = "HEADER")]
pub headers: Vec<String>,
/// HTTP method to use.
#[arg(long, default_value = "GET")]
pub method: String,
/// Optional request body template. Use `{id}` as the placeholder.
/// Required for POST when the identifier is in the body.
/// Also used for PATCH and PUT to supply the patch payload.
#[arg(long)]
pub body: Option<String>,
}
/// Arguments for the automated elicitation scan subcommand.
#[derive(Debug, clap::Args)]
pub struct ScanArgs {
/// Target URL template. `{id}` is substituted with the resource ID wherever it appears — in the URL, body, or both. The URL may be static if the ID is body-only.
#[arg(long)]
pub target: String,
/// Known-existing resource ID (baseline).
#[arg(long)]
pub baseline_id: String,
/// Known-nonexistent resource ID (probe). Defaults to a random `UUIDv4`.
#[arg(long)]
pub probe_id: Option<String>,
/// Maximum risk level of strategies to run: safe | method-destructive | operation-destructive
#[arg(long, default_value = "safe")]
pub risk: String,
/// Additional request headers in `Name: Value` format. Repeatable.
#[arg(long = "header", value_name = "HEADER")]
pub headers: Vec<String>,
/// Under-scoped credential header for scope-manipulation strategy, e.g. `Authorization: Bearer ...`
#[arg(long = "alt-credential", value_name = "HEADER")]
pub alt_credential: Option<String>,
/// Known-duplicate field=value for uniqueness strategy, e.g. `email=alice@example.com`
#[arg(long)]
pub known_duplicate: Option<String>,
/// State field=value for state-transition strategy, e.g. `status=invalid`
#[arg(long)]
pub state_field: Option<String>,
/// Filter strategies by vector. Format: `name` or `name:risk-level`.
/// Repeatable. Mutually exclusive with --risk.
/// Examples: `--vector cache-probing`, `--vector status-code-diff:method-destructive`
#[arg(long = "vector", value_name = "VECTOR")]
pub vectors: Vec<String>,
/// Run only the named strategy. Repeatable. Mutually exclusive with --risk and --vector.
/// Example: --strategy rd-percent-encoding
#[arg(long = "strategy", value_name = "STRATEGY_ID")]
pub strategies: Vec<String>,
/// Optional request body template. `{id}` is substituted with the resource ID.
/// Required when the target API identifies resources via request body rather than URL path.
#[arg(long)]
pub body: Option<String>,
/// Run all strategies regardless of interim confidence. Records the strategy
/// that first crossed the confirmation threshold in the output.
#[arg(long, default_value = "false")]
pub exhaustive: bool,
/// Emit reproducible `curl` commands for the baseline and probe of each finding.
/// Headers are NOT redacted — `Authorization` and other secrets appear verbatim
/// so the curl commands actually work for hand verification. Use carefully.
#[arg(long, default_value = "false")]
pub repro: bool,
/// Include filtered request/response headers and body samples in each finding.
/// Headers are NOT redacted — `Authorization`, `Cookie`, and other secrets
/// appear verbatim. Same opt-in stance as `--repro`. Use carefully.
#[arg(long, default_value = "false")]
pub verbose: bool,
}