parlov 0.7.0

HTTP oracle detection tool — systematic probing for RFC-compliant information leakage.
Documentation
//! Cross-method test server for application-generated vs server-generated 404.
//!
//! The oracle: app-routed paths return structured JSON 404s with `X-Request-Id`,
//! while unrouted paths return axum's plain-text default 404. Both are 404 status
//! codes, but the metadata surface differs — confirming the app handled the route.

#![deny(clippy::all)]

use axum::{
    Router,
    extract::Path,
    http::{HeaderValue, StatusCode, header},
    response::{IntoResponse, Response},
    routing::get,
};
use std::net::SocketAddr;
use tokio::net::TcpListener;

// --- handlers ---

/// Always returns a structured JSON 404. The `X-Request-Id` and
/// `Content-Type: application/json` distinguish this from axum's default 404.
async fn users(Path(_id): Path<String>) -> Response {
    let body = serde_json::json!({"error": "user not found"});
    let mut resp = (StatusCode::NOT_FOUND, axum::Json(body)).into_response();
    resp.headers_mut().insert(
        header::CONTENT_TYPE,
        HeaderValue::from_static("application/json"),
    );
    resp.headers_mut().insert(
        "x-request-id",
        HeaderValue::from_static("req_test123"),
    );
    resp
}

fn router() -> Router {
    // Only /api/users/{id} is routed. /api/zzz/{id} falls through to axum's
    // built-in 404 handler, which returns plain text.
    Router::new().route("/api/users/{id}", get(users))
}

/// Spawns the cross-method test server on a random port.
/// Returns the bound socket address.
pub async fn spawn() -> SocketAddr {
    let listener = TcpListener::bind("127.0.0.1:0")
        .await
        .expect("failed to bind test server listener");
    let addr = listener
        .local_addr()
        .expect("failed to read local_addr from listener");
    tokio::spawn(async move {
        axum::serve(listener, router())
            .await
            .expect("test server failed");
    });
    addr
}