parlov_analysis/existence/
analyzer.rs1use parlov_core::{DifferentialSet, OracleClass, OracleResult, OracleVerdict, ProbeExchange};
4
5use crate::signals;
6use crate::{Analyzer, SampleDecision};
7
8use super::classifier::classify;
9
10pub struct ExistenceAnalyzer;
16
17impl Analyzer for ExistenceAnalyzer {
18 fn evaluate(&self, data: &DifferentialSet) -> SampleDecision {
19 let b0 = data.baseline[0].response.status;
20 let p0 = data.probe[0].response.status;
21
22 if b0 == p0 {
23 return SampleDecision::Complete(Box::new(build_result(data)));
24 }
25
26 if data.baseline.len() < 3 {
27 return SampleDecision::NeedMore;
28 }
29
30 let stable = is_consistent(&data.baseline) && is_consistent(&data.probe);
31 if stable {
32 SampleDecision::Complete(Box::new(build_result(data)))
33 } else {
34 SampleDecision::Complete(Box::new(unstable_result(data)))
35 }
36 }
37
38 fn oracle_class(&self) -> OracleClass {
39 OracleClass::Existence
40 }
41}
42
43fn build_result(data: &DifferentialSet) -> OracleResult {
45 let b0 = data.baseline[0].response.status;
46 let p0 = data.probe[0].response.status;
47
48 let signals = extract_all_signals(data);
49 classify(b0, p0, signals, &data.technique)
50}
51
52fn extract_all_signals(data: &DifferentialSet) -> Vec<parlov_core::Signal> {
54 let mut out = Vec::new();
55 out.extend(signals::status_code::extract(data));
56 out.extend(signals::header::extract(data));
57 out.extend(signals::metadata::extract(data));
58 out.extend(signals::body::extract(data));
59 out
60}
61
62fn is_consistent(exchanges: &[ProbeExchange]) -> bool {
64 exchanges
65 .iter()
66 .all(|e| e.response.status == exchanges[0].response.status)
67}
68
69fn unstable_result(data: &DifferentialSet) -> OracleResult {
70 let baseline_stable = is_consistent(&data.baseline);
71 let probe_stable = is_consistent(&data.probe);
72
73 let which = match (baseline_stable, probe_stable) {
74 (false, false) => "baseline and probe sides",
75 (false, true) => "baseline side",
76 (true, false) => "probe side",
77 (true, true) => unreachable!("unstable_result called when both sides are stable"),
78 };
79
80 OracleResult {
81 class: OracleClass::Existence,
82 verdict: OracleVerdict::NotPresent,
83 severity: None,
84 confidence: 0,
85 impact_class: None,
86 reasons: vec![],
87 signals: vec![parlov_core::Signal {
88 kind: parlov_core::SignalKind::StatusCodeDiff,
89 evidence: format!("unstable: {which}"),
90 rfc_basis: None,
91 }],
92 technique_id: Some(data.technique.id.to_string()),
93 vector: Some(data.technique.vector),
94 normative_strength: Some(data.technique.strength),
95 label: None,
96 leaks: None,
97 rfc_basis: None,
98 }
99}
100
101#[cfg(test)]
102#[path = "analyzer_tests.rs"]
103mod tests;