parlov 0.7.0

HTTP oracle detection tool — systematic probing for RFC-compliant information leakage.
Documentation
//! Snapshot tests for the PATCH 422/404 existence oracle output — table and JSON.
//!
//! Uses `insta` to lock the output format for the canonical PATCH oracle pattern.
//! Run `cargo insta review` after first run to accept generated snapshots.

#![deny(clippy::all)]

use bytes::Bytes;
use http::{HeaderMap, Method, StatusCode};
use insta::assert_snapshot;
use parlov_analysis::existence::ExistenceAnalyzer;
use parlov_analysis::Analyzer;
use parlov_core::{
    DifferentialSet, NormativeStrength, OracleClass, OracleResult, ProbeDefinition,
    ProbeExchange, ResponseSurface, Technique, Vector,
};
use parlov_output::{render_json, render_table};

fn exchange(status: u16) -> ProbeExchange {
    ProbeExchange {
        request: ProbeDefinition {
            url: "http://test/resource/1".to_string(),
            method: Method::GET,
            headers: HeaderMap::new(),
            body: None,
        },
        response: ResponseSurface {
            status: StatusCode::from_u16(status).expect("valid status code"),
            headers: HeaderMap::new(),
            body: Bytes::new(),
            timing_ns: 0,
        },
    }
}

fn test_technique() -> Technique {
    Technique {
        id: "test-snapshot",
        name: "Test snapshot",
        oracle_class: OracleClass::Existence,
        vector: Vector::StatusCodeDiff,
        strength: NormativeStrength::Should,
    }
}

fn diff_set(baseline: u16, probe: u16) -> DifferentialSet {
    DifferentialSet {
        baseline: vec![exchange(baseline), exchange(baseline), exchange(baseline)],
        probe: vec![exchange(probe), exchange(probe), exchange(probe)],
        technique: test_technique(),
    }
}

fn run_analyze(baseline: u16, probe: u16) -> OracleResult {
    ExistenceAnalyzer.analyze(&diff_set(baseline, probe))
}

// --- table snapshot ---

#[test]
fn snap_patch_422_vs_404_table() {
    assert_snapshot!("patch_table_422_vs_404", render_table(&run_analyze(422, 404)));
}

// --- JSON snapshot ---

const TARGET: &str = "http://test/resource/{id}";
const STRATEGY_ID: &str = "test-snapshot";
const STRATEGY_NAME: &str = "Test snapshot";
const METHOD: &str = "GET";

#[test]
fn snap_patch_422_vs_404_json() {
    assert_snapshot!(
        "patch_json_422_vs_404",
        render_json(TARGET, &run_analyze(422, 404), STRATEGY_ID, STRATEGY_NAME, METHOD)
            .expect("serialization failed")
    );
}