From a9f98fcb48d554bee55e783bd87a6804067fd9a8 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 26 Aug 2026 21:30:08 -0300 Subject: [PATCH] test(video): expose guardrail log retention paths --- ...hatcore-postcall-guardrail-context.test.ts | 10 ++- tests/unit/guardrails-registry.test.ts | 83 +++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/tests/unit/chatcore-postcall-guardrail-context.test.ts b/tests/unit/chatcore-postcall-guardrail-context.test.ts index 74d39a4f4d..d3dafddfb6 100644 --- a/tests/unit/chatcore-postcall-guardrail-context.test.ts +++ b/tests/unit/chatcore-postcall-guardrail-context.test.ts @@ -7,9 +7,8 @@ import { test } from "node:test"; import assert from "node:assert/strict"; -const { buildPostCallGuardrailContext } = await import( - "../../open-sse/handlers/chatCore/postCallGuardrailContext.ts" -); +const { buildPostCallGuardrailContext } = + await import("../../open-sse/handlers/chatCore/postCallGuardrailContext.ts"); function baseArgs(overrides: Record = {}) { return { @@ -47,6 +46,11 @@ test("maps fields, constants, and source/target formats", () => { }); }); +test("propagates the server-owned transcript sensitivity bit", () => { + const ctx = buildPostCallGuardrailContext(baseArgs({ videoTranscriptSensitive: true }), () => []); + assert.equal(ctx.videoTranscriptSensitive, true); +}); + test("null clientRawRequest → endpoint/headers null", () => { const ctx = buildPostCallGuardrailContext(baseArgs({ clientRawRequest: null }), () => []); assert.equal(ctx.endpoint, null); diff --git a/tests/unit/guardrails-registry.test.ts b/tests/unit/guardrails-registry.test.ts index bb64e067ea..4ecf21193e 100644 --- a/tests/unit/guardrails-registry.test.ts +++ b/tests/unit/guardrails-registry.test.ts @@ -259,6 +259,89 @@ test("guardrail registry fails open when a guardrail throws", async () => { assert.equal(warnings.length, 1); }); +test("guardrail registry sanitizes native pre-call logs for a transcript-sensitive request", async () => { + const transcriptSentinel = "PRIVATE_GUARDRAIL_PRECALL_TRANSCRIPT_SENTINEL"; + class EchoingGuardrail extends BaseGuardrail { + constructor() { + super("echoing", { priority: 5 }); + } + + override async preCall( + _payload: unknown, + context: import("../../src/lib/guardrails/base.ts").GuardrailContext + ) { + context.log?.warn?.("GUARDRAIL", `native echo: ${transcriptSentinel}`, { + transcriptEcho: transcriptSentinel, + }); + return { meta: { transcriptEcho: transcriptSentinel } }; + } + } + + class ExplodingGuardrail extends BaseGuardrail { + constructor() { + super("exploding-private", { priority: 10 }); + } + + override async preCall() { + throw new Error(transcriptSentinel); + } + } + + const retainedLogs: unknown[][] = []; + const capture = (...args: unknown[]) => retainedLogs.push(args); + const registry = new GuardrailRegistry(); + registry.register(new EchoingGuardrail()); + registry.register(new ExplodingGuardrail()); + const result = await registry.runPreCallHooks( + { safe: true }, + { + log: { debug: capture, info: capture, warn: capture, error: capture }, + videoTranscriptSensitive: true, + } + ); + + assert.equal(result.blocked, false); + const retained = JSON.stringify(retainedLogs); + assert.equal(retained.includes(transcriptSentinel), false); + assert.match(retained, /omitted: video transcript/); +}); + +test("guardrail registry sanitizes native post-call logs for a transcript-sensitive request", async () => { + const transcriptSentinel = "PRIVATE_GUARDRAIL_POSTCALL_TRANSCRIPT_SENTINEL"; + class ExplodingPostGuardrail extends BaseGuardrail { + constructor() { + super("exploding-post-private", { priority: 5 }); + } + + override async postCall( + _response: unknown, + context: import("../../src/lib/guardrails/base.ts").GuardrailContext + ) { + context.log?.warn?.("GUARDRAIL", `native echo: ${transcriptSentinel}`, { + transcriptEcho: transcriptSentinel, + }); + throw new Error(transcriptSentinel); + } + } + + const retainedLogs: unknown[][] = []; + const capture = (...args: unknown[]) => retainedLogs.push(args); + const registry = new GuardrailRegistry(); + registry.register(new ExplodingPostGuardrail()); + const result = await registry.runPostCallHooks( + { choices: [] }, + { + log: { debug: capture, info: capture, warn: capture, error: capture }, + videoTranscriptSensitive: true, + } + ); + + assert.equal(result.blocked, false); + const retained = JSON.stringify(retainedLogs); + assert.equal(retained.includes(transcriptSentinel), false); + assert.match(retained, /omitted: video transcript/); +}); + test("guardrail registry never fails open after the client request aborts", async () => { class AbortedGuardrail extends BaseGuardrail { constructor() {