parlov-output 0.8.0

Output formatters for parlov: SARIF, terminal table, and raw JSON.
Documentation
//! Output formatters for parlov: terminal table, structured JSON, and SARIF v2.1.0.

#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![deny(missing_docs)]

mod context;
mod json;
mod json_endpoint;
mod repro;
mod sarif;
mod sarif_builder;
#[cfg(not(target_arch = "wasm32"))]
mod table;
#[cfg(not(target_arch = "wasm32"))]
mod table_rows;
pub mod wire;

#[cfg(test)]
#[path = "repro_output_tests.rs"]
mod repro_output_tests;

#[cfg(test)]
#[path = "context_output_tests.rs"]
mod context_output_tests;

pub use context::{BodySamplesBundle, ExchangeContext, HeadersBundle, ProbeContext};
pub use json::{render_endpoint_verdict_json, render_json, render_scan_json};
pub use parlov_elicit::ChainProvenance;
pub use repro::build_curl;
pub use sarif::{render_endpoint_verdict_sarif, render_sarif, render_scan_sarif};
#[cfg(not(target_arch = "wasm32"))]
pub use table::{render_endpoint_verdict_table, render_scan_table, render_table};

use parlov_core::OracleResult;
use serde::{Deserialize, Serialize};

/// Reproducible `curl` commands for the baseline and probe requests of a finding.
///
/// Populated only when `--repro` is set on the scan CLI; emitted in JSON, SARIF,
/// and table outputs. Headers (including `Authorization`) appear verbatim — the
/// curls are designed to be copy-pasteable for hand verification.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ReproInfo {
    /// Curl command for the baseline request — headers verbatim.
    pub baseline_curl: String,
    /// Curl command for the probe request — headers verbatim.
    pub probe_curl: String,
}

/// A single finding from a scan run -- one strategy applied to one method.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ScanFinding {
    /// e.g. `"https://api.example.com/users/1"`
    pub target_url: String,
    /// e.g. `"existence-get-200-404"`
    pub strategy_id: String,
    /// e.g. `"GET 200/404 existence"`
    pub strategy_name: String,
    /// e.g. `"GET"`
    pub method: String,
    /// Oracle analysis verdict, signals, and scoring breakdown.
    pub result: OracleResult,
    /// Reproducible curl commands — `Some` only when `--repro` was set.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub repro: Option<ReproInfo>,
    /// What was actually sent on the wire — always present.
    pub probe: ProbeContext,
    /// What came back — status codes always present; headers and body samples
    /// only when `--verbose` was set.
    pub exchange: ExchangeContext,
    /// Phase-2 chain provenance — `None` for phase-1 findings, `Some` when the
    /// underlying spec was generated by `generate_dag_chained_plan` from a
    /// harvested phase-1 signal.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub chain_provenance: Option<ChainProvenance>,
}