Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/lib/guardrails/piiMasker.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -11,7 +12,8 @@ type PiiDetection = {
type JsonRecord = Record<string, unknown>;

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");
}

Expand Down
32 changes: 30 additions & 2 deletions tests/unit/guardrails-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -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() {
Expand Down
Loading