Add sanitizeForDiscord() in src/runtime/sanitize.ts. Router applies sanitizer to all channel.send() calls. Prevents path/token leaks into Discord channels. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.8 KiB
TypeScript
67 lines
2.8 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { sanitizeForDiscord } from "../../src/runtime/sanitize";
|
|
|
|
describe("sanitizeForDiscord", () => {
|
|
// (a) Repo-root path is replaced with <disclaw>
|
|
it("replaces repoRoot with <disclaw>", () => {
|
|
const text = "Reading config from /home/user/project/disclaw.yaml";
|
|
const result = sanitizeForDiscord(text, { repoRoot: "/home/user/project" });
|
|
expect(result).toBe("Reading config from <disclaw>/disclaw.yaml");
|
|
expect(result).not.toContain("/home/user/project");
|
|
});
|
|
|
|
// (b) Home directory is replaced with ~
|
|
it("replaces home directory with ~", () => {
|
|
const text = "Workspace located at /home/user/.disclaw/workspaces/agent-x";
|
|
const result = sanitizeForDiscord(text, { home: "/home/user" });
|
|
expect(result).toBe("Workspace located at ~/.disclaw/workspaces/agent-x");
|
|
expect(result).not.toContain("/home/user");
|
|
});
|
|
|
|
// (c) Discord bot token pattern is redacted
|
|
it("redacts Discord bot tokens", () => {
|
|
const token = "MTA1NjYwNDczNTA3NDU2NjE2.GbXkRa.abcdefghijklmnopqrstuvwxyz123";
|
|
const text = `Bot initialized with token ${token} — ready.`;
|
|
const result = sanitizeForDiscord(text);
|
|
expect(result).not.toContain(token);
|
|
expect(result).toContain("<redacted>");
|
|
});
|
|
|
|
// (d) Anthropic API key is redacted
|
|
it("redacts Anthropic API keys", () => {
|
|
const key = "sk-ant-api03-ABCDEFGHIJ1234567890abcdefghij";
|
|
const text = `Using key ${key} for requests.`;
|
|
const result = sanitizeForDiscord(text);
|
|
expect(result).not.toContain(key);
|
|
expect(result).toContain("<redacted>");
|
|
});
|
|
|
|
// (e) Harmless text is not modified (no false positives)
|
|
it("does not modify harmless text", () => {
|
|
const text = "Hello! The agent processed your request successfully in 120ms.";
|
|
const result = sanitizeForDiscord(text, { home: "/home/user", repoRoot: "/home/user/project" });
|
|
expect(result).toBe(text);
|
|
});
|
|
|
|
// (f) Empty string returns empty string
|
|
it("handles empty string input", () => {
|
|
expect(sanitizeForDiscord("")).toBe("");
|
|
expect(sanitizeForDiscord("", { home: "/home/user", repoRoot: "/home/user/project" })).toBe("");
|
|
});
|
|
|
|
// (g) No opts provided — plain text stays unchanged when no token patterns present
|
|
it("returns text unchanged when no opts and no token patterns", () => {
|
|
const text = "This is a perfectly normal response from the agent.";
|
|
expect(sanitizeForDiscord(text)).toBe(text);
|
|
});
|
|
|
|
// Ordering: repoRoot (sub-path of home) is replaced before home
|
|
it("replaces repoRoot before home so nested paths are handled correctly", () => {
|
|
const text = "/home/user/project/src/index.ts and /home/user/other.ts";
|
|
const result = sanitizeForDiscord(text, {
|
|
repoRoot: "/home/user/project",
|
|
home: "/home/user",
|
|
});
|
|
expect(result).toBe("<disclaw>/src/index.ts and ~/other.ts");
|
|
});
|
|
});
|