csaf_walker/verification/
mod.rs

1//! Verification
2//!
3//! Checks to ensure conformity with the specification.
4
5use crate::{
6    discover::{AsDiscovered, DiscoveredAdvisory},
7    retrieve::{AsRetrieved, RetrievalContext, RetrievedAdvisory, RetrievedVisitor},
8    source::Source,
9    validation::{ValidatedAdvisory, ValidatedVisitor, ValidationContext, ValidationError},
10    verification::check::{Check, CheckError},
11};
12use csaf::Csaf;
13use serde::de::Error as _;
14use std::{
15    collections::{HashMap, HashSet},
16    fmt::{Debug, Display},
17    future::Future,
18    hash::Hash,
19    marker::PhantomData,
20    ops::{Deref, DerefMut},
21};
22use url::Url;
23use walker_common::retrieve::RetrievalError;
24use walker_common::utils::url::Urlify;
25
26pub mod check;
27
28#[derive(Debug)]
29pub struct VerifiedAdvisory<A, I>
30where
31    A: AsRetrieved,
32    I: Clone + PartialEq + Eq + Hash,
33{
34    pub advisory: A,
35    pub csaf: Csaf,
36    pub failures: HashMap<I, Vec<CheckError>>,
37    pub successes: HashSet<I>,
38}
39
40impl<A, I> Deref for VerifiedAdvisory<A, I>
41where
42    A: AsRetrieved,
43    I: Clone + PartialEq + Eq + Hash,
44{
45    type Target = A;
46
47    fn deref(&self) -> &Self::Target {
48        &self.advisory
49    }
50}
51
52impl<A, I> DerefMut for VerifiedAdvisory<A, I>
53where
54    A: AsRetrieved,
55    I: Clone + PartialEq + Eq + Hash,
56{
57    fn deref_mut(&mut self) -> &mut Self::Target {
58        &mut self.advisory
59    }
60}
61
62#[derive(Debug, thiserror::Error)]
63pub enum VerificationError<UE, A>
64where
65    A: Debug,
66    UE: Display + Debug,
67{
68    #[error(transparent)]
69    Upstream(UE),
70    #[error("document parsing error: {error}")]
71    Parsing {
72        advisory: A,
73        error: serde_json::Error,
74    },
75    #[error("check runtime error: {error}")]
76    Check { advisory: A, error: anyhow::Error },
77}
78
79impl<A, UE> AsDiscovered for VerificationError<UE, A>
80where
81    A: AsDiscovered + Debug,
82    UE: AsDiscovered + Display + Debug,
83{
84    fn as_discovered(&self) -> &DiscoveredAdvisory {
85        match self {
86            Self::Upstream(err) => err.as_discovered(),
87            Self::Parsing { advisory, .. } => advisory.as_discovered(),
88            Self::Check { advisory, .. } => advisory.as_discovered(),
89        }
90    }
91}
92
93impl<UE, A> Urlify for VerificationError<UE, A>
94where
95    A: AsRetrieved + Debug,
96    UE: Urlify + Display + Debug,
97{
98    fn url(&self) -> &Url {
99        match self {
100            Self::Upstream(err) => err.url(),
101            Self::Parsing { advisory, .. } => advisory.as_retrieved().url(),
102            Self::Check { advisory, .. } => advisory.as_retrieved().url(),
103        }
104    }
105}
106
107pub struct VerificationContext {}
108
109/// A visitor accepting a verified advisory
110pub trait VerifiedVisitor<A, E, I>
111where
112    A: AsRetrieved,
113    E: Display + Debug,
114    I: Clone + PartialEq + Eq + Hash,
115{
116    type Error: Display + Debug;
117    type Context;
118
119    fn visit_context(
120        &self,
121        context: &VerificationContext,
122    ) -> impl Future<Output = Result<Self::Context, Self::Error>>;
123
124    fn visit_advisory(
125        &self,
126        context: &Self::Context,
127        result: Result<VerifiedAdvisory<A, I>, VerificationError<E, A>>,
128    ) -> impl Future<Output = Result<(), Self::Error>>;
129}
130
131#[derive(Debug, thiserror::Error)]
132pub enum Error<VE>
133where
134    VE: Display + Debug,
135{
136    #[error(transparent)]
137    Visitor(VE),
138}
139
140/// A visitor implementing the verification of a CSAF document
141pub struct VerifyingVisitor<A, E, V, I>
142where
143    A: AsRetrieved,
144    V: VerifiedVisitor<A, E, I>,
145    E: Display + Debug,
146    I: Clone + PartialEq + Eq + Hash,
147{
148    visitor: V,
149    checks: Vec<(I, Box<dyn Check>)>,
150    _marker: PhantomData<(A, E)>,
151}
152
153impl<A, E, V, I> VerifyingVisitor<A, E, V, I>
154where
155    A: AsRetrieved,
156    V: VerifiedVisitor<A, E, I>,
157    E: Display + Debug,
158    I: Clone + PartialEq + Eq + Hash,
159{
160    pub fn new(visitor: V) -> Self {
161        Self {
162            visitor,
163            checks: vec![],
164            _marker: Default::default(),
165        }
166    }
167
168    pub fn with_checks(visitor: V, checks: Vec<(I, Box<dyn Check>)>) -> Self {
169        Self {
170            visitor,
171            checks,
172            _marker: Default::default(),
173        }
174    }
175
176    pub fn add<F: Check + 'static>(mut self, index: I, check: F) -> Self {
177        self.checks.push((index, Box::new(check)));
178        self
179    }
180
181    async fn verify(&self, advisory: A) -> Result<VerifiedAdvisory<A, I>, VerificationError<E, A>> {
182        let data = advisory.as_retrieved().data.clone();
183
184        let csaf = match tokio::task::spawn_blocking(move || serde_json::from_slice::<Csaf>(&data))
185            .await
186        {
187            Ok(Ok(csaf)) => csaf,
188            Ok(Err(error)) => return Err(VerificationError::Parsing { error, advisory }),
189            Err(_) => {
190                return Err(VerificationError::Parsing {
191                    error: serde_json::error::Error::custom("failed to wait for deserialization"),
192                    advisory,
193                })
194            }
195        };
196
197        let mut failures = HashMap::new();
198        let mut successes = HashSet::new();
199
200        for (index, check) in &self.checks {
201            let result = match check.as_ref().check(&csaf).await {
202                Ok(result) => result,
203                Err(error) => return Err(VerificationError::Check { error, advisory }),
204            };
205            if !result.is_empty() {
206                failures.insert(index.clone(), result);
207            } else {
208                successes.insert(index.clone());
209            }
210        }
211
212        Ok(VerifiedAdvisory {
213            advisory,
214            csaf,
215            failures,
216            successes,
217        })
218    }
219}
220
221impl<V, I, S> RetrievedVisitor<S>
222    for VerifyingVisitor<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>, V, I>
223where
224    V: VerifiedVisitor<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>, I>,
225    I: Clone + PartialEq + Eq + Hash,
226    S: Source,
227{
228    type Error = Error<V::Error>;
229    type Context = V::Context;
230
231    async fn visit_context(
232        &self,
233        _context: &RetrievalContext<'_>,
234    ) -> Result<Self::Context, Self::Error> {
235        self.visitor
236            .visit_context(&VerificationContext {})
237            .await
238            .map_err(Error::Visitor)
239    }
240
241    async fn visit_advisory(
242        &self,
243        context: &Self::Context,
244        result: Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>,
245    ) -> Result<(), Self::Error> {
246        let result = match result {
247            Ok(doc) => self.verify(doc).await,
248            Err(err) => Err(VerificationError::Upstream(err)),
249        };
250
251        self.visitor
252            .visit_advisory(context, result)
253            .await
254            .map_err(Error::Visitor)?;
255
256        Ok(())
257    }
258}
259
260impl<V, I, S> ValidatedVisitor<S> for VerifyingVisitor<ValidatedAdvisory, ValidationError<S>, V, I>
261where
262    V: VerifiedVisitor<ValidatedAdvisory, ValidationError<S>, I>,
263    I: Clone + PartialEq + Eq + Hash,
264    S: Source,
265{
266    type Error = Error<V::Error>;
267    type Context = V::Context;
268
269    async fn visit_context(
270        &self,
271        _context: &ValidationContext<'_>,
272    ) -> Result<Self::Context, Self::Error> {
273        self.visitor
274            .visit_context(&VerificationContext {})
275            .await
276            .map_err(Error::Visitor)
277    }
278
279    async fn visit_advisory(
280        &self,
281        context: &Self::Context,
282        result: Result<ValidatedAdvisory, ValidationError<S>>,
283    ) -> Result<(), Self::Error> {
284        let result = match result {
285            Ok(doc) => self.verify(doc).await,
286            Err(err) => Err(VerificationError::Upstream(err)),
287        };
288
289        self.visitor
290            .visit_advisory(context, result)
291            .await
292            .map_err(Error::Visitor)?;
293
294        Ok(())
295    }
296}
297
298impl<F, E, Fut, A, I, UE> VerifiedVisitor<A, UE, I> for F
299where
300    UE: Debug + Display + 'static,
301    F: Fn(Result<VerifiedAdvisory<A, I>, VerificationError<UE, A>>) -> Fut,
302    Fut: Future<Output = Result<(), E>>,
303    E: Display + Debug + 'static,
304    A: AsRetrieved + 'static,
305    I: Clone + PartialEq + Eq + Hash + 'static,
306{
307    type Error = E;
308    type Context = ();
309
310    async fn visit_context(
311        &self,
312        _context: &VerificationContext,
313    ) -> Result<Self::Context, Self::Error> {
314        Ok(())
315    }
316
317    async fn visit_advisory(
318        &self,
319        _ctx: &Self::Context,
320        outcome: Result<VerifiedAdvisory<A, I>, VerificationError<UE, A>>,
321    ) -> Result<(), Self::Error> {
322        self(outcome).await
323    }
324}