use bytes::Bytes;
use http::{HeaderMap, StatusCode};
use parlov_core::{OracleResult, ProbeDefinition};
use parlov_elicit::{ChainProvenance, StrategyMetadata};
use parlov_output::wire::{filter_security_headers, truncate_body_sample};
use parlov_output::{
build_curl, BodySamplesBundle, ExchangeContext, HeadersBundle, ProbeContext, ReproInfo,
ScanFinding,
};
pub(crate) struct WireSurfaces<'a> {
pub(crate) baseline_def: &'a ProbeDefinition,
pub(crate) probe_def: &'a ProbeDefinition,
pub(crate) baseline_status: StatusCode,
pub(crate) probe_status: StatusCode,
pub(crate) baseline_resp_headers: &'a HeaderMap,
pub(crate) probe_resp_headers: &'a HeaderMap,
pub(crate) baseline_body: &'a Bytes,
pub(crate) probe_body: &'a Bytes,
}
#[derive(Clone, Copy)]
pub(crate) struct FindingOpts {
pub(crate) repro: bool,
pub(crate) verbose: bool,
}
pub(crate) fn make_repro(
enabled: bool,
baseline: &ProbeDefinition,
probe: &ProbeDefinition,
) -> Option<ReproInfo> {
if !enabled {
return None;
}
Some(ReproInfo {
baseline_curl: build_curl(baseline),
probe_curl: build_curl(probe),
})
}
fn build_probe_context(surfaces: &WireSurfaces<'_>, method: &str, verbose: bool) -> ProbeContext {
let headers = if verbose {
Some(HeadersBundle {
baseline: filter_security_headers(&surfaces.baseline_def.headers),
probe: filter_security_headers(&surfaces.probe_def.headers),
})
} else {
None
};
ProbeContext {
baseline_url: surfaces.baseline_def.url.clone(),
probe_url: surfaces.probe_def.url.clone(),
method: method.to_owned(),
headers,
}
}
fn build_exchange_context(surfaces: &WireSurfaces<'_>, verbose: bool) -> ExchangeContext {
let headers = if verbose {
Some(HeadersBundle {
baseline: filter_security_headers(surfaces.baseline_resp_headers),
probe: filter_security_headers(surfaces.probe_resp_headers),
})
} else {
None
};
let body_samples = if verbose {
Some(BodySamplesBundle {
baseline: truncate_body_sample(surfaces.baseline_body),
probe: truncate_body_sample(surfaces.probe_body),
})
} else {
None
};
ExchangeContext {
baseline_status: surfaces.baseline_status.as_u16(),
probe_status: surfaces.probe_status.as_u16(),
headers,
body_samples,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn make_finding(
target: &str,
meta: &StrategyMetadata,
method: &str,
result: &OracleResult,
repro: Option<ReproInfo>,
surfaces: &WireSurfaces<'_>,
chain_provenance: Option<ChainProvenance>,
opts: FindingOpts,
) -> ScanFinding {
ScanFinding {
target_url: target.to_owned(),
strategy_id: meta.strategy_id.to_owned(),
strategy_name: meta.strategy_name.to_owned(),
method: method.to_owned(),
result: result.clone(),
repro,
probe: build_probe_context(surfaces, method, opts.verbose),
exchange: build_exchange_context(surfaces, opts.verbose),
chain_provenance,
}
}