provide_telemetry/
classification.rs1use std::sync::{Mutex, OnceLock};
7
8#[derive(Clone, Debug, Default, PartialEq, Eq)]
9pub enum DataClass {
10 #[default]
11 Public,
12 Internal,
13 Pii,
14 Phi,
15 Pci,
16 Secret,
17}
18
19impl DataClass {
20 pub fn as_str(&self) -> &'static str {
21 match self {
22 Self::Public => "PUBLIC",
23 Self::Internal => "INTERNAL",
24 Self::Pii => "PII",
25 Self::Phi => "PHI",
26 Self::Pci => "PCI",
27 Self::Secret => "SECRET", }
29 }
30}
31
32#[derive(Clone, Debug, Default, PartialEq, Eq)]
33pub struct ClassificationRule {
34 pub pattern: String,
35 pub classification: DataClass,
36}
37
38impl ClassificationRule {
39 pub fn new(pattern: impl Into<String>, classification: DataClass) -> Self {
40 Self {
41 pattern: pattern.into(),
42 classification,
43 }
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct ClassificationPolicy {
49 pub public: String,
50 pub internal: String,
51 pub pii: String,
52 pub phi: String,
53 pub pci: String,
54 pub secret: String,
55}
56
57impl ClassificationPolicy {
58 pub fn lookup_action(&self, label: &str) -> &str {
62 match label {
63 "PUBLIC" => &self.public,
64 "INTERNAL" => &self.internal,
65 "PII" => &self.pii,
66 "PHI" => &self.phi,
67 "PCI" => &self.pci,
68 "SECRET" => &self.secret, _ => "pass",
70 }
71 }
72}
73
74impl Default for ClassificationPolicy {
75 fn default() -> Self {
76 Self {
77 public: "pass".to_string(),
78 internal: "pass".to_string(),
79 pii: "redact".to_string(),
80 phi: "drop".to_string(),
81 pci: "hash".to_string(),
82 secret: "drop".to_string(), }
84 }
85}
86
87static POLICY: OnceLock<Mutex<ClassificationPolicy>> = OnceLock::new();
88
89#[cfg_attr(test, mutants::skip)] fn default_policy_mutex() -> Mutex<ClassificationPolicy> {
91 Mutex::new(ClassificationPolicy::default())
92}
93
94fn policy() -> &'static Mutex<ClassificationPolicy> {
95 POLICY.get_or_init(default_policy_mutex)
96}
97
98pub fn set_classification_policy(p: ClassificationPolicy) {
99 *crate::_lock::lock(policy()) = p;
100}
101
102pub fn get_classification_policy() -> ClassificationPolicy {
103 crate::_lock::lock(policy()).clone()
104}
105
106static RULES: OnceLock<Mutex<Vec<ClassificationRule>>> = OnceLock::new();
107
108#[cfg_attr(test, mutants::skip)] fn empty_rules_mutex() -> Mutex<Vec<ClassificationRule>> {
110 Mutex::new(Vec::new())
111}
112
113fn rules() -> &'static Mutex<Vec<ClassificationRule>> {
114 RULES.get_or_init(empty_rules_mutex)
115}
116
117fn match_glob(pattern: &str, key: &str) -> bool {
121 if !pattern.contains(['*', '?']) {
123 return pattern == key;
124 }
125 fn glob_match(p: &[u8], s: &[u8]) -> bool {
127 match (p.first(), s.first()) {
128 (None, None) => true,
130 (None, Some(_)) => false,
132 (Some(b'*'), _) => {
134 let p_rest = &p[1..];
135 for offset in 0..=s.len() {
137 if glob_match(p_rest, &s[offset..]) {
138 return true;
139 }
140 }
141 false
142 }
143 (Some(b'?'), Some(_)) => glob_match(&p[1..], &s[1..]),
145 (Some(b'?'), None) => false,
147 (Some(&pc), Some(&sc)) => pc == sc && glob_match(&p[1..], &s[1..]),
149 (Some(_), None) => false,
151 }
152 }
153 glob_match(pattern.as_bytes(), key.as_bytes())
154}
155
156pub fn register_classification_rule(rule: ClassificationRule) {
157 crate::_lock::lock(rules()).push(rule);
158}
159
160pub fn register_classification_rules(next: Vec<ClassificationRule>) {
161 crate::_lock::lock(rules()).extend(next);
162}
163
164pub fn clear_classification_rules() {
165 crate::_lock::lock(rules()).clear();
166}
167
168pub fn classify_key(key: &str) -> Option<String> {
169 let rules = crate::_lock::lock(rules());
170 for rule in rules.iter() {
171 if match_glob(&rule.pattern, key) {
172 return Some(rule.classification.as_str().to_string());
173 }
174 }
175 None
176}
177
178#[cfg(test)]
179#[path = "classification_tests.rs"]
180mod tests;