relay_pii/
compiledconfig.rs

1use std::cmp::Ordering;
2use std::collections::BTreeSet;
3
4use crate::builtin::BUILTIN_RULES_MAP;
5use crate::{PiiConfig, PiiConfigError, Redaction, RuleSpec, RuleType, SelectorSpec};
6
7/// A representation of `PiiConfig` that is more (CPU-)efficient for use in `PiiProcessor`.
8///
9/// It is lossy in the sense that it cannot be consumed by downstream Relays, so both versions have
10/// to be kept around.
11#[derive(Debug, Clone)]
12pub struct CompiledPiiConfig {
13    pub(super) applications: Vec<(SelectorSpec, BTreeSet<RuleRef>)>,
14}
15
16impl CompiledPiiConfig {
17    /// Computes the compiled PII config.
18    pub fn new(config: &PiiConfig) -> Self {
19        let mut applications = Vec::new();
20        for (selector, rules) in &config.applications {
21            #[allow(clippy::mutable_key_type)]
22            let mut rule_set = BTreeSet::default();
23            for rule_id in rules {
24                collect_rules(config, &mut rule_set, rule_id, None);
25            }
26            applications.push((selector.clone(), rule_set));
27        }
28
29        CompiledPiiConfig { applications }
30    }
31
32    /// Force compilation of all regex patterns in this config.
33    ///
34    /// Used to verify that all patterns are valid regex.
35    pub fn force_compile(&self) -> Result<(), PiiConfigError> {
36        for rule in self.applications.iter().flat_map(|(_, rules)| rules.iter()) {
37            match &rule.ty {
38                RuleType::Pattern(rule) => {
39                    rule.pattern.compiled().map_err(|e| e.clone())?;
40                }
41                RuleType::RedactPair(rule) => {
42                    rule.key_pattern.compiled().map_err(|e| e.clone())?;
43                }
44                RuleType::Anything
45                | RuleType::Imei
46                | RuleType::Mac
47                | RuleType::Uuid
48                | RuleType::Email
49                | RuleType::Ip
50                | RuleType::Creditcard
51                | RuleType::Iban
52                | RuleType::Userpath
53                | RuleType::Pemkey
54                | RuleType::UrlAuth
55                | RuleType::UsSsn
56                | RuleType::Bearer
57                | RuleType::Password
58                | RuleType::Multiple(_)
59                | RuleType::Alias(_)
60                | RuleType::Unknown(_) => {}
61            }
62        }
63        Ok(())
64    }
65}
66
67fn get_rule(config: &PiiConfig, id: &str) -> Option<RuleRef> {
68    if let Some(spec) = config.rules.get(id) {
69        Some(RuleRef::new(id.to_owned(), spec))
70    } else {
71        BUILTIN_RULES_MAP
72            .get(id)
73            .map(|spec| RuleRef::new(id.to_owned(), spec))
74    }
75}
76
77#[allow(clippy::mutable_key_type)]
78fn collect_rules(
79    config: &PiiConfig,
80    rules: &mut BTreeSet<RuleRef>,
81    rule_id: &str,
82    parent: Option<RuleRef>,
83) {
84    let rule = match get_rule(config, rule_id) {
85        Some(rule) => rule,
86        None => return,
87    };
88
89    if rules.contains(&rule) {
90        return;
91    }
92
93    let rule = match parent {
94        Some(parent) => rule.for_parent(parent),
95        None => rule,
96    };
97
98    match rule.ty {
99        RuleType::Multiple(ref m) => {
100            let parent = if m.hide_inner {
101                Some(rule.clone())
102            } else {
103                None
104            };
105            for rule_id in &m.rules {
106                collect_rules(config, rules, rule_id, parent.clone());
107            }
108        }
109        RuleType::Alias(ref a) => {
110            let parent = if a.hide_inner {
111                Some(rule.clone())
112            } else {
113                None
114            };
115            collect_rules(config, rules, &a.rule, parent);
116        }
117        RuleType::Unknown(_) => {}
118        _ => {
119            rules.insert(rule);
120        }
121    }
122}
123
124/// Reference to a PII rule.
125#[derive(Debug, Clone)]
126pub(super) struct RuleRef {
127    pub id: String,
128    pub origin: String,
129    pub ty: RuleType,
130    pub redaction: Redaction,
131}
132
133impl RuleRef {
134    fn new(id: String, spec: &RuleSpec) -> Self {
135        RuleRef {
136            origin: id.clone(),
137            id,
138            ty: spec.ty.clone(),
139            redaction: spec.redaction.clone(),
140        }
141    }
142
143    pub fn for_parent(self, parent: Self) -> Self {
144        RuleRef {
145            id: self.id,
146            origin: parent.origin,
147            ty: self.ty,
148            redaction: match parent.redaction {
149                Redaction::Default => self.redaction,
150                _ => parent.redaction,
151            },
152        }
153    }
154}
155
156impl PartialEq for RuleRef {
157    fn eq(&self, other: &Self) -> bool {
158        self.id == other.id
159    }
160}
161
162impl Eq for RuleRef {}
163
164impl PartialOrd for RuleRef {
165    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
166        Some(self.cmp(other))
167    }
168}
169
170impl Ord for RuleRef {
171    fn cmp(&self, other: &Self) -> Ordering {
172        self.id.cmp(&other.id)
173    }
174}