secfinding 0.3.0

Universal security finding types for vulnerability scanners.
Documentation
// Extracted from src/reportable.rs
use secfinding::*;
use secfinding::reportable::*;

use super::*;
use crate::{Finding, Severity};

#[test]
fn finding_implements_reportable() {
    let f = Finding::new("scanner", "target", Severity::High, "Title", "Detail").unwrap();
    assert_eq!(Reportable::scanner(&f), "scanner");
    assert_eq!(Reportable::target(&f), "target");
    assert_eq!(Reportable::severity(&f), Severity::High);
    assert_eq!(Reportable::title(&f), "Title");
    assert_eq!(Reportable::detail(&f), "Detail");
}

#[test]
fn custom_type_implements_reportable() {
    struct CustomFinding {
        name: String,
    }

    impl Reportable for CustomFinding {
        fn scanner(&self) -> &'static str {
            "custom"
        }
        fn target(&self) -> &'static str {
            "custom-target"
        }
        fn severity(&self) -> Severity {
            Severity::Critical
        }
        fn title(&self) -> &str {
            &self.name
        }
        fn cwe_ids(&self) -> &[String] {
            &[]
        }
        fn cve_ids(&self) -> &[String] {
            &[]
        }
        fn tags(&self) -> &[String] {
            &[]
        }
    }

    let f = CustomFinding { name: "XSS".into() };
    assert_eq!(f.scanner(), "custom");
    assert_eq!(f.severity(), Severity::Critical);
    assert_eq!(f.detail(), ""); // default
    assert!(f.tags().is_empty()); // default
    assert!(f.rule_id().contains("xss"));
}

#[test]
fn reportable_defaults_are_sensible() {
    struct Minimal;
    impl Reportable for Minimal {
        fn scanner(&self) -> &'static str {
            "s"
        }
        fn target(&self) -> &'static str {
            "t"
        }
        fn severity(&self) -> Severity {
            Severity::Info
        }
        fn title(&self) -> &'static str {
            "minimal"
        }
        fn cwe_ids(&self) -> &[String] {
            &[]
        }
        fn cve_ids(&self) -> &[String] {
            &[]
        }
        fn tags(&self) -> &[String] {
            &[]
        }
    }

    let m = Minimal;
    assert_eq!(m.detail(), "");
    assert!(m.cwe_ids().is_empty());
    assert!(m.cve_ids().is_empty());
    assert!(m.tags().is_empty());
    assert_eq!(m.confidence(), None);
    assert_eq!(m.exploit_hint(), None);
    assert_eq!(m.rule_id(), "s/minimal");
}

#[test]
fn reportable_custom_sarif_level() {
    struct CustomSev;
    impl Reportable for CustomSev {
        fn scanner(&self) -> &'static str {
            "s"
        }
        fn target(&self) -> &'static str {
            "t"
        }
        fn severity(&self) -> Severity {
            Severity::Critical
        }
        fn title(&self) -> &'static str {
            "t"
        }
        fn cwe_ids(&self) -> &[String] {
            &[]
        }
        fn cve_ids(&self) -> &[String] {
            &[]
        }
        fn tags(&self) -> &[String] {
            &[]
        }
    }
    let f = CustomSev;
    assert_eq!(f.sarif_level(), "error");
}

#[test]
fn reportable_custom_rule_id() {
    struct CustomRuleId;
    impl Reportable for CustomRuleId {
        fn scanner(&self) -> &'static str {
            "scanner"
        }
        fn target(&self) -> &'static str {
            "target"
        }
        fn severity(&self) -> Severity {
            Severity::Info
        }
        fn title(&self) -> &'static str {
            "MY custom TITLE!"
        }
        fn rule_id(&self) -> String {
            "CUSTOM-RULE-ID".to_string()
        }
        fn cwe_ids(&self) -> &[String] {
            &[]
        }
        fn cve_ids(&self) -> &[String] {
            &[]
        }
        fn tags(&self) -> &[String] {
            &[]
        }
    }
    let f = CustomRuleId;
    assert_eq!(f.rule_id(), "CUSTOM-RULE-ID");
}

#[test]
fn reportable_default_rule_id_formatting() {
    struct Spaces;
    impl Reportable for Spaces {
        fn scanner(&self) -> &'static str {
            "scan"
        }
        fn target(&self) -> &'static str {
            "target"
        }
        fn severity(&self) -> Severity {
            Severity::Info
        }
        fn title(&self) -> &'static str {
            "Some spaces here"
        }
        fn cwe_ids(&self) -> &[String] {
            &[]
        }
        fn cve_ids(&self) -> &[String] {
            &[]
        }
        fn tags(&self) -> &[String] {
            &[]
        }
    }
    let f = Spaces;
    assert_eq!(f.rule_id(), "scan/some-spaces-here");
}