puffin/
differential.rs

1use core::fmt;
2
3use serde::Serialize;
4
5use crate::trace::Source;
6
7/// A difference between two executions in differential fuzzing
8#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)]
9pub enum TraceDifference {
10    /// Both PUT returned a different status
11    Status(StatusDiff),
12    /// A difference in the knowledges
13    Knowledges(KnowledgeDiff),
14    /// A difference in the claims
15    Claims(ClaimDiff),
16    /// One PUT raised a security claim violation and not the other
17    SecurityClaim(SecurityClaimDiff),
18}
19
20impl fmt::Display for TraceDifference {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            TraceDifference::Status(diff) => writeln!(f, "Execution status difference\n{}", diff),
24            TraceDifference::Knowledges(diff) => {
25                writeln!(f, "Differences in knowledges: {}", diff)
26            }
27            TraceDifference::Claims(diff) => write!(f, "{diff}"),
28            TraceDifference::SecurityClaim(diff) => write!(f, "{diff}"),
29        }
30    }
31}
32
33#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)]
34pub struct StatusDiff {
35    pub first_executed_steps: usize,
36    pub first_status: String,
37    pub second_executed_steps: usize,
38    pub second_status: String,
39    /// Total number of step in the trace
40    pub total_step: usize,
41}
42
43impl StatusDiff {
44    pub fn as_trace_difference(self) -> TraceDifference {
45        TraceDifference::Status(self)
46    }
47}
48
49impl fmt::Display for StatusDiff {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        writeln!(
52            f,
53            "\tfirst put : (step {}/{}) {}\n\tsecond put : (step {}/{}) {}",
54            self.first_executed_steps,
55            self.total_step,
56            self.first_status,
57            self.second_executed_steps,
58            self.total_step,
59            self.second_status
60        )
61    }
62}
63
64#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)]
65pub enum KnowledgeDiff {
66    /// The two knowledges compared have a different rust type
67    DifferentTypes {
68        index: usize,
69        first_type: String,
70        second_type: String,
71        first_source: Source,
72        second_source: Source,
73    },
74    /// The two knowledges have the same type but their internal values are different
75    InnerDifference {
76        index: usize,
77        type_name: String,
78        /// Comparable crate as String
79        diff: String,
80        source: Source,
81    },
82}
83
84impl fmt::Display for KnowledgeDiff {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        match self {
87            KnowledgeDiff::DifferentTypes {
88                index,
89                first_type,
90                second_type,
91                first_source,
92                second_source,
93            } => writeln!(
94                f,
95                "knowledge[{}]: {} ({}) != {} ({})",
96                index, first_type, first_source, second_type, second_source
97            ),
98            KnowledgeDiff::InnerDifference {
99                index,
100                type_name,
101                diff,
102                source,
103            } => writeln!(
104                f,
105                "knowledge[{}] ({}, {}) : \n{}",
106                index, type_name, source, diff
107            ),
108        }
109    }
110}
111
112#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)]
113pub enum ClaimDiff {
114    DifferentTypes {
115        agent: u8,
116        index: usize,
117        first_type: String,
118        second_type: String,
119    },
120    InnerDifference {
121        agent: u8,
122        index: usize,
123        diff: String,
124    },
125}
126
127impl fmt::Display for ClaimDiff {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        match self {
130            ClaimDiff::DifferentTypes {
131                index,
132                first_type,
133                second_type,
134                agent,
135            } => {
136                writeln!(
137                    f,
138                    "Claim [agent{agent},{}]: {} != {}",
139                    index, first_type, second_type
140                )
141            }
142            ClaimDiff::InnerDifference { index, diff, agent } => {
143                writeln!(f, "Claim [agent{agent},{}]:\n{}", index, diff)
144            }
145        }
146    }
147}
148
149#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)]
150pub enum SecurityClaimDiff {
151    Different {
152        put: u8,
153        claim: String,
154    },
155    BothError {
156        first_put: String,
157        second_put: String,
158    },
159}
160
161impl SecurityClaimDiff {
162    pub fn as_trace_difference(self) -> TraceDifference {
163        TraceDifference::SecurityClaim(self)
164    }
165}
166
167impl fmt::Display for SecurityClaimDiff {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        match self {
170            SecurityClaimDiff::Different { put, claim } => {
171                writeln!(f, "Security Claim violation on PUT {} : \"{}\"", put, claim)
172            }
173            SecurityClaimDiff::BothError {
174                first_put,
175                second_put,
176            } => writeln!(
177                f,
178                "Security Claim violation on both PUT : \"{}\" and \"{}\"",
179                first_put, second_put
180            ),
181        }
182    }
183}