# parlov-core
Shared types for HTTP oracle detection. Zero I/O, zero async — just data structures.
## types
```rust
use parlov_core::{
ResponseSurface, ProbeDefinition, ProbeExchange, DifferentialSet,
OracleResult, Technique, Vector, NormativeStrength, Signal, SignalKind,
};
```
**`ResponseSurface`** — one captured HTTP interaction:
```rust
pub struct ResponseSurface {
pub status: StatusCode,
pub headers: HeaderMap,
pub body: Bytes,
pub timing_ns: u64,
}
```
**`ProbeDefinition`** — one HTTP request to execute:
```rust
pub struct ProbeDefinition {
pub url: String,
pub method: Method,
pub headers: HeaderMap, // auth context lives here
pub body: Option<Bytes>,
}
```
**`ProbeExchange`** — paired request and response:
```rust
pub struct ProbeExchange {
pub request: ProbeDefinition,
pub response: ResponseSurface,
}
```
**`DifferentialSet`** — paired exchanges with technique context (replaces `ProbeSet`):
```rust
pub struct DifferentialSet {
pub baseline: Vec<ProbeExchange>,
pub probe: Vec<ProbeExchange>,
pub technique: Technique,
}
```
**`Technique`** — strategy metadata:
```rust
pub struct Technique {
pub id: String,
pub name: String,
pub vector: Vector, // StatusCodeDiff, CacheProbing, ErrorMessageGranularity, RedirectDiff
pub normative_strength: NormativeStrength, // Must, MustNot, Should, May
}
```
**`Signal`** and **`SignalKind`** — typed observations from differential analysis:
```rust
pub struct Signal {
pub kind: SignalKind,
pub description: String,
}
```
**`ProbeSet`** *(deprecated — use `DifferentialSet`)* — paired surfaces for differential analysis:
```rust
pub struct ProbeSet {
pub baseline: Vec<ResponseSurface>, // known-valid input
pub probe: Vec<ResponseSurface>, // suspect input
}
```
**`OracleResult`** — the analysis output:
```rust
pub struct OracleResult {
pub class: OracleClass, // Existence, Authentication, Timing, ...
pub verdict: OracleVerdict, // Confirmed / Likely / NotPresent
pub severity: Option<Severity>, // High / Medium / Low
pub confidence: u8, // 0-100 weighted score
pub impact_class: Option<ImpactClass>, // Low / Medium / High leak impact
pub reasons: Vec<ScoringReason>, // audit trail for scoring decisions
pub label: Option<String>, // "Authorization-based differential"
pub leaks: Option<String>, // "Resource existence confirmed to low-privilege callers"
pub rfc_basis: Option<String>, // "RFC 9110 §15.5.4"
pub signals: Vec<Signal>, // typed observations from analysis
pub technique_id: Option<String>, // originating strategy id
pub vector: Option<Vector>, // detection method
pub normative_strength: Option<NormativeStrength>, // RFC mandate level
}
```
**`ImpactClass`** — leak severity classification:
```rust
pub enum ImpactClass { Low, Medium, High }
```
**`ScoringReason`** — audit trail entry for a scoring decision:
```rust
pub struct ScoringReason {
pub dimension: ScoringDimension, // Confidence or Severity
pub description: String,
}
```
All types derive `Serialize` and `Deserialize` with custom serde helpers for `StatusCode`, `Method`, and `HeaderMap` (the `http` crate types lack native serde support).
**`finding_id()`** — deterministic SHA-256 finding ID from technique, target, and status pair for cross-run deduplication.
## use it
Build a differential set from captured HTTP exchanges:
```rust
use parlov_core::{
ProbeExchange, ProbeDefinition, ResponseSurface,
DifferentialSet, Technique, Vector, NormativeStrength,
};
use bytes::Bytes;
use http::{HeaderMap, Method, StatusCode};
let exchange = ProbeExchange {
request: ProbeDefinition {
url: "https://api.example.com/users/123".into(),
method: Method::GET,
headers: HeaderMap::new(),
body: None,
},
response: ResponseSurface {
status: StatusCode::FORBIDDEN,
headers: HeaderMap::new(),
body: Bytes::new(),
timing_ns: 12_500_000,
},
};
```
## errors
```rust
use parlov_core::Error;
```
Three variants via `thiserror`: `Http(String)`, `Analysis(String)`, `Serialization(serde_json::Error)`.
## license
MIT OR Apache-2.0