Redact common credential patterns from published run output (#1595)

* Redact common credential patterns from published run output

* Handle color codes and escape sequences ahead of redacted values

Vendor-prefixed formats no longer require a leading word boundary, so a
value that follows an ANSI SGR terminator or a serialized JSON escape is
still matched. AWS key ids keep a boundary but also accept those cases.
sanitizeContent goes back to GitHub-only redaction for inbound content,
and the failure annotation is redacted like the tracking comment.

* Coerce non-string text content before redacting tool results

No-Verification-Needed: one-line coercion in a formatting helper plus regression test
This commit is contained in:
Ashwin Bhat
2026-08-06 10:17:25 -07:00
committed by GitHub
parent c038e4dcde
commit 0aee57ab82
6 changed files with 296 additions and 20 deletions
+112
View File
@@ -467,6 +467,15 @@ describe("formatResultContent non-string input", () => {
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
test("handles a text content block whose text field is not a string", () => {
expect(() =>
formatResultContent('[{"type":"text","text":{"foo":"bar"}}]'),
).not.toThrow();
expect(formatResultContent('[{"type":"text","text":123}]')).toContain(
"123",
);
});
});
describe("system_other handling", () => {
@@ -515,3 +524,106 @@ describe("system_other handling", () => {
expect(result).toContain("## 🚀 System Initialization");
});
});
describe("credential redaction", () => {
test("redacts credentials embedded in tool results", () => {
const data: Turn[] = [
{
type: "assistant",
message: {
content: [
{
type: "tool_use",
id: "toolu_1",
name: "Bash",
input: { command: "cat .env" },
},
],
},
},
{
type: "user",
message: {
content: [
{
type: "tool_result",
tool_use_id: "toolu_1",
content:
"GITHUB_TOKEN=ghs_xz7yzju2SZjGPa0dUNMAx0SH4xDOCS31LXQW\nAWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE",
},
],
},
},
];
const result = formatTurnsFromData(data);
expect(result).toContain("[REDACTED_GITHUB_TOKEN]");
expect(result).toContain("[REDACTED_AWS_KEY_ID]");
expect(result).not.toContain("ghs_xz7yzju2SZjGPa0dUNMAx0SH4xDOCS31LXQW");
expect(result).not.toContain("AKIAIOSFODNN7EXAMPLE");
});
test("redacts credentials embedded in multi-line tool inputs", () => {
const data: Turn[] = [
{
type: "assistant",
message: {
content: [
{
type: "tool_use",
id: "toolu_2",
name: "Write",
input: {
file_path: ".env",
content:
"AWS_ACCESS_KEY_ID=x\nGITHUB_TOKEN=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\n",
},
},
],
},
},
];
const result = formatTurnsFromData(data);
expect(result).toContain("[REDACTED_GITHUB_TOKEN]");
expect(result).not.toContain("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");
});
test("redacts credentials wrapped in ANSI color codes", () => {
const key = "sk-ant-api03-AbCdEfGhIjKlMnOpQrStUvWxYz0123456789_-abcdefgh";
const data: Turn[] = [
{
type: "assistant",
message: {
content: [
{
type: "tool_use",
id: "toolu_3",
name: "Bash",
input: { command: "node print-config.js" },
},
],
},
},
{
type: "user",
message: {
content: [
{
type: "tool_result",
tool_use_id: "toolu_3",
content: `apiKey: \x1b[32m${key}\x1b[39m\nregion: us-east-1`,
},
],
},
},
];
const result = formatTurnsFromData(data);
expect(result).toContain("[REDACTED_ANTHROPIC_KEY]");
expect(result).not.toContain(key);
});
});