diff --git a/src/lib/guardrails/piiMasker.ts b/src/lib/guardrails/piiMasker.ts index 20d4e59bce86..cb3b77f9564a 100644 --- a/src/lib/guardrails/piiMasker.ts +++ b/src/lib/guardrails/piiMasker.ts @@ -1,6 +1,7 @@ import { BaseGuardrail, type GuardrailContext, type GuardrailResult } from "./base"; import { processPII } from "@/shared/utils/inputSanitizer"; import { sanitizePII, sanitizePIIResponse } from "@/lib/piiSanitizer"; + import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags"; type PiiDetection = { @@ -11,7 +12,8 @@ type PiiDetection = { type JsonRecord = Record; function isRequestPiiMaskingEnabled() { - // Request PII redaction is controlled by PII_REDACTION_ENABLED feature flag (DB settings override env var) + // Request PII redaction is controlled by PII_REDACTION_ENABLED feature flag (DB > env > default). + // INPUT_SANITIZER_MODE only governs prompt-injection policy (warn/block/log). return isFeatureFlagEnabled("PII_REDACTION_ENABLED"); } diff --git a/tests/unit/guardrails-registry.test.ts b/tests/unit/guardrails-registry.test.ts index bb3e48590a11..a91f89d9bf6c 100644 --- a/tests/unit/guardrails-registry.test.ts +++ b/tests/unit/guardrails-registry.test.ts @@ -176,13 +176,42 @@ test("pii masker guardrail redacts request and response payloads", async () => { const postBody = postCall?.modifiedResponse as ChatLikePayload; const redactedContent = String(postBody.choices?.[0]?.message?.content); assert.ok( - redactedContent.includes("[EMAIL_REDACTED]") || redactedContent.includes("[PHONE_REDACTED]"), + redactedContent.includes("[EMAIL_REDACTED]") || + redactedContent.includes("[PHONE_REDACTED]"), "email or phone should be redacted in response" ); } ); }); +test("pii masker respects feature flag overrides (DB and env)", async () => { + const { setFeatureFlagOverride, removeFeatureFlagOverride } = + await import("@/lib/db/featureFlags"); + + await withEnv( + { + PII_REDACTION_ENABLED: undefined, + }, + async () => { + try { + setFeatureFlagOverride("PII_REDACTION_ENABLED", "true"); + const guardrail = new PIIMaskerGuardrail(); + const preCall = await guardrail.preCall({ + messages: [{ role: "user", content: "Email me at dev@example.com" }], + }); + assert.ok( + preCall?.modifiedPayload, + "DB override for PII_REDACTION_ENABLED=true should enable request redaction" + ); + const preBody = preCall?.modifiedPayload as ChatLikePayload; + assert.match(String(preBody.messages?.[0]?.content), /\[EMAIL_REDACTED\]/); + } finally { + removeFeatureFlagOverride("PII_REDACTION_ENABLED"); + } + } + ); +}); + test("pii masker does not rewrite request PII when redaction flag is off", async () => { await withEnv( { @@ -200,7 +229,6 @@ test("pii masker does not rewrite request PII when redaction flag is off", async ); }); - test("guardrail registry fails open when a guardrail throws", async () => { class ExplodingGuardrail extends BaseGuardrail { constructor() {