1mod load;
5mod validate;
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10pub use load::{
11 load_detector_cache, load_detectors, load_detectors_with_gate, save_detector_cache,
12};
13pub use validate::{QualityIssue, validate_detector};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct MetadataSpec {
18 pub name: String,
20 pub json_path: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, Default)]
26pub struct DetectorSpec {
27 pub id: String,
29 pub name: String,
31 pub service: String,
33 pub severity: Severity,
35 pub patterns: Vec<PatternSpec>,
37 #[serde(default)]
39 pub companions: Vec<CompanionSpec>,
40 pub verify: Option<VerifySpec>,
42 #[serde(default)]
44 pub keywords: Vec<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct PatternSpec {
50 pub regex: String,
52 pub description: Option<String>,
54 pub group: Option<usize>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct CompanionSpec {
61 pub name: String,
63 pub regex: String,
65 pub within_lines: usize,
67 #[serde(default)]
69 pub required: bool,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct VerifySpec {
75 #[serde(default)]
77 pub service: String,
78 pub method: Option<HttpMethod>,
80 pub url: Option<String>,
82 pub auth: Option<AuthSpec>,
84 #[serde(default)]
86 pub headers: Vec<HeaderSpec>,
87 pub body: Option<String>,
89 pub success: Option<SuccessSpec>,
91 #[serde(default)]
93 pub metadata: Vec<MetadataSpec>,
94 pub timeout_ms: Option<u64>,
96 #[serde(default)]
98 pub steps: Vec<StepSpec>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct StepSpec {
104 pub name: String,
105 pub method: HttpMethod,
106 pub url: String,
107 pub auth: AuthSpec,
108 #[serde(default)]
109 pub headers: Vec<HeaderSpec>,
110 pub body: Option<String>,
111 pub success: SuccessSpec,
112 #[serde(default)]
113 pub extract: Vec<MetadataSpec>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct HeaderSpec {
119 pub name: String,
120 pub value: String,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(tag = "type", rename_all = "snake_case")]
126pub enum AuthSpec {
127 None,
128 Bearer {
129 field: String,
130 },
131 Basic {
132 username: String,
133 password: String,
134 },
135 Header {
136 name: String,
137 template: String,
138 },
139 Query {
140 param: String,
141 field: String,
142 },
143 #[serde(rename = "aws_v4")]
144 AwsV4 {
145 access_key: String,
146 secret_key: String,
147 region: String,
148 service: String,
149 session_token: Option<String>,
150 },
151 Script {
152 engine: String,
153 code: String,
154 },
155}
156
157impl AuthSpec {
158 pub fn service_name(&self) -> Option<&str> {
159 match self {
160 AuthSpec::AwsV4 { service, .. } => Some(service),
161 _ => None,
162 }
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, Default)]
168pub struct SuccessSpec {
169 #[serde(default)]
170 pub status: Option<u16>,
172 #[serde(default)]
173 pub status_not: Option<u16>,
175 #[serde(default)]
176 pub body_contains: Option<String>,
178 #[serde(default)]
179 pub body_not_contains: Option<String>,
181 #[serde(default)]
182 pub json_path: Option<String>,
184 #[serde(default)]
185 pub equals: Option<String>,
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
191#[serde(rename_all = "lowercase")]
192pub enum Severity {
193 #[default]
194 Info,
195 Low,
196 Medium,
197 High,
198 Critical,
199}
200
201impl Severity {
202 pub fn to_severity(&self) -> Self {
203 *self
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub enum HttpMethod {
210 #[serde(rename = "GET")]
211 Get,
212 #[serde(rename = "POST")]
213 Post,
214 #[serde(rename = "PUT")]
215 Put,
216 #[serde(rename = "DELETE")]
217 Delete,
218 #[serde(rename = "PATCH")]
219 Patch,
220 #[serde(rename = "HEAD")]
221 Head,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct DetectorFile {
227 pub detector: DetectorSpec,
228}
229
230#[derive(Debug, Error)]
232pub enum SpecError {
233 #[error(
234 "failed to read detector file {path}: {source}. Fix: check the detector path exists and that the file is readable TOML"
235 )]
236 ReadFile {
237 path: String,
238 source: std::io::Error,
239 },
240}