Files
diegosouzapw--omniroute/tests/unit/vscode-context-sanitizer.test.ts
2026-07-13 13:39:12 +08:00

157 lines
5.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const { sanitizeVscodeRequestBody, sanitizeVscodeRequest } =
await import("../../src/app/api/v1/vscode/contextSanitizer.ts");
delete process.env.OMNIROUTE_VSCODE_SANITIZE_CONTEXT;
type FileAttachment = {
filePath: string;
content: string;
};
type TestMessage = {
role: string;
content: string;
};
type TestPayload = {
attachments?: FileAttachment[];
editorContext?: FileAttachment;
messages?: TestMessage[];
};
test("vscode sanitizer preserves explicit attachments and strips implicit editor context", () => {
const result = sanitizeVscodeRequestBody({
messages: [
{
role: "user",
content: "Review the attached Dockerfile.hermes.",
},
],
attachments: [
{
filePath: "/repo/Dockerfile.hermes",
content: "FROM nousresearch/hermes-agent:latest",
},
],
editorContext: {
filePath: "/repo/hermes-bootstrap.sh",
content: "#!/usr/bin/env bash\necho should-not-forward",
},
});
assert.equal(result.changed, true);
const sanitizedBody = result.body as TestPayload;
assert.equal(sanitizedBody.attachments?.[0].filePath, "/repo/Dockerfile.hermes");
assert.equal(sanitizedBody.attachments?.[0].content.includes("nousresearch"), true);
assert.equal("editorContext" in sanitizedBody, false);
assert.deepEqual(result.audit.removedImplicitKeys, ["editorContext"]);
});
test("vscode sanitizer redacts sensitive explicit file contents", () => {
const result = sanitizeVscodeRequestBody({
attachments: [
{
filePath: "/repo/local-docs/server-access.md",
content: "ssh password and production host details",
},
],
});
assert.equal(result.changed, true);
const sanitizedBody = result.body as TestPayload;
assert.equal(sanitizedBody.attachments?.[0].filePath, "/repo/local-docs/server-access.md");
assert.equal(sanitizedBody.attachments?.[0].content, "[REDACTED SENSITIVE CONTEXT]");
assert.deepEqual(result.audit.redactedSensitivePaths, ["/repo/local-docs/server-access.md"]);
});
test("vscode sanitizer removes implicit editor text blocks unless they reference explicit files", () => {
const result = sanitizeVscodeRequestBody({
attachments: [
{
filePath: "/repo/Dockerfile.hermes",
content: "FROM alpine",
},
],
messages: [
{
role: "user",
content:
"Current file:\n/repo/hermes-bootstrap.sh\nsecret bootstrap content\n\nUser request:\nReview the Dockerfile.hermes",
},
],
});
assert.equal(result.changed, true);
const sanitizedBody = result.body as TestPayload;
assert.equal(sanitizedBody.messages?.[0].content.includes("hermes-bootstrap.sh"), false);
assert.equal(sanitizedBody.messages?.[0].content.includes("User request:"), true);
});
test("vscode sanitizer leaves ordinary chat payloads unchanged", () => {
const payload = {
model: "gpt-5.5-medium",
messages: [{ role: "user", content: "Explain Dockerfile best practices." }],
stream: true,
};
const result = sanitizeVscodeRequestBody(payload);
assert.equal(result.changed, false);
assert.equal(result.body, payload);
assert.deepEqual(result.audit.removedImplicitKeys, []);
assert.deepEqual(result.audit.redactedSensitivePaths, []);
});
test("vscode request sanitizer rewrites JSON body and keeps auth headers", async () => {
const request = new Request("http://localhost/api/v1/vscode/token/chat/completions", {
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": "test-key",
},
body: JSON.stringify({
messages: [{ role: "user", content: "Analyze attachment" }],
editorContext: { filePath: "/repo/open-file.ts", content: "not explicit" },
}),
});
const sanitizedRequest = await sanitizeVscodeRequest(request);
const body = (await sanitizedRequest.json()) as TestPayload;
assert.equal(sanitizedRequest.headers.get("x-api-key"), "test-key");
assert.equal("editorContext" in body, false);
});
test("vscode sanitizer preserves ordinary prose mentioning credentials/secrets/kubeconfig", () => {
// Regression: the sensitive-path keyword patterns (credentials/secrets/kubeconfig)
// must NOT redact free-form chat text. They only flag sensitive *file paths* at the
// object level — applying them to prose corrupted legitimate coding prompts.
const prose =
"How do I store API credentials and secrets in production, and edit the kubeconfig?";
const payload = {
messages: [{ role: "user", content: prose }],
};
const result = sanitizeVscodeRequestBody(payload);
assert.equal(result.changed, false);
const sanitizedBody = result.body as TestPayload;
assert.equal(sanitizedBody.messages?.[0].content, prose);
assert.equal(sanitizedBody.messages?.[0].content.includes("[REDACTED"), false);
assert.deepEqual(result.audit.redactedSensitivePaths, []);
});
test("vscode sanitizer still redacts sensitive file paths referenced in object content", () => {
// The object-level path+content redaction must keep working after the text-scan
// narrowing — a structured attachment whose path matches a keyword pattern is redacted.
const result = sanitizeVscodeRequestBody({
attachments: [{ filePath: "/home/user/.aws/credentials", content: "aws_secret_access_key=..." }],
});
assert.equal(result.changed, true);
const sanitizedBody = result.body as TestPayload;
assert.equal(sanitizedBody.attachments?.[0].content, "[REDACTED SENSITIVE CONTEXT]");
assert.deepEqual(result.audit.redactedSensitivePaths, ["/home/user/.aws/credentials"]);
});