# parlov
HTTP oracle detection tool — systematic probing for RFC-compliant information leakage.
HTTP servers that faithfully implement RFC 9110 often leak protected internal state through deterministic differences in status codes, cache-conditional responses, and response metadata. parlov detects those differential signals, scores confidence with per-signal normative weighting, and reports whether an application is vulnerable to oracle-based enumeration.
```bash
cargo install parlov
```
## usage
### manual probe (`existence`) — deprecated
> Deprecated since 0.8.0. Use `parlov scan --strategy <id>` for single-strategy runs; the `existence` subcommand is retained for backward compatibility.
```bash
# GET existence check
parlov existence \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--method GET
# POST registration enumeration
parlov existence \
--target "https://api.example.com/register" \
--baseline-id "alice@corp.com" \
--method POST \
--body '{"email": "{id}", "password": "test123"}'
# HEAD — lightweight, no response body
parlov existence \
--target "https://api.example.com/users/{id}/avatar" \
--baseline-id "1001" \
--method HEAD
# with auth header
parlov existence \
--target "https://api.example.com/projects/{id}" \
--baseline-id "proj-abc" \
--method GET \
--header "Authorization: Bearer eyJhbG..."
```
### automated scan (`scan`)
```bash
# default: all vectors, safe risk
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001"
# specific vector with risk ceiling
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--vector cache-probing:safe
# mixed risk per vector
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--vector status-code-diff:method-destructive \
--vector cache-probing:safe \
--vector error-message-granularity:safe
# specific vector with risk ceiling
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--vector redirect-diff:safe
# specific strategy
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--strategy cp-if-none-match
# SARIF output for CI integration
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--format sarif
# reproducible curl commands per finding (Authorization not redacted)
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--header "Authorization: Bearer eyJhbG..." \
--repro
# include filtered headers + body samples in each finding
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--verbose
# run every strategy regardless of interim confidence
parlov scan \
--target "https://api.example.com/users/{id}" \
--baseline-id "1001" \
--exhaustive
```
39 strategies across four detection vectors (16 status-code-diff + 8 cache-probing + 7 error-message-granularity + 8 redirect-diff). Each scan also walks a Producer/Consumer DAG to emit Phase-2 chained probes informed by Phase-1 observations (`Location` redirects, `ETag` validators, harvested resource IDs, `Problem-Details` envelopes, `Content-Type` negotiation, auth challenges). Each finding includes a confidence score, impact class, severity, and deterministic finding ID for cross-run deduplication; the run as a whole carries an `EndpointVerdict` envelope with posterior probability, stop reason, and observability status.
## options
### global
| Flag | Description |
|------|-------------|
| `--format` | Output format: `table` (default), `json`, `sarif` |
### `existence` subcommand
| Flag | Description |
|------|-------------|
| `--target` | URL template with `{id}` placeholder |
| `--baseline-id` | Resource ID known to exist |
| `--probe-id` | Resource ID to test (defaults to random UUIDv4) |
| `--method` | HTTP method (defaults to GET) |
| `--header` | Request header in `Name: Value` format (repeatable) |
| `--body` | Body template with `{id}` placeholder |
### `scan` subcommand
| Flag | Description |
|------|-------------|
| `--target` | URL template with `{id}` placeholder |
| `--baseline-id` | Resource ID known to exist |
| `--probe-id` | Resource ID to test (defaults to random UUIDv4) |
| `--body` | Body template with `{id}` placeholder (required when the API identifies resources via body) |
| `--header` | Request header in `Name: Value` format (repeatable) |
| `--vector` | Detection vector with optional risk ceiling (repeatable) |
| `--risk` | Global risk ceiling for all vectors (mutually exclusive with `--vector`) |
| `--strategy` | Run specific strategy by ID (repeatable, mutually exclusive with `--risk` and `--vector`) |
| `--alt-credential` | Alternative credential header for scope manipulation |
| `--known-duplicate` | Known duplicate value for uniqueness strategies (`field=value`) |
| `--state-field` | State field name for state transition strategies (`field=value`) |
| `--exhaustive` | Run all strategies regardless of interim confidence; records the first-confirming strategy |
| `--repro` | Emit reproducible `curl` commands for each non-`NotPresent` finding (headers NOT redacted) |
| `--verbose` | Include filtered request/response headers and 256-byte body samples in each finding (headers NOT redacted) |
## exit codes
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Runtime error |
## license
MIT OR Apache-2.0