Skip to main content

parlov_core/
technique.rs

1//! Probe generation metadata carried end-to-end through the pipeline.
2
3use serde::{Deserialize, Serialize};
4
5use crate::OracleClass;
6
7/// Why these probes were generated and what normative basis justifies the expected differential.
8///
9/// Set by a strategy at probe generation time, carried through execution unchanged, and consumed
10/// by the analyzer for confidence calibration and evidence labeling.
11///
12/// Signal extraction is unconditional — the analyzer runs all extractors on every
13/// `DifferentialSet`. Technique metadata is for attribution and confidence calibration, not for
14/// gating which signals are extracted.
15#[derive(Debug, Clone)]
16pub struct Technique {
17    /// Machine-readable identifier, e.g. `"if-none-match"` or `"get-200-404"`.
18    pub id: &'static str,
19    /// Human-readable name, e.g. `"If-None-Match conditional request"`.
20    pub name: &'static str,
21    /// Which oracle class this technique targets.
22    pub oracle_class: OracleClass,
23    /// Detection vector: how the differential is produced.
24    pub vector: Vector,
25    /// How strongly the RFC mandates the expected server behavior.
26    pub strength: NormativeStrength,
27}
28
29/// Detection method being used to produce the differential.
30///
31/// Strategies declare their vector. The analyzer uses it to select which signal extractors to run.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub enum Vector {
34    /// Differential produced by comparing status codes across baseline/probe inputs.
35    StatusCodeDiff,
36    /// Differential produced by manipulating cache-related headers (e.g. `If-None-Match`).
37    CacheProbing,
38    /// Differential produced by comparing error message body content across baseline/probe inputs.
39    ErrorMessageGranularity,
40    /// Differential produced by comparing redirect behavior (3xx vs non-3xx) and Location headers.
41    RedirectDiff,
42}
43
44#[cfg(test)]
45mod tests {
46    use super::Vector;
47
48    #[test]
49    fn error_message_granularity_variant_exists() {
50        let v = Vector::ErrorMessageGranularity;
51        assert_ne!(v, Vector::StatusCodeDiff);
52        assert_ne!(v, Vector::CacheProbing);
53    }
54
55    #[test]
56    fn error_message_granularity_serializes() {
57        let original = Vector::ErrorMessageGranularity;
58        let json = serde_json::to_string(&original).expect("serialization must succeed");
59        let roundtrip: Vector =
60            serde_json::from_str(&json).expect("deserialization must succeed");
61        assert_eq!(original, roundtrip);
62    }
63
64    #[test]
65    fn error_message_granularity_is_copy() {
66        let a = Vector::ErrorMessageGranularity;
67        let b = a;
68        assert_eq!(a, b);
69    }
70
71    #[test]
72    fn redirect_diff_variant_exists() {
73        let v = Vector::RedirectDiff;
74        assert_ne!(v, Vector::StatusCodeDiff);
75        assert_ne!(v, Vector::CacheProbing);
76        assert_ne!(v, Vector::ErrorMessageGranularity);
77    }
78
79    #[test]
80    fn redirect_diff_serializes() {
81        let original = Vector::RedirectDiff;
82        let json = serde_json::to_string(&original).expect("serialization must succeed");
83        let roundtrip: Vector =
84            serde_json::from_str(&json).expect("deserialization must succeed");
85        assert_eq!(original, roundtrip);
86    }
87
88    #[test]
89    fn redirect_diff_is_copy() {
90        let a = Vector::RedirectDiff;
91        let b = a;
92        assert_eq!(a, b);
93    }
94}
95
96/// How strongly the RFC mandates the expected server behavior.
97///
98/// Directly affects confidence calibration: a `Must`-level differential is stronger evidence than
99/// a `May`-level one.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
101pub enum NormativeStrength {
102    /// RFC MUST: server is required to behave this way.
103    Must,
104    /// RFC MUST NOT: violation of this is definitive evidence.
105    MustNot,
106    /// RFC SHOULD: server is expected but not required to comply.
107    Should,
108    /// RFC MAY: server is permitted but not expected to exhibit this behavior.
109    May,
110}