fix(compaction): stop retaining credential-like values (#67801)

This commit is contained in:
Devin Robison
2026-04-16 14:04:45 -06:00
committed by GitHub
parent 894e728fd0
commit 8b7d76bfbb
5 changed files with 25 additions and 6 deletions

View File

@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
import { createAnthropicPayloadLogger } from "./anthropic-payload-log.js";
describe("createAnthropicPayloadLogger", () => {
it("redacts image base64 payload data before writing logs", async () => {
it("sanitizes credential fields and image base64 payload data before writing logs", async () => {
const lines: string[] = [];
const logger = createAnthropicPayloadLogger({
env: { OPENCLAW_ANTHROPIC_PAYLOAD_LOG: "1" },
@@ -19,6 +19,7 @@ describe("createAnthropicPayloadLogger", () => {
messages: [
{
role: "user",
authorization: "Bearer sk-secret", // pragma: allowlist secret
content: [
{
type: "image",
@@ -27,6 +28,11 @@ describe("createAnthropicPayloadLogger", () => {
],
},
],
metadata: {
api_key: "sk-test", // pragma: allowlist secret
nestedToken: "shh", // pragma: allowlist secret
tokenBudget: 1024,
},
};
const streamFn: StreamFn = ((model, __, options) => {
options?.onPayload?.(payload, model);
@@ -37,10 +43,17 @@ describe("createAnthropicPayloadLogger", () => {
await wrapped?.({ api: "anthropic-messages" } as never, { messages: [] } as never, {});
const event = JSON.parse(lines[0]?.trim() ?? "{}") as Record<string, unknown>;
const message = ((event.payload as { messages?: unknown[] } | undefined)?.messages ??
[]) as Array<Record<string, unknown>>;
const sanitizedPayload = (event.payload ?? {}) as Record<string, unknown>;
const message = ((sanitizedPayload.messages as unknown[] | undefined) ?? []) as Array<
Record<string, unknown>
>;
const source = (((message[0]?.content as Array<Record<string, unknown>> | undefined) ?? [])[0]
?.source ?? {}) as Record<string, unknown>;
const metadata = (sanitizedPayload.metadata ?? {}) as Record<string, unknown>;
expect(message[0]).not.toHaveProperty("authorization");
expect(metadata).not.toHaveProperty("api_key");
expect(metadata).not.toHaveProperty("nestedToken");
expect(metadata.tokenBudget).toBe(1024);
expect(source.data).toBe("<redacted>");
expect(source.bytes).toBe(4);
expect(source.sha256).toBe(crypto.createHash("sha256").update("QUJDRA==").digest("hex"));

View File

@@ -7,7 +7,7 @@ import { createSubsystemLogger } from "../logging/subsystem.js";
import { resolveUserPath } from "../utils.js";
import { parseBooleanValue } from "../utils/boolean.js";
import { safeJsonStringify } from "../utils/safe-json.js";
import { redactImageDataForDiagnostics } from "./payload-redaction.js";
import { sanitizeDiagnosticPayload } from "./payload-redaction.js";
import { getQueuedFileWriter, type QueuedFileWriter } from "./queued-file-writer.js";
type PayloadLogStage = "request" | "usage";
@@ -137,7 +137,7 @@ export function createAnthropicPayloadLogger(params: {
return streamFn(model, context, options);
}
const nextOnPayload = (payload: unknown) => {
const redactedPayload = redactImageDataForDiagnostics(payload);
const redactedPayload = sanitizeDiagnosticPayload(payload);
record({
...base,
ts: new Date().toISOString(),

View File

@@ -6,6 +6,8 @@ describe("compaction identifier policy", () => {
const built = buildCompactionSummarizationInstructions();
expect(built).toContain("Preserve all opaque identifiers exactly as written");
expect(built).toContain("UUIDs");
expect(built).not.toContain("tokens");
expect(built).not.toContain("API keys");
});
it("can disable identifier preservation with off policy", () => {

View File

@@ -71,6 +71,8 @@ describe("compaction identifier-preservation instructions", () => {
expect(firstSummaryInstructions()).toContain("UUIDs");
expect(firstSummaryInstructions()).toContain("IPs");
expect(firstSummaryInstructions()).toContain("ports");
expect(firstSummaryInstructions()).not.toContain("tokens");
expect(firstSummaryInstructions()).not.toContain("API keys");
});
it("keeps identifier-preservation guidance when custom instructions are provided", async () => {
@@ -139,6 +141,8 @@ describe("buildCompactionSummarizationInstructions", () => {
const result = buildCompactionSummarizationInstructions();
expect(result).toContain("Preserve all opaque identifiers exactly as written");
expect(result).not.toContain("Additional focus:");
expect(result).not.toContain("tokens");
expect(result).not.toContain("API keys");
});
it("appends custom instructions in a stable format", () => {

View File

@@ -37,7 +37,7 @@ const MERGE_SUMMARIES_INSTRUCTIONS = [
].join("\n");
const IDENTIFIER_PRESERVATION_INSTRUCTIONS =
"Preserve all opaque identifiers exactly as written (no shortening or reconstruction), " +
"including UUIDs, hashes, IDs, tokens, API keys, hostnames, IPs, ports, URLs, and file names.";
"including UUIDs, hashes, IDs, hostnames, IPs, ports, URLs, and file names.";
export type CompactionSummarizationInstructions = {
identifierPolicy?: AgentCompactionIdentifierPolicy;