subcog/security/mod.rs
1//! Security features.
2//!
3//! Secret detection, PII filtering, content redaction, and audit logging.
4//!
5//! # Overview
6//!
7//! This module provides security features for protecting sensitive content:
8//!
9//! - **Secret Detection**: Identifies API keys, tokens, credentials in content
10//! - **PII Detection**: Finds personally identifiable information
11//! - **Content Redaction**: Masks or removes sensitive content before storage
12//! - **Audit Logging**: SOC2/GDPR-compliant event logging
13//!
14//! # Redaction Patterns
15//!
16//! ## Secret Patterns
17//!
18//! | Pattern | Regex | Example |
19//! |---------|-------|---------|
20//! | AWS Access Key | `AKIA[0-9A-Z]{16}` | `AKIAIOSFODNN7EXAMPLE` |
21//! | AWS Secret Key | `aws_secret_access_key\s*=\s*[A-Za-z0-9/+=]{40}` | `aws_secret_key = wJalrXUtnFEMI...` |
22//! | GitHub Token | `gh[pousr]_[A-Za-z0-9_]{36,}` | `ghp_xxxxxxxxxxxx...` |
23//! | GitHub PAT | `github_pat_[A-Za-z0-9_]{22,}` | `github_pat_xxxxx...` |
24//! | Generic API Key | `api[_-]?key\s*[=:]\s*[A-Za-z0-9_\-]{24,}` | `api_key = sk-xxxxx...` |
25//! | Generic Secret | `(?:secret\|password)\s*[=:]\s*[^\s]{8,}` | `password = mypassword` |
26//! | Private Key | `-----BEGIN (?:RSA )?PRIVATE KEY-----` | PEM headers |
27//! | JWT | `eyJ[...].eyJ[...].[...]` | Base64-encoded JWT |
28//! | Slack Token | `xox[baprs]-[0-9]{10,13}-...` | `xoxb-123456789012-...` |
29//! | Slack Webhook | `https://hooks.slack.com/services/T.../B.../...` | Webhook URLs |
30//! | Google API Key | `AIza[0-9A-Za-z_-]{35}` | `AIzaSyC...` |
31//! | Stripe Key | `(?:sk\|pk)_(?:live\|test)_[A-Za-z0-9]{24,}` | `sk_live_xxxxx...` |
32//! | Database URL | `(?:postgres\|mysql)://user:pass@host` | Connection strings |
33//! | Bearer Token | `bearer\s+[A-Za-z0-9_\-.]{20,}` | `Bearer eyJhbGci...` |
34//! | `OpenAI` API Key | `sk-[A-Za-z0-9]{48}` | `sk-xxxxxxxx...` |
35//! | Anthropic API Key | `sk-ant-api[A-Za-z0-9_-]{90,}` | `sk-ant-api03-xxx...` |
36//!
37//! ## PII Patterns
38//!
39//! | Pattern | Regex | Example |
40//! |---------|-------|---------|
41//! | Email Address | `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}` | `user@example.com` |
42//! | SSN | `\d{3}-\d{2}-\d{4}` | `123-45-6789` |
43//! | Phone Number | `\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}` | `(555) 123-4567` |
44//! | Credit Card | `4[0-9]{12}(?:[0-9]{3})?` (Visa) | `4111111111111111` |
45//! | IP Address | `(?:\d{1,3}\.){3}\d{1,3}` | `192.168.1.1` |
46//! | Date of Birth | `(?:dob\|birth\s*date)\s*[:=]?\s*\d{1,2}[/-]\d{1,2}[/-]\d{2,4}` | `DOB: 01/15/1990` |
47//! | ZIP Code | `\d{5}(?:-\d{4})?` | `90210` |
48//! | Driver's License | `(?:driver's?\s*license\|dl)\s*#?\s*[A-Z0-9]{6,12}` | `DL: D12345678` |
49//! | Passport | `passport\s*#?\s*[A-Z0-9]{6,9}` | `Passport: AB123456` |
50//!
51//! ## Redaction Modes
52//!
53//! | Mode | Output | Example |
54//! |------|--------|---------|
55//! | `Mask` (default) | `[REDACTED]` | `Key: [REDACTED]` |
56//! | `TypedMask` | `[REDACTED:TYPE]` | `Key: [REDACTED:AWS_ACCESS_KEY_ID]` |
57//! | `Asterisks` | `****...` | `Key: ********************` |
58//! | `Remove` | (empty) | `Key: ` |
59//!
60//! # Usage
61//!
62//! ```rust
63//! use subcog::security::{ContentRedactor, RedactionConfig, RedactionMode, SecretDetector};
64//!
65//! // Basic secret detection
66//! let detector = SecretDetector::new();
67//! assert!(detector.contains_secrets("AKIAIOSFODNN7EXAMPLE"));
68//!
69//! // Redact secrets with custom mode
70//! let config = RedactionConfig::new()
71//! .with_mode(RedactionMode::TypedMask)
72//! .with_pii(); // Also redact PII
73//! let redactor = ContentRedactor::with_config(config);
74//! let redacted = redactor.redact("Key: AKIAIOSFODNN7EXAMPLE");
75//! assert!(redacted.contains("[REDACTED:AWS_ACCESS_KEY_ID]"));
76//! ```
77//!
78//! # False Positive Prevention
79//!
80//! Generic patterns (API keys, secrets, bearer tokens) include placeholder filtering:
81//!
82//! ```rust
83//! use subcog::security::SecretDetector;
84//!
85//! let detector = SecretDetector::new();
86//! // Placeholders are NOT flagged
87//! assert!(!detector.contains_secrets("api_key = your_api_key_here"));
88//! assert!(!detector.contains_secrets("api_key = example_key_12345678"));
89//! ```
90//!
91//! Filtered placeholder patterns: `example`, `test`, `demo`, `your_`, `placeholder`,
92//! `changeme`, `xxx`, `foo`, `bar`, `sample`, `fake`, `dummy`, `mock`.
93//!
94//! # Graceful Degradation
95//!
96//! All detection is performed locally without external dependencies:
97//!
98//! - No network calls required
99//! - No LLM fallback needed
100//! - Regex patterns are statically compiled at startup
101//! - Detection completes in <5ms for typical content
102
103// Allow cast_possible_truncation for timestamp conversions.
104#![allow(clippy::cast_possible_truncation)]
105// Allow match_same_arms for explicit enum handling.
106#![allow(clippy::match_same_arms)]
107// Allow clone_on_ref_ptr - Arc clones are intentional.
108#![allow(clippy::clone_on_ref_ptr)]
109// Allow option_if_let_else for clearer if-let patterns with mutex locks.
110#![allow(clippy::option_if_let_else)]
111// Allow needless_pass_by_value for entry types passed by value for cloning.
112#![allow(clippy::needless_pass_by_value)]
113// Allow unused_self for methods kept for API consistency.
114#![allow(clippy::unused_self)]
115
116mod audit;
117pub mod encryption;
118mod pii;
119pub mod rbac;
120mod redactor;
121mod secrets;
122
123pub use audit::{
124 AccessReviewReport, ActorAccessSummary, AuditConfig, AuditEntry, AuditLogger, AuditOutcome,
125 OutcomeSummary, global_logger, init_global, record_event,
126};
127pub use encryption::{EncryptionConfig, Encryptor, is_encrypted};
128pub use pii::{PiiDetector, PiiMatch};
129pub use rbac::{
130 AccessControl, AccessResult, Permission, PermissionCategory, RbacSummary, Role, RoleSummary,
131};
132pub use redactor::{ContentRedactor, RedactionConfig, RedactionMode};
133pub use secrets::{SecretDetector, SecretMatch};