Skip to main content

sigil_protocol/
scanner.rs

1//! Sensitivity scanning — detect sensitive content before it enters agent context.
2//!
3//! SIGIL defines the trait; implementations choose their detection strategy
4//! (regex, ML-based, dictionary lookup, etc.).
5
6/// Trait for detecting sensitive content in text.
7///
8/// Implementors define their own patterns and detection logic.
9/// The protocol requires only that detected content is categorized
10/// by a human-readable name (e.g., "API Key", "IBAN", "Bank PIN").
11///
12/// # Example
13///
14/// ```rust
15/// use sigil_protocol::SensitivityScanner;
16///
17/// struct RegexScanner { /* ... */ }
18///
19/// impl SensitivityScanner for RegexScanner {
20///     fn scan(&self, text: &str) -> Option<String> {
21///         if text.contains("sk-") {
22///             Some("API Key".to_string())
23///         } else {
24///             None
25///         }
26///     }
27/// }
28/// ```
29pub trait SensitivityScanner: Send + Sync {
30    /// Scan text for sensitive content.
31    ///
32    /// Returns `Some(category_name)` if sensitive content is detected,
33    /// or `None` if the text is safe.
34    ///
35    /// The category name is used in audit logs and vault metadata.
36    fn scan(&self, text: &str) -> Option<String>;
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    struct MockScanner;
44
45    impl SensitivityScanner for MockScanner {
46        fn scan(&self, text: &str) -> Option<String> {
47            if text.contains("SECRET") {
48                Some("Test Secret".to_string())
49            } else {
50                None
51            }
52        }
53    }
54
55    #[test]
56    fn scanner_detects_sensitive_content() {
57        let scanner = MockScanner;
58        assert_eq!(
59            scanner.scan("Contains SECRET data"),
60            Some("Test Secret".to_string())
61        );
62    }
63
64    #[test]
65    fn scanner_passes_safe_content() {
66        let scanner = MockScanner;
67        assert!(scanner.scan("This is totally safe").is_none());
68    }
69}