]> git.99rst.org Git - sgasser-llm-shield.git/commitdiff
Expose logging and secrets_detection in /info, remove unused log_content
authorStefan Gasser <redacted>
Wed, 10 Jun 2026 12:19:59 +0000 (14:19 +0200)
committerStefan Gasser <redacted>
Wed, 10 Jun 2026 12:19:59 +0000 (14:19 +0200)
Follow-ups from #91:

- /info now includes secrets_detection and logging sections so users can
  verify their config is loaded (the issue reporter could not).
- The dashboard fallback message no longer claims log_masked_content is
  false when content is simply absent for another reason.
- Remove the log_content option: it was defined in config and documented,
  but no code ever read it. Logging raw request/response content would
  contradict the privacy guarantees, so it is removed rather than
  implemented. Zod strips unknown keys, so existing configs that still
  set log_content keep loading.

config.example.yaml
docs/api-reference/status.mdx
docs/configuration/logging.mdx
src/config.ts
src/routes/info.test.ts
src/routes/info.ts
src/views/dashboard/page.tsx

index a63bf0dc14551382d9d2a518d68649a5cabf4a0b..00d7da466b9c366901bf6ef925eb17e13c2d3022 100644 (file)
@@ -156,8 +156,7 @@ secrets_detection:
   # 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)
@@ -178,11 +177,8 @@ logging:
   # 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
 
index 2735c134327cf98076173bb28a4f00ca4e05d196..cbcead3d5bfa4b8d04296a67a8723796a319d5d3 100644 (file)
@@ -86,6 +86,17 @@ curl http://localhost:3000/info
     "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
   }
index a37155a232b982ca57e7e850e443ee1e4636edb8..d610d13c0bddfcc3b1e9c6cfae732993b380e64e 100644 (file)
@@ -7,7 +7,6 @@ description: Configure request logging
 logging:
   database: ./data/pasteguard.db
   retention_days: 30
-  log_content: false
   log_masked_content: true
 ```
 
@@ -17,7 +16,6 @@ logging:
 |--------|---------|-------------|
 | `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
@@ -49,33 +47,23 @@ logging:
 
 ## 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
 ```
 
@@ -83,6 +71,7 @@ Only metadata (timestamps, models, PII detected) is logged.
 
 ## 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
index 3f6ceb9cd8d3d0b4379f22d1a14a1e7e9fafb619..3791118f11354a65d5c6721cd702a41f7d7f02f9 100644 (file)
@@ -83,7 +83,6 @@ const ServerSchema = z.object({
 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),
 });
 
index b4aa8f00c2292e3d713752eced6dc99f6075581a..a07d935270180ced3ac1168b5006a06849d62237 100644 (file)
@@ -23,6 +23,21 @@ describe("GET /info", () => {
     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");
 
index 5f06cd1d795ccfbc464443dac45dbdf0ba5abb20..6fcf0427d2dbe9945503ebfeff0b0f598069eb59 100644 (file)
@@ -43,6 +43,17 @@ infoRoutes.get("/info", (c) => {
       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) {
index 9f438d99051ccb12ada5e7bc0c78dc89368fd924..f1a33bf485aba869565ed142466e8e177b361d04 100644 (file)
@@ -542,7 +542,7 @@ function formatMaskedPreview(maskedContent, entities) {
   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) {
git clone https://git.99rst.org/PROJECT