-
Notifications
You must be signed in to change notification settings - Fork 0
feat(privacy): Privacy & Secret Protection Engine #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,161 @@ | ||||||||||||||||||||
| # Privacy & Secret Protection Engine | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Bower's privacy engine inspects every event **after parse and before | ||||||||||||||||||||
| persistence / normalisation**. It is a core control for preventing accidental | ||||||||||||||||||||
| leakage of regulated Australian identifiers, credentials and cryptographic | ||||||||||||||||||||
| material into downstream SIEM platforms. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ``` | ||||||||||||||||||||
| Raw Event → Parser → Privacy & Secret Engine → Normalisation → Output | ||||||||||||||||||||
| ├── Detect | ||||||||||||||||||||
| ├── Validate | ||||||||||||||||||||
| ├── Classify | ||||||||||||||||||||
| └── Apply Policy | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Design principles | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | Principle | How | | ||||||||||||||||||||
| | --- | --- | | ||||||||||||||||||||
| | Deterministic | Compiled regex, checksums, structure, entropy — **no AI at runtime** | | ||||||||||||||||||||
| | Streaming / per-event | Events processed independently; safe under concurrent callers | | ||||||||||||||||||||
| | Extensible | `ISensitiveDetector` plugins; provider API keys via pattern registry | | ||||||||||||||||||||
| | Configurable | Global default action + per-detector overrides | | ||||||||||||||||||||
| | Fail closed | Invalid / oversized payloads → redaction failure → quarantine | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Actions | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | Action | Effect | | ||||||||||||||||||||
| | --- | --- | | ||||||||||||||||||||
| | `Allow` | Leave value unchanged (still recorded in metadata if detected) | | ||||||||||||||||||||
| | `Remove` | Delete property (field-name) or replace span with empty | | ||||||||||||||||||||
| | `Replace` | Replace with configured placeholder | | ||||||||||||||||||||
| | `Mask` | Partial reveal (e.g. last 4 digits, email local-part) | | ||||||||||||||||||||
| | `Sha256` | `sha256:<hex>` | | ||||||||||||||||||||
| | `Hmac` | `hmac-sha256:<hex>` (requires 32+ byte key; else SHA-256) | | ||||||||||||||||||||
| | `Encrypt` | AES-GCM (`enc:aesgcm:…`); requires 32-byte key; else remove | | ||||||||||||||||||||
| | `AlertOnly` | Detect only; do not rewrite | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Default policy (production-oriented) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | Detector | Default | | ||||||||||||||||||||
| | --- | --- | | ||||||||||||||||||||
| | Field-name secrets | Remove | | ||||||||||||||||||||
| | TFN | SHA-256 | | ||||||||||||||||||||
| | CRN / Medicare / IHI / Passport / Licence / DVA | Mask | | ||||||||||||||||||||
| | ABN / ACN | Allow | | ||||||||||||||||||||
| | Credit card | Remove | | ||||||||||||||||||||
| | JWT / cloud secrets / API keys / PEM / DB / env | Remove | | ||||||||||||||||||||
| | Email / phone / DOB | Mask | | ||||||||||||||||||||
| | Security markings | AlertOnly (opt-in) | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Opt-in (disabled by default): IP, hostname, username, residential address, GPS, | ||||||||||||||||||||
| security markings. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Metadata | ||||||||||||||||||||
|
|
||||||||||||||||||||
| When findings exist, the engine attaches (never with original values): | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```json | ||||||||||||||||||||
| { | ||||||||||||||||||||
| "privacy": { | ||||||||||||||||||||
| "detected": ["au.tfn", "secret.jwt", "id.email"], | ||||||||||||||||||||
| "actions": { | ||||||||||||||||||||
| "au.tfn": "SHA256", | ||||||||||||||||||||
| "secret.jwt": "Removed", | ||||||||||||||||||||
| "id.email": "Masked" | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `SecurityEventEnvelope.Privacy` maps the same shape. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Detector modules | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Australian identifiers | ||||||||||||||||||||
|
|
||||||||||||||||||||
| | Id | Validation | | ||||||||||||||||||||
| | --- | --- | | ||||||||||||||||||||
| | `au.tfn` | ATO 8/9-digit checksum | | ||||||||||||||||||||
| | `au.crn` | Pattern (9 digits + letter) | | ||||||||||||||||||||
| | `au.medicare` | Medicare checksum + issue digit | | ||||||||||||||||||||
| | `au.ihi` | `800360` + Luhn | | ||||||||||||||||||||
| | `au.passport` | Format + contextual label | | ||||||||||||||||||||
| | `au.driver-licence` | State-heuristic formats (context gated) | | ||||||||||||||||||||
| | `au.abn` | ABN mod-89 | | ||||||||||||||||||||
| | `au.acn` | ACN check digit | | ||||||||||||||||||||
| | `au.dva` | DVA pattern | | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Financial | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `fin.credit-card` (Luhn + network), `fin.bsb-account`, `fin.iban` (mod-97), | ||||||||||||||||||||
| `fin.swift-bic`, `fin.payid`. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Identity | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `id.email`, `id.phone.au`, `id.phone.intl`, `id.dob`, plus optional | ||||||||||||||||||||
| `id.address`, `id.gps`, `id.ip`, `id.hostname`, `id.username`. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Secrets & crypto | ||||||||||||||||||||
|
|
||||||||||||||||||||
| AWS, Azure, Entra, GCP, JWT, OAuth, provider API keys (OpenAI, Anthropic, | ||||||||||||||||||||
| GitHub, GitLab, Slack, Stripe, Twilio, Cloudflare, Atlassian, Datadog, | ||||||||||||||||||||
| PagerDuty, Okta, MongoDB, Snowflake), Kubernetes, Docker, database connection | ||||||||||||||||||||
| strings, environment variable assignments, PEM/PKCS8/SSH/PGP/X.509. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ### Classification | ||||||||||||||||||||
|
|
||||||||||||||||||||
| `class.security-marking` — OFFICIAL, PROTECTED, SECRET, TOP SECRET, | ||||||||||||||||||||
| CABINET-IN-CONFIDENCE (opt-in). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Configuration (code) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ```csharp | ||||||||||||||||||||
| var defaults = PrivacyPolicy.CreateDefault(); | ||||||||||||||||||||
| var policy = new PrivacyPolicy | ||||||||||||||||||||
| { | ||||||||||||||||||||
| DefaultAction = defaults.DefaultAction, | ||||||||||||||||||||
| DetectorActions = new Dictionary<string, PrivacyAction>(defaults.DetectorActions) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| [DetectorIds.Tfn] = PrivacyAction.Hmac, | ||||||||||||||||||||
| [DetectorIds.Email] = PrivacyAction.Mask, | ||||||||||||||||||||
| [DetectorIds.Abn] = PrivacyAction.Allow | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| HmacKey = hmacKey32PlusBytes, | ||||||||||||||||||||
| EnabledOptInDetectors = new HashSet<string> { DetectorIds.IpAddress }, | ||||||||||||||||||||
| EmitMetadata = true | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| IEventRedactor redactor = new JsonEventRedactor(policy); | ||||||||||||||||||||
| // or | ||||||||||||||||||||
| var engine = new PrivacyEngine(policy, customDetectors); | ||||||||||||||||||||
| ``` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Extension points | ||||||||||||||||||||
|
|
||||||||||||||||||||
| 1. Implement `ISensitiveDetector` (value scan) and/or `IFieldNameDetector`. | ||||||||||||||||||||
| 2. Pass detectors into `PrivacyEngine` constructor **or** extend | ||||||||||||||||||||
| `DetectorCatalog.CreateDefaultValueDetectors()`. | ||||||||||||||||||||
| 3. Set per-id actions / disable flags on `PrivacyPolicy`. | ||||||||||||||||||||
|
Comment on lines
+137
to
+140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Require
Proposed documentation fix-1. Implement `ISensitiveDetector` (value scan) and/or `IFieldNameDetector`.
+1. Implement `ISensitiveDetector`; additionally implement `IFieldNameDetector`
+ when the detector needs field-name matching.As per coding guidelines, “New detectors must implement 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||||||||||||||||
| 4. Future (not in this milestone): WASM plugins, org-specific learning, | ||||||||||||||||||||
| AI-assisted policy recommendation (config-time only). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Performance | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - Per-event JSON walk; detectors only run on string leaves. | ||||||||||||||||||||
| - Overlapping matches resolved once (prefer longer, then earlier). | ||||||||||||||||||||
| - Replacements applied right-to-left. | ||||||||||||||||||||
| - Micro-benchmarks: `tests/Bower.Benchmarks` (Stopwatch; no extra packages). | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Tests | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - Unit tests under `tests/Bower.UnitTests` cover checksums and major detectors. | ||||||||||||||||||||
| - Existing `JsonEventRedactor` / `SensitiveDataDetector` tests remain green via | ||||||||||||||||||||
| compatibility facades. | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ## Related | ||||||||||||||||||||
|
|
||||||||||||||||||||
| - `src/Bower.Redaction/AGENTS.md` — contributor invariants | ||||||||||||||||||||
| - `src/Bower.Core/SecurityEventProcessor.cs` — redaction before enqueue | ||||||||||||||||||||
| } | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Privacy & Secret Protection Engine | ||
|
|
||
| Runtime redaction must stay **deterministic**. No AI, no network lookups, no | ||
| arbitrary code from configuration. | ||
|
|
||
| ## Invariants | ||
|
|
||
| - Redact **before** persistence. Treat redaction failure as a security failure. | ||
| - Never log original secrets, credentials, tokens, or unrestricted bodies. | ||
| - Privacy metadata may list detector ids and actions only — never original values. | ||
| - Default-deny field-name secrets (password, token, body, headers, …). | ||
| - Prefer checksum validation (TFN, ABN, ACN, Medicare, IHI, Luhn) to cut false positives. | ||
| - New detectors implement `ISensitiveDetector` and register via `DetectorCatalog` | ||
| or constructor injection — do not hard-code detector lists inside `PrivacyEngine`. | ||
| - Keep detectors independently enable/disable-able through `PrivacyPolicy`. | ||
| - Opt-in detectors (IP, hostname, username, address, GPS, markings) stay off unless enabled. | ||
| - Add unit tests for every new detector (positive + negative / invalid checksum). | ||
| - Add or update micro-benchmarks when changing hot paths. | ||
|
|
||
| ## Layout | ||
|
|
||
| - `Privacy/` — engine, policy, interfaces, catalog, applicator | ||
| - `Validation/` — checksum algorithms | ||
| - `Detectors/` — modular detector implementations by domain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Define one redaction boundary across the architecture docs.
The privacy guide places the engine after parsing, while the architecture guide places it before typed parsing. This ambiguity can lead integrations to parse sensitive raw data before the control runs.
docs/privacy/privacy-secret-engine.md#L3-L14: change the lifecycle text and diagram to place redaction before typed parsing.docs/architecture/overview.md#L10-L13: clarify that “typed parsing” means creation of typed event contracts after raw JSON redaction.Based on learnings, behavior changes require documentation updates.
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)
[warning] 8-8: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
📍 Affects 2 files
docs/privacy/privacy-secret-engine.md#L3-L14(this comment)docs/architecture/overview.md#L10-L13🤖 Prompt for AI Agents
Source: Learnings