1use serde::Serialize;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
12#[serde(rename_all = "lowercase")]
13pub enum Category {
14 Email,
15 Web,
16 Tls,
17 Dns,
18 Takeover,
19 Registration,
20}
21
22impl Category {
23 pub const ALL: [Category; 6] = [
25 Category::Email,
26 Category::Web,
27 Category::Tls,
28 Category::Dns,
29 Category::Takeover,
30 Category::Registration,
31 ];
32
33 pub fn weight(self) -> u32 {
35 match self {
36 Category::Email | Category::Web | Category::Tls => 20,
37 Category::Dns | Category::Takeover => 15,
38 Category::Registration => 10,
39 }
40 }
41
42 pub fn as_str(self) -> &'static str {
44 match self {
45 Category::Email => "email",
46 Category::Web => "web",
47 Category::Tls => "tls",
48 Category::Dns => "dns",
49 Category::Takeover => "takeover",
50 Category::Registration => "registration",
51 }
52 }
53
54 pub fn label(self) -> &'static str {
56 match self {
57 Category::Email => "Email",
58 Category::Web => "Web",
59 Category::Tls => "TLS",
60 Category::Dns => "DNS",
61 Category::Takeover => "Takeover",
62 Category::Registration => "Registration",
63 }
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
70#[serde(rename_all = "lowercase")]
71pub enum Severity {
72 Info,
73 Low,
74 Medium,
75 High,
76 Critical,
77}
78
79impl Severity {
80 pub fn deduction(self) -> u32 {
82 match self {
83 Severity::Info => 0,
84 Severity::Low => 3,
85 Severity::Medium => 10,
86 Severity::High => 25,
87 Severity::Critical => 60,
88 }
89 }
90
91 pub fn as_str(self) -> &'static str {
93 match self {
94 Severity::Info => "info",
95 Severity::Low => "low",
96 Severity::Medium => "medium",
97 Severity::High => "high",
98 Severity::Critical => "critical",
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
105pub struct Finding {
106 pub id: &'static str,
108 pub category: Category,
109 pub severity: Severity,
110 pub host: String,
112 pub title: String,
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub evidence: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub remediation: Option<String>,
120}
121
122impl Finding {
123 pub(crate) fn new(
124 id: &'static str,
125 category: Category,
126 severity: Severity,
127 host: &str,
128 title: impl Into<String>,
129 ) -> Self {
130 Self {
131 id,
132 category,
133 severity,
134 host: host.to_string(),
135 title: title.into(),
136 evidence: None,
137 remediation: None,
138 }
139 }
140
141 pub(crate) fn evidence(mut self, evidence: impl Into<String>) -> Self {
142 self.evidence = Some(evidence.into());
143 self
144 }
145
146 pub(crate) fn fix(mut self, remediation: impl Into<String>) -> Self {
147 self.remediation = Some(remediation.into());
148 self
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
154#[serde(tag = "status", rename_all = "kebab-case")]
155pub enum CheckStatus {
156 Ran,
158 Incomplete { reason: String },
160 NotApplicable { reason: String },
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
166pub struct CheckRun {
167 pub check: &'static str,
168 pub category: Category,
169 #[serde(flatten)]
170 pub status: CheckStatus,
171}