Sanitizer Policy
DAWG 0.2.3-naughty sanitizes supported capture streams before packaging and then evaluates an Open Policy Agent (OPA) allow decision. Detection is heuristic: sanitization reduces exposure, but it is not a confidentiality or no-leak guarantee.
Processing Flow
flowchart LR
A[Supported JSONL] --> B[Parse each record]
B --> C[Classify field paths and values]
C --> D[Redact or synthesize]
D --> E[Write through temporary file]
E --> F[Evaluate OPA allow]
F --> G[Write sanitize report]
G --> H[Allow or block packaging]The sanitizer processes these files when they exist:
http/frontend.jsonlhttp/backend.jsonldb/diff.jsonllogs/structured.jsonltraces/rrweb.jsonlactions/browser.jsonlcassettes/thirdparty.jsonl
Other file formats are not sanitized by this pass. Each supported file is replaced only after its complete temporary output succeeds. Invalid JSONL aborts sanitization and leaves the original file intact.
Detection Rules
The rule engine recursively inspects JSON field paths and string values. It uses its own fixed heuristics rather than an external secret-scanning rule set, and it does not redact every unmatched field.
Sensitive Field Paths
A field is treated as sensitive when its normalized path contains one of these substrings:
- credentials and sessions:
password,authorization,cookie,token,secret,api_key,api-key,apikey,session - identity:
ssn,social_security,passport,national_id,tax_id - personal data:
date_of_birth,birthdate,address
Email, phone, card, and name handling also uses field paths:
- email paths or anchored email values
phoneormobilepaths, or anchored phone valuescardorcreditpaths, or anchored payment-card values- any non-structural path containing
name
Sensitive Values
String values are recognized as secrets when they match:
- the
Bearerprefix - a JWT-like three-segment value
- an AWS access key ID
- a GCP API key
- a GitHub token
- a generic private-key header
- a Slack token
Email, phone, and card values have separate anchored patterns. There is no generic rule that treats every long alphanumeric string as a secret.
Context and Structural Exceptions
To avoid corrupting rrweb structure, these path suffixes are preserved:
tagNamenodeNamelocalNamefieldNameinputTypeselector
The sanitizer also preserves doctype name and SVG viewBox and points values.
For an action field named value, the associated selector, fieldName, and inputType are added to the classification context. This allows values from password-like action targets to be detected even though the generic structural keys remain unchanged.
A string-valued body field is recursively sanitized only when its contents parse as JSON. Non-JSON body strings receive ordinary field/value classification rather than recursive parsing.
Replacement Behavior
| Category | Replacement |
|---|---|
user_<8 lowercase hex>@example.com | |
| Phone | +1555 followed by 7 deterministic digits |
| Name | User <8 uppercase hex> |
| Secrets, cards, and other sensitive values | [REDACTED] |
Email, phone, and name replacements are deterministic SHA-256-derived values based on the category and original value. The same category and input produce the same replacement. Payment-card data is not format-preserving.
Only email, phone, and name classifications produce synthetic values. Deterministic replacement can preserve some application behavior, but it does not guarantee that every application-specific validation rule will accept the result.
OPA Policy Gate
The engine evaluates exactly this decision:
data.dawg.sanitizer.allowA custom policy must therefore declare this package:
package dawg.sanitizerOPA does not receive the complete sanitized capture. Its input is limited to:
{
"fieldsRedacted": 0,
"blockedFields": []
}fieldsRedacted is the number of redacted or synthesized fields. blockedFields is passed to the report and policy, but the current heuristic scanner initializes it empty and does not add residual or unrecognized sensitive fields.
Bundled Default Policy
schema/policies/default.rego contains:
package dawg.sanitizer
import rego.v1
default allow := false
allow if {
input.fieldsRedacted >= 0
count(input.blockedFields) == 0
}With the current input construction, this policy allows every successfully completed sanitization pass. It does not independently scan output for remaining secrets.
A missing or invalid policy fails before the report is written. An explicit policy denial writes a report with exportAllowed: false and blocks packaging.
Generated policy
dawg init writes a separate state policy containing default allow = true. The current CLI does not load dawg.config.yaml; select a custom policy for capture with dawg capture --policy-file <file>.
Custom Policy Example
Because policy input contains only a count and a blocked-field list, custom decisions must be written against those values:
package dawg.sanitizer
import rego.v1
default allow := false
allow if {
input.fieldsRedacted <= 100
count(input.blockedFields) == 0
}Apply it during capture:
dawg capture --url <http-or-https-url> --policy-file ./policy.regoThis example limits the number of changed fields; it cannot inspect their original or sanitized values.
Sanitization Report
A completed pass writes sanitize-report.json. Capture currently records policy version 1.0.0.
{
"policyVersion": "1.0.0",
"policyFile": "/path/to/default.rego",
"opaResult": "allow",
"fieldsScanned": 42,
"fieldsRedacted": 1,
"redactions": [
{
"file": "actions/browser.jsonl",
"line": 1,
"field": "value",
"reason": "secret:token",
"action": "redacted"
}
],
"blockedFields": [],
"exportAllowed": true
}A synthetic redaction entry additionally includes syntheticValue. The report does not contain aggregate fieldsSynthetic or secretsDetected fields, reviewer metadata, or a timestamp.
Packaging requires both meta.json and a sanitization report whose exportAllowed value is true.
Unsafe Localhost Bypass
--unsafe-skip-sanitize is accepted only when capturing a localhost target. It leaves captured content unchanged and writes an allowed report with policyVersion and policyFile set to skipped, while opaResult remains allow.
Use this option only for controlled local data. The resulting artifact can contain credentials, personal data, and any other values present in the capture.
Security Boundaries
- Detection depends on the documented field substrings and value patterns; novel names and formats can pass unchanged.
- OPA sees counters and blocked-field names, not captured records or residual values.
blockedFieldsdoes not currently represent heuristic misses.- String bodies that are not valid JSON are not recursively inspected as structured payloads.
- rrweb input masking does not protect values copied into the separate browser action stream; that stream relies on sanitizer detection.
- A successful default policy decision means the configured sanitization pass completed and was allowed. It does not prove that the artifact contains no PII or secrets.
Review capture sources and sanitized artifacts according to your organization's data-handling requirements before sharing or pushing them to a registry.