1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
//! RustSec Vulnerability Categories

use crate::error::Error;
use serde::{de, ser, Deserialize, Serialize};
use std::{fmt, str::FromStr};

/// RustSec Vulnerability Categories
///
/// The RustSec project maintains its own categorization system for
/// vulnerabilities according to our [criteria for acceptable advisories][1].
///
/// This type represents the present list of allowable vulnerability types for
/// which we allow advisories to be filed.
///
/// [1]: https://github.com/RustSec/advisory-db/blob/master/CONTRIBUTING.md#criteria
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum Category {
    /// Execution of arbitrary code allowing an attacker to gain partial or
    /// total control of an impacted computer system.
    CodeExecution,

    /// Cryptography Failure (e.g. confidentiality breakage, integrity
    /// breakage, key leakage)
    CryptoFailure,

    /// Vulnerabilities an attacker can leverage to cause crashes or excess
    /// resource consumption such that software ceases to function normally,
    /// notably panics in code that is advertised as "panic-free" (particularly
    /// in format parsers for untrusted data)
    DenialOfService,

    /// Disclosure of local files (a.k.a. "directory traversal")
    FileDisclosure,

    /// Mishandled escaping allowing an attacker to execute code or perform
    /// otherwise unexpected operations, e.g. shell escaping, SQL injection, XSS.
    FormatInjection,

    /// Memory unsafety vulnerabilities allowing an attacker to write to
    /// unintended locations in memory.
    MemoryCorruption,

    /// Read-only memory safety vulnerabilities which unintentionally expose data.
    MemoryExposure,

    /// Attacks which bypass authentication and/or authorization systems,
    /// allowing the attacker to obtain unintended privileges.
    PrivilegeEscalation,

    /// Other types of categories: left open-ended to add more of them in the future.
    Other(String),
}

impl Category {
    /// Get the short "kebab case" identifier for a category
    pub fn name(&self) -> &str {
        match self {
            Category::CodeExecution => "code-execution",
            Category::CryptoFailure => "crypto-failure",
            Category::DenialOfService => "denial-of-service",
            Category::FileDisclosure => "file-disclosure",
            Category::FormatInjection => "format-injection",
            Category::MemoryCorruption => "memory-corruption",
            Category::MemoryExposure => "memory-exposure",
            Category::PrivilegeEscalation => "privilege-escalation",
            Category::Other(other) => other,
        }
    }
}

impl fmt::Display for Category {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.name())
    }
}

impl FromStr for Category {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Error> {
        Ok(match s {
            "code-execution" => Category::CodeExecution,
            "crypto-failure" => Category::CryptoFailure,
            "denial-of-service" => Category::DenialOfService,
            "file-disclosure" => Category::FileDisclosure,
            "format-injection" => Category::FormatInjection,
            "memory-corruption" => Category::MemoryCorruption,
            "memory-exposure" => Category::MemoryExposure,
            "privilege-escalation" => Category::PrivilegeEscalation,
            other => Category::Other(other.to_owned()),
        })
    }
}

impl<'de> Deserialize<'de> for Category {
    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        use de::Error;
        let string = String::deserialize(deserializer)?;
        string.parse().map_err(D::Error::custom)
    }
}

impl Serialize for Category {
    fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_string().serialize(serializer)
    }
}