disclaw/tests/unit/sanitize-for-discord.test.ts
Nick Tabeling 714e0c01e5
Some checks failed
CI / build-and-test (ubuntu-latest) (pull_request) Has been cancelled
CI / build-and-test (windows-latest) (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
fix(DIS-102): sanitize response before DB storage to break path feedback loop
The root cause was twofold:
1. Raw (unsanitized) agent responses were stored in the conversation
   history DB. On subsequent calls, these paths were fed back to the
   agent as context, causing it to reproduce full paths regardless of
   Discord-side sanitization.
2. repoRoot (workspace_path) was not passed to sanitizeForDiscord,
   so workspace-specific paths were only partially masked via the
   home directory fallback.

Fix: sanitize the full response once before both sending to Discord
and storing in the DB. Pass workspace_path as repoRoot for precise
path replacement. Add Windows path tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 10:02:17 +02:00

87 lines
3.7 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");
});
// Windows-style backslash paths are sanitized
it("replaces Windows backslash paths", () => {
const text = "File at C:\\Users\\dev\\.disclaw\\workspaces\\agent-x\\src\\main.ts";
const result = sanitizeForDiscord(text, {
repoRoot: "C:\\Users\\dev\\.disclaw\\workspaces\\agent-x",
home: "C:\\Users\\dev",
});
expect(result).toBe("File at <disclaw>\\src\\main.ts");
});
// Mixed slash styles (common on Windows when tools output forward slashes)
it("replaces forward-slash variants of Windows paths", () => {
const text = "File at C:/Users/dev/.disclaw/workspaces/agent-x/src/main.ts";
const result = sanitizeForDiscord(text, {
repoRoot: "C:\\Users\\dev\\.disclaw\\workspaces\\agent-x",
home: "C:\\Users\\dev",
});
expect(result).toBe("File at <disclaw>/src/main.ts");
});
});