use parlov_core::{DifferentialSet, OracleClass, OracleResult, OracleVerdict, ProbeExchange};
use crate::signals;
use crate::{Analyzer, SampleDecision};
use super::classifier::classify;
pub struct ExistenceAnalyzer;
impl Analyzer for ExistenceAnalyzer {
fn evaluate(&self, data: &DifferentialSet) -> SampleDecision {
let b0 = data.baseline[0].response.status;
let p0 = data.probe[0].response.status;
if b0 == p0 {
return SampleDecision::Complete(Box::new(build_result(data)));
}
if data.baseline.len() < 3 {
return SampleDecision::NeedMore;
}
let stable = is_consistent(&data.baseline) && is_consistent(&data.probe);
if stable {
SampleDecision::Complete(Box::new(build_result(data)))
} else {
SampleDecision::Complete(Box::new(unstable_result(data)))
}
}
fn oracle_class(&self) -> OracleClass {
OracleClass::Existence
}
}
fn build_result(data: &DifferentialSet) -> OracleResult {
let b0 = data.baseline[0].response.status;
let p0 = data.probe[0].response.status;
let signals = extract_all_signals(data);
classify(b0, p0, signals, &data.technique)
}
fn extract_all_signals(data: &DifferentialSet) -> Vec<parlov_core::Signal> {
let mut out = Vec::new();
out.extend(signals::status_code::extract(data));
out.extend(signals::header::extract(data));
out.extend(signals::metadata::extract(data));
out
}
fn is_consistent(exchanges: &[ProbeExchange]) -> bool {
exchanges
.iter()
.all(|e| e.response.status == exchanges[0].response.status)
}
fn unstable_result(data: &DifferentialSet) -> OracleResult {
let baseline_stable = is_consistent(&data.baseline);
let probe_stable = is_consistent(&data.probe);
let which = match (baseline_stable, probe_stable) {
(false, false) => "baseline and probe sides",
(false, true) => "baseline side",
(true, false) => "probe side",
(true, true) => unreachable!("unstable_result called when both sides are stable"),
};
OracleResult {
class: OracleClass::Existence,
verdict: OracleVerdict::NotPresent,
severity: None,
confidence: 0,
impact_class: None,
reasons: vec![],
signals: vec![parlov_core::Signal {
kind: parlov_core::SignalKind::StatusCodeDiff,
evidence: format!("unstable: {which}"),
rfc_basis: None,
}],
technique_id: Some(data.technique.id.to_string()),
vector: Some(data.technique.vector),
normative_strength: Some(data.technique.strength),
label: None,
leaks: None,
rfc_basis: None,
}
}
#[cfg(test)]
#[path = "analyzer_tests.rs"]
mod tests;