parlov-core 0.6.0

Shared types, error types, and oracle class definitions for parlov.
Documentation
# 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