Files
boss/tests/runtime-leak-redaction.test.ts

124 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import test from "node:test";
import assert from "node:assert/strict";
import os from "node:os";
import path from "node:path";
import { mkdtemp, rm } from "node:fs/promises";
let runtimeRoot = "";
let readState: (typeof import("../src/lib/boss-data"))["readState"];
let writeState: (typeof import("../src/lib/boss-data"))["writeState"];
const leakedPrompt = [
"管理员全局主提示词:",
"你是 Boss 控制台的主 Agent。",
"默认只说和当前问题直接相关的判断、动作和风险。",
"",
"用户私有主提示词:",
"默认中文回复。",
"",
"当前对话附加提示词:",
"同步项目目标和版本记录后记得告诉我。",
"",
"当前消息:",
"同步完成记得要和我说,以后也是这样。",
].join("\n");
async function setup() {
if (runtimeRoot) {
return;
}
runtimeRoot = await mkdtemp(path.join(os.tmpdir(), "boss-runtime-leak-redaction-"));
process.env.BOSS_RUNTIME_ROOT = runtimeRoot;
process.env.BOSS_STATE_FILE = path.join(runtimeRoot, "boss-state.json");
const data = await import("../src/lib/boss-data.ts");
readState = data.readState;
writeState = data.writeState;
}
test.after(async () => {
if (runtimeRoot) {
await rm(runtimeRoot, { recursive: true, force: true });
}
});
test("读取已有状态时会清洗历史提示词泄漏内容", async () => {
await setup();
const state = await readState();
const masterProject = state.projects.find((project) => project.id === "master-agent");
assert.ok(masterProject, "expected a master-agent project");
masterProject.messages.push({
id: "msg-leak-1",
sender: "ops",
senderLabel: "主 Agent Relay",
body: `Master Codex Node 执行失败:\n${leakedPrompt}`,
sentAt: "2026-04-19T08:35:01.079Z",
kind: "text",
});
masterProject.preview = leakedPrompt;
state.masterAgentTasks.unshift({
taskId: "task-leak-1",
projectId: "master-agent",
taskType: "conversation_reply",
requestMessageId: "msg-user-1",
requestText: "同步完成记得要和我说,以后也是这样。",
executionPrompt: leakedPrompt,
requestedBy: "Boss 超级管理员",
requestedByAccount: "krisolo",
deviceId: "mac-studio",
status: "failed",
requestedAt: "2026-04-19T08:35:01.079Z",
errorMessage: leakedPrompt,
});
state.appLogs.unshift({
logId: "log-leak-1",
deviceId: "mac-studio",
projectId: "master-agent",
level: "error",
source: "local_agent",
category: "local_agent.master_agent_task_failed",
message: "Master Codex Node 执行主 Agent 任务失败task-leak-1",
detail: leakedPrompt,
mirroredToProject: true,
createdAt: "2026-04-19T08:35:01.079Z",
});
await writeState(state);
const nextState = await readState();
const nextMasterProject = nextState.projects.find((project) => project.id === "master-agent");
assert.ok(nextMasterProject, "expected a reloaded master-agent project");
const leakedMessage = nextMasterProject.messages.find((message) => message.id === "msg-leak-1");
assert.ok(leakedMessage, "expected the historical message to remain");
assert.equal(
/管理员全局主提示词:|用户私有主提示词:|当前对话附加提示词:/.test(leakedMessage.body),
false,
);
assert.match(leakedMessage.body, /已拦截内部执行日志|原始内容已隐藏/);
assert.equal(
/管理员全局主提示词:|用户私有主提示词:|当前对话附加提示词:/.test(
nextMasterProject.preview ?? "",
),
false,
);
const sanitizedTask = nextState.masterAgentTasks.find((task) => task.taskId === "task-leak-1");
assert.equal(sanitizedTask?.errorMessage, "MASTER_CODEX_NODE_OUTPUT_LEAKED");
const sanitizedLog = nextState.appLogs.find((log) => log.logId === "log-leak-1");
assert.ok(sanitizedLog, "expected the historical app log to remain");
assert.equal(
/管理员全局主提示词:|用户私有主提示词:|当前对话附加提示词:/.test(
sanitizedLog?.detail ?? "",
),
false,
);
assert.match(sanitizedLog?.detail ?? "", /已拦截内部执行日志|原始内容不再展示/);
});