dev_security/producer.rs
1//! [`Producer`] adapter for [`AuditRun`].
2
3use dev_report::{CheckResult, Producer, Report, Severity};
4
5use crate::AuditRun;
6
7/// `Producer` adapter that runs an [`AuditRun`] and emits a [`Report`].
8///
9/// Subprocess failures map to a single failing
10/// [`CheckResult`] named `security::audit` with `Severity::Critical`.
11/// No panics.
12///
13/// # Example
14///
15/// ```no_run
16/// use dev_security::{AuditProducer, AuditRun, AuditScope};
17/// use dev_report::Producer;
18///
19/// let producer = AuditProducer::new(
20/// AuditRun::new("my-crate", "0.1.0").scope(AuditScope::All),
21/// );
22/// let report = producer.produce();
23/// println!("{}", report.to_json().unwrap());
24/// ```
25pub struct AuditProducer {
26 run: AuditRun,
27}
28
29impl AuditProducer {
30 /// Wrap an `AuditRun` so it can be composed with other producers.
31 pub fn new(run: AuditRun) -> Self {
32 Self { run }
33 }
34
35 /// Access the wrapped run.
36 pub fn run(&self) -> &AuditRun {
37 &self.run
38 }
39}
40
41impl Producer for AuditProducer {
42 fn produce(&self) -> Report {
43 let subject = self.run.subject().to_string();
44 let version = self.run.subject_version().to_string();
45 match self.run.execute() {
46 Ok(result) => result.into_report(),
47 Err(e) => {
48 let mut report = Report::new(&subject, &version).with_producer("dev-security");
49 let check = CheckResult::fail("security::audit", Severity::Critical)
50 .with_detail(e.to_string())
51 .with_tag("security")
52 .with_tag("subprocess");
53 report.push(check);
54 report.finish();
55 report
56 }
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::AuditScope;
65
66 #[test]
67 fn produce_returns_report_when_tool_missing() {
68 // The default runner image won't have cargo-audit / cargo-deny
69 // installed; the producer should surface that as a failing
70 // CheckResult rather than panicking.
71 let producer =
72 AuditProducer::new(AuditRun::new("self", "0.0.0").scope(AuditScope::Vulnerabilities));
73 let report = producer.produce();
74 assert_eq!(report.subject, "self");
75 assert!(!report.checks.is_empty());
76 }
77}