# The 200KB default covers typical use cases
max_scan_chars: 200000
- # Log detected secret types (never logs secret content)
- # Even if logging.log_content is true, secret content is never logged
+ # Log detected secret types (never logs raw secret content)
log_detected_types: true
# Which message roles to scan for secrets (optional)
# Log retention in days (0 = keep forever)
retention_days: 30
- # Log request/response content (may contain sensitive data!)
- log_content: false
-
# Log masked content for dashboard preview (default: true)
- # Shows what was actually sent to provider with PII replaced by placeholders
+ # Shows what was actually sent to provider with PII and secrets replaced by placeholders
# Disable if you don't want any content stored, even masked
log_masked_content: true
"score_threshold": 0.7,
"entities": ["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER"]
},
+ "secrets_detection": {
+ "enabled": true,
+ "action": "mask",
+ "entities": ["OPENSSH_PRIVATE_KEY", "PEM_PRIVATE_KEY"],
+ "max_scan_chars": 200000,
+ "log_detected_types": true
+ },
+ "logging": {
+ "retention_days": 30,
+ "log_masked_content": true
+ },
"masking": {
"show_markers": false
}
logging:
database: ./data/pasteguard.db
retention_days: 30
- log_content: false
log_masked_content: true
```
|--------|---------|-------------|
| `database` | `./data/pasteguard.db` | SQLite database path |
| `retention_days` | `30` | Days to keep logs. `0` = forever |
-| `log_content` | `false` | Log raw request/response (contains PII!) |
| `log_masked_content` | `true` | Log masked version for dashboard |
## Database
## Content Logging
-### Raw Content (not recommended)
-
-Logs original request/response including PII:
-
-```yaml
-logging:
- log_content: true # Contains sensitive data!
-```
-
### Masked Content (default)
-Logs masked version for dashboard preview:
+Logs the masked version for dashboard preview:
```yaml
logging:
log_masked_content: true
```
-Shows what was actually sent upstream with PII replaced by placeholders.
+Shows what was actually sent upstream with PII and secrets replaced by placeholders.
### No Content
-Disable all content logging:
+Disable content logging:
```yaml
logging:
- log_content: false
log_masked_content: false
```
## Security
-- Secret content is **never** logged, even if `log_content: true`
+- Raw request/response content is **never** logged — only the masked version, and only when `log_masked_content` is enabled
+- With `secrets_detection.action: route_local`, content is not logged at all when secrets are detected, since secrets stay unmasked for the local provider
- Only secret types are logged if `log_detected_types: true`
-- Masked content shows placeholders like `[[EMAIL_ADDRESS_1]]`, not real PII
+- Masked content shows placeholders like `[[EMAIL_ADDRESS_1]]` and `[API_KEY_SK_1]`, not real values
const LoggingSchema = z.object({
database: z.string().default("./data/pasteguard.db"),
retention_days: z.coerce.number().int().min(0).default(30),
- log_content: z.boolean().default(false),
log_masked_content: z.boolean().default(true),
});
expect(body.pii_detection).toBeDefined();
});
+ test("includes secrets_detection and logging sections", async () => {
+ const res = await app.request("/info");
+
+ const body = (await res.json()) as Record<string, Record<string, unknown>>;
+ expect(body.secrets_detection).toBeDefined();
+ expect(body.secrets_detection.enabled).toBeDefined();
+ expect(body.secrets_detection.action).toBeDefined();
+ expect(body.secrets_detection.entities).toBeDefined();
+ expect(body.logging).toBeDefined();
+ expect(body.logging.retention_days).toBeDefined();
+ expect(body.logging.log_masked_content).toBeDefined();
+ // Database path is intentionally not exposed
+ expect(body.logging.database).toBeUndefined();
+ });
+
test("returns correct content-type", async () => {
const res = await app.request("/info");
score_threshold: config.pii_detection.score_threshold,
entities: config.pii_detection.entities,
},
+ secrets_detection: {
+ enabled: config.secrets_detection.enabled,
+ action: config.secrets_detection.action,
+ entities: config.secrets_detection.entities,
+ max_scan_chars: config.secrets_detection.max_scan_chars,
+ log_detected_types: config.secrets_detection.log_detected_types,
+ },
+ logging: {
+ retention_days: config.logging.retention_days,
+ log_masked_content: config.logging.log_masked_content,
+ },
};
if (config.mode === "route" && config.local) {
if (!entities || entities.length === 0) {
return '<span class="text-text-muted">No PII detected in this request</span>';
}
- return '<span class="text-text-muted">Masked content not logged (log_masked_content: false)</span>';
+ return '<span class="text-text-muted">Masked content not logged</span>';
}
function renderEntityList(entities) {