124 lines
4.1 KiB
TypeScript
124 lines
4.1 KiB
TypeScript
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 ?? "", /已拦截内部执行日志|原始内容不再展示/);
|
||
});
|