From: Stefan Gasser Date: Tue, 23 Jun 2026 06:34:57 +0000 (+0200) Subject: Rename masking whitelist config to allowlist (#104) X-Git-Tag: v0.7.0~13 X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=5bd68d750160dbac0925a9c4d4fa44e42857f83f;p=sgasser-llm-shield.git Rename masking whitelist config to allowlist (#104) Rename the masking whitelist config key and its associated schema, types, and helper (filterAllowlistedEntities) to use allowlist terminology. Updates docs and the example config to match. --- diff --git a/config.example.yaml b/config.example.yaml index e4a8cdb..426f122 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -51,7 +51,7 @@ masking: marker_text: "[protected]" # Text patterns that are never masked (protects against false positives) - # whitelist: + # allowlist: # - pattern: "Company Name Inc." # - pattern: 'TEST-\d+' # regex: true diff --git a/docs/concepts/mask-mode.mdx b/docs/concepts/mask-mode.mdx index 8558b5d..7524160 100644 --- a/docs/concepts/mask-mode.mdx +++ b/docs/concepts/mask-mode.mdx @@ -46,7 +46,7 @@ providers: masking: show_markers: false marker_text: "[protected]" - whitelist: + allowlist: - pattern: "Company Name Inc." - pattern: 'TEST-\d+' regex: true @@ -62,7 +62,7 @@ masking: |--------|---------|-------------| | `show_markers` | `false` | Add visual markers around unmasked values | | `marker_text` | `[protected]` | Marker text if enabled | -| `whitelist` | `[]` | Text patterns that are never masked; set `regex: true` for regex patterns | +| `allowlist` | `[]` | Text patterns that are never masked; set `regex: true` for regex patterns | | `denylist` | `[]` | Text patterns that are always masked with the configured `type`; set `regex: true` for regex patterns | ## Response Headers diff --git a/docs/configuration/pii-detection.mdx b/docs/configuration/pii-detection.mdx index 92cda1e..11e9dbc 100644 --- a/docs/configuration/pii-detection.mdx +++ b/docs/configuration/pii-detection.mdx @@ -85,13 +85,13 @@ environment variables on the detector service — e.g. `DETECTOR_FLOOR_PERSON=0. (fewer person false positives) or `DETECTOR_FLOOR_LOCATION=0.4` (more location recall). Street addresses are detected by the model and reported as `LOCATION`. -## Whitelist +## Allowlist Exclude specific text patterns from PII masking. Useful for preventing false positives on company names or product identifiers. ```yaml masking: - whitelist: + allowlist: - pattern: "Acme Corp" - pattern: "Product XYZ" - pattern: 'TEST-\d+' diff --git a/src/config.test.ts b/src/config.test.ts index 1b62691..cd83f54 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -56,14 +56,14 @@ pii_detection: } }); - test("accepts masking whitelist and denylist patterns", () => { + test("accepts masking allowlist and denylist patterns", () => { const path = writeConfig(` mode: mask providers: openai: {} anthropic: {} masking: - whitelist: + allowlist: - "Acme Corp" - pattern: 'TEST-\\d+' regex: true @@ -80,7 +80,7 @@ pii_detection: try { const config = loadConfig(path); - expect(config.masking.whitelist).toEqual([ + expect(config.masking.allowlist).toEqual([ { pattern: "You are Claude Code, Anthropic's official CLI for Claude.", regex: false }, { pattern: "Acme Corp", regex: false }, { pattern: "TEST-\\d+", regex: true }, @@ -94,14 +94,14 @@ pii_detection: } }); - test("rejects invalid masking whitelist regex patterns", () => { + test("rejects invalid masking allowlist regex patterns", () => { const path = writeConfig(` mode: mask providers: openai: {} anthropic: {} masking: - whitelist: + allowlist: - pattern: "[Acme" regex: true pii_detection: diff --git a/src/config.ts b/src/config.ts index 60bf1c4..a2e6673 100644 --- a/src/config.ts +++ b/src/config.ts @@ -30,7 +30,7 @@ const CodexProviderSchema = z.object({ base_url: z.string().url().default("https://chatgpt.com/backend-api/codex"), }); -const DEFAULT_WHITELIST = [ +const DEFAULT_ALLOWLIST = [ { pattern: "You are Claude Code, Anthropic's official CLI for Claude.", regex: false }, ]; @@ -64,7 +64,7 @@ function validateRegexPattern( } } -const WhitelistPatternSchema = z.union([ +const AllowlistPatternSchema = z.union([ z .string() .min(1) @@ -75,7 +75,7 @@ const WhitelistPatternSchema = z.union([ regex: z.boolean().default(false), }) .superRefine((entry, ctx) => { - validateRegexPattern(entry.pattern, entry.regex, ctx, "Invalid whitelist regex pattern"); + validateRegexPattern(entry.pattern, entry.regex, ctx, "Invalid allowlist regex pattern"); }), ]); @@ -92,10 +92,10 @@ const DenylistPatternSchema = z const MaskingSchema = z.object({ show_markers: z.boolean().default(false), marker_text: z.string().default("[protected]"), - whitelist: z - .array(WhitelistPatternSchema) + allowlist: z + .array(AllowlistPatternSchema) .default([]) - .transform((arr) => [...DEFAULT_WHITELIST, ...arr]), + .transform((arr) => [...DEFAULT_ALLOWLIST, ...arr]), denylist: z.array(DenylistPatternSchema).default([]), }); @@ -228,7 +228,7 @@ export type AnthropicProviderConfig = z.infer; export type CodexProviderConfig = z.infer; export type LocalProviderConfig = z.infer; export type MaskingConfig = z.infer; -export type WhitelistPattern = z.infer; +export type AllowlistPattern = z.infer; export type DenylistPattern = z.infer; export type SecretsDetectionConfig = z.infer; export type ServerConfig = z.infer; diff --git a/src/pii/detect.test.ts b/src/pii/detect.test.ts index 7fb6e4c..cc65f59 100644 --- a/src/pii/detect.test.ts +++ b/src/pii/detect.test.ts @@ -3,7 +3,7 @@ import { getConfig } from "../config"; import { openaiExtractor } from "../masking/extractors/openai"; import type { OpenAIMessage, OpenAIRequest } from "../providers/openai/types"; import { - filterWhitelistedEntities, + filterAllowlistedEntities, findDenylistedEntities, mergeDenylistEntities, PIIDetector, @@ -311,70 +311,70 @@ describe("PIIDetector", () => { }); }); - describe("filterWhitelistedEntities", () => { - test("filters entities matching whitelist pattern", () => { + describe("filterAllowlistedEntities", () => { + test("filters entities matching allowlist pattern", () => { const text = "You are Claude Code, Anthropic's official CLI for Claude."; const entities = [{ entity_type: "PERSON", start: 8, end: 14, score: 0.9 }]; - const whitelist = [ + const allowlist = [ { pattern: "You are Claude Code, Anthropic's official CLI for Claude.", regex: false }, ]; - const result = filterWhitelistedEntities(text, entities, whitelist); + const result = filterAllowlistedEntities(text, entities, allowlist); expect(result).toHaveLength(0); }); - test("keeps entities not in whitelist", () => { + test("keeps entities not in allowlist", () => { const text = "Contact John Doe at john@example.com"; const entities = [ { entity_type: "PERSON", start: 8, end: 16, score: 0.9 }, { entity_type: "EMAIL_ADDRESS", start: 20, end: 36, score: 0.95 }, ]; - const whitelist = [{ pattern: "Claude", regex: false }]; + const allowlist = [{ pattern: "Claude", regex: false }]; - const result = filterWhitelistedEntities(text, entities, whitelist); + const result = filterAllowlistedEntities(text, entities, allowlist); expect(result).toHaveLength(2); }); - test("filters when entity text is contained in whitelist pattern", () => { + test("filters when entity text is contained in allowlist pattern", () => { const text = "Hello Claude, how are you?"; const entities = [{ entity_type: "PERSON", start: 6, end: 12, score: 0.85 }]; - const whitelist = [{ pattern: "You are Claude Code", regex: false }]; + const allowlist = [{ pattern: "You are Claude Code", regex: false }]; - const result = filterWhitelistedEntities(text, entities, whitelist); + const result = filterAllowlistedEntities(text, entities, allowlist); expect(result).toHaveLength(0); }); - test("returns all entities when whitelist is empty", () => { + test("returns all entities when allowlist is empty", () => { const text = "Contact Claude at claude@example.com"; const entities = [ { entity_type: "PERSON", start: 8, end: 14, score: 0.9 }, { entity_type: "EMAIL_ADDRESS", start: 18, end: 36, score: 0.95 }, ]; - const result = filterWhitelistedEntities(text, entities, []); + const result = filterAllowlistedEntities(text, entities, []); expect(result).toHaveLength(2); }); - test("filters entities matching regex whitelist pattern", () => { + test("filters entities matching regex allowlist pattern", () => { const text = "Reference TEST-1234 is public"; const entities = [{ entity_type: "CUSTOMER_ID", start: 10, end: 19, score: 0.9 }]; - const whitelist = [{ pattern: "TEST-\\d+", regex: true }]; + const allowlist = [{ pattern: "TEST-\\d+", regex: true }]; - const result = filterWhitelistedEntities(text, entities, whitelist); + const result = filterAllowlistedEntities(text, entities, allowlist); expect(result).toHaveLength(0); }); - test("does not filter when a regex whitelist only partially matches the entity", () => { + test("does not filter when a regex allowlist only partially matches the entity", () => { const text = "card 1234567890123456 end"; const entities = [{ entity_type: "CREDIT_CARD", start: 5, end: 21, score: 0.99 }]; - const whitelist = [{ pattern: "\\d{4}", regex: true }]; + const allowlist = [{ pattern: "\\d{4}", regex: true }]; - const result = filterWhitelistedEntities(text, entities, whitelist); + const result = filterAllowlistedEntities(text, entities, allowlist); expect(result).toHaveLength(1); }); diff --git a/src/pii/detect.ts b/src/pii/detect.ts index ee4410e..0183961 100644 --- a/src/pii/detect.ts +++ b/src/pii/detect.ts @@ -1,4 +1,4 @@ -import { type DenylistPattern, getConfig, type WhitelistPattern } from "../config"; +import { type AllowlistPattern, type DenylistPattern, getConfig } from "../config"; import { HEALTH_CHECK_TIMEOUT_MS } from "../constants/timeouts"; import { overlaps, resolveConflicts } from "../masking/conflict-resolver"; import type { RequestExtractor } from "../masking/types"; @@ -110,16 +110,16 @@ export function mergeDenylistEntities(detected: PIIEntity[], denylisted: PIIEnti return result.map((r) => r.e); } -export function filterWhitelistedEntities( +export function filterAllowlistedEntities( text: string, entities: PIIEntity[], - whitelist: WhitelistPattern[], + allowlist: AllowlistPattern[], ): PIIEntity[] { - if (whitelist.length === 0) return entities; + if (allowlist.length === 0) return entities; return entities.filter((entity) => { const detectedText = text.slice(entity.start, entity.end); - return !whitelist.some(({ pattern, regex }) => { + return !allowlist.some(({ pattern, regex }) => { if (regex) { // Anchor to the whole entity so a partial match can't un-mask a larger detected span. return new RegExp(`^(?:${pattern})$`).test(detectedText); @@ -236,7 +236,7 @@ export class PIIDetector { const scanRoles = config.pii_detection.scan_roles ? new Set(config.pii_detection.scan_roles) : null; - const whitelist = config.masking.whitelist; + const allowlist = config.masking.allowlist; const denylist = config.masking.denylist; const spanEntities: PIIEntity[][] = await Promise.all( @@ -251,7 +251,7 @@ export class PIIDetector { const detectedEntities = config.pii_detection.enabled ? await this.detectPII(span.text, langResult.language) : []; - const filteredEntities = filterWhitelistedEntities(span.text, detectedEntities, whitelist); + const filteredEntities = filterAllowlistedEntities(span.text, detectedEntities, allowlist); return mergeDenylistEntities(filteredEntities, denylistedEntities); }), ); diff --git a/src/pii/mask.test.ts b/src/pii/mask.test.ts index da7ceff..ae71712 100644 --- a/src/pii/mask.test.ts +++ b/src/pii/mask.test.ts @@ -17,14 +17,14 @@ import { const defaultConfig: MaskingConfig = { show_markers: false, marker_text: "[protected]", - whitelist: [], + allowlist: [], denylist: [], }; const configWithMarkers: MaskingConfig = { show_markers: true, marker_text: "[protected]", - whitelist: [], + allowlist: [], denylist: [], }; diff --git a/src/providers/anthropic/stream-transformer.test.ts b/src/providers/anthropic/stream-transformer.test.ts index 28bfe16..37908e2 100644 --- a/src/providers/anthropic/stream-transformer.test.ts +++ b/src/providers/anthropic/stream-transformer.test.ts @@ -6,7 +6,7 @@ import { createAnthropicUnmaskingStream } from "./stream-transformer"; const defaultConfig: MaskingConfig = { show_markers: false, marker_text: "[protected]", - whitelist: [], + allowlist: [], denylist: [], }; diff --git a/src/providers/openai/stream-transformer.test.ts b/src/providers/openai/stream-transformer.test.ts index d4dcd73..2566fcc 100644 --- a/src/providers/openai/stream-transformer.test.ts +++ b/src/providers/openai/stream-transformer.test.ts @@ -6,7 +6,7 @@ import { createUnmaskingStream } from "./stream-transformer"; const defaultConfig: MaskingConfig = { show_markers: false, marker_text: "[protected]", - whitelist: [], + allowlist: [], denylist: [], }; diff --git a/src/routes/api.test.ts b/src/routes/api.test.ts index ffdc738..a0747fa 100644 --- a/src/routes/api.test.ts +++ b/src/routes/api.test.ts @@ -1,7 +1,7 @@ import { describe, expect, mock, test } from "bun:test"; import { Hono } from "hono"; import { - filterWhitelistedEntities, + filterAllowlistedEntities, findDenylistedEntities, mergeDenylistEntities, type PIIEntity, @@ -16,7 +16,7 @@ mock.module("../pii/detect", () => ({ detectPII: mockDetectPII, healthCheck: mock(() => Promise.resolve(true)), }), - filterWhitelistedEntities, + filterAllowlistedEntities, findDenylistedEntities, mergeDenylistEntities, })); diff --git a/src/routes/api.ts b/src/routes/api.ts index 8c00f33..0bd26d1 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -10,7 +10,7 @@ import { z } from "zod"; import { getConfig, type SecretsDetectionConfig } from "../config"; import { createPlaceholderContext, type PlaceholderContext } from "../masking/context"; import { - filterWhitelistedEntities, + filterAllowlistedEntities, findDenylistedEntities, getPIIDetector, mergeDenylistEntities, @@ -208,11 +208,10 @@ apiRoutes.post("/mask", async (c) => { : []; scanTimeMs = Date.now() - piiStartTime; - // Apply whitelist filtering - const filteredEntities = filterWhitelistedEntities( + const filteredEntities = filterAllowlistedEntities( maskedText, piiEntities, - config.masking.whitelist, + config.masking.allowlist, ); const entitiesToMask = mergeDenylistEntities( filteredEntities, diff --git a/src/routes/codex.test.ts b/src/routes/codex.test.ts index c92763e..db8e370 100644 --- a/src/routes/codex.test.ts +++ b/src/routes/codex.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, mock, test } from "bun:test"; import { Hono } from "hono"; import { getConfig } from "../config"; -import { filterWhitelistedEntities, type PIIDetectionResult, PIIDetector } from "../pii/detect"; +import { filterAllowlistedEntities, type PIIDetectionResult, PIIDetector } from "../pii/detect"; const mockAnalyzeRequest = mock<() => Promise>(() => Promise.resolve({ @@ -17,7 +17,7 @@ const mockLogRequest = mock(() => {}); mock.module("../pii/detect", () => ({ PIIDetector, - filterWhitelistedEntities, + filterAllowlistedEntities, getPIIDetector: () => ({ analyzeRequest: mockAnalyzeRequest, detectPII: mock(() => Promise.resolve([])),