mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
perf(test): merge chat and system run cases
This commit is contained in:
@@ -500,23 +500,21 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
};
|
||||
}
|
||||
|
||||
it("uses local execution by default when mac app exec host preference is disabled", async () => {
|
||||
const { runCommand, runViaMacAppExecHost, sendInvokeResult } = await runSystemInvoke({
|
||||
it("routes local, mac host, and canonical shell-wrapper requests", async () => {
|
||||
const localInvoke = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
});
|
||||
|
||||
expect(runViaMacAppExecHost).not.toHaveBeenCalled();
|
||||
expect(runCommand).toHaveBeenCalledTimes(1);
|
||||
expectInvokeOk(sendInvokeResult, { payloadContains: "local-ok" });
|
||||
});
|
||||
expect(localInvoke.runViaMacAppExecHost).not.toHaveBeenCalled();
|
||||
expect(localInvoke.runCommand).toHaveBeenCalledTimes(1);
|
||||
expectInvokeOk(localInvoke.sendInvokeResult, { payloadContains: "local-ok" });
|
||||
|
||||
it("uses mac app exec host when explicitly preferred", async () => {
|
||||
const { runCommand, runViaMacAppExecHost, sendInvokeResult } = await runSystemInvoke({
|
||||
const macHostInvoke = await runSystemInvoke({
|
||||
preferMacAppExecHost: true,
|
||||
runViaResponse: createMacExecHostSuccess(),
|
||||
});
|
||||
|
||||
expect(runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
expect(macHostInvoke.runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
approvals: expect.objectContaining({
|
||||
agent: expect.objectContaining({
|
||||
security: "full",
|
||||
@@ -527,18 +525,16 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
command: ["echo", "ok"],
|
||||
}),
|
||||
});
|
||||
expect(runCommand).not.toHaveBeenCalled();
|
||||
expectInvokeOk(sendInvokeResult, { payloadContains: "app-ok" });
|
||||
});
|
||||
expect(macHostInvoke.runCommand).not.toHaveBeenCalled();
|
||||
expectInvokeOk(macHostInvoke.sendInvokeResult, { payloadContains: "app-ok" });
|
||||
|
||||
it("forwards canonical command text to mac app exec host for positional-argv shell wrappers", async () => {
|
||||
const { runViaMacAppExecHost } = await runSystemInvoke({
|
||||
const shellWrapperInvoke = await runSystemInvoke({
|
||||
preferMacAppExecHost: true,
|
||||
command: ["/bin/sh", "-lc", '$0 "$1"', "/usr/bin/touch", "/tmp/marker"],
|
||||
runViaResponse: createMacExecHostSuccess(),
|
||||
});
|
||||
|
||||
expect(runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
expect(shellWrapperInvoke.runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
approvals: expect.anything(),
|
||||
request: expect.objectContaining({
|
||||
command: ["/bin/sh", "-lc", '$0 "$1"', "/usr/bin/touch", "/tmp/marker"],
|
||||
@@ -558,69 +554,72 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
},
|
||||
] as const;
|
||||
|
||||
for (const testCase of approvedEnvShellWrapperCases) {
|
||||
it.runIf(process.platform !== "win32")(testCase.name, async () => {
|
||||
const tmp = createFixtureDir("openclaw-approved-wrapper-");
|
||||
const marker = path.join(tmp, "marker");
|
||||
const attackerScript = path.join(tmp, "sh");
|
||||
fs.writeFileSync(attackerScript, "#!/bin/sh\necho exploited > marker\n");
|
||||
fs.chmodSync(attackerScript, 0o755);
|
||||
const runCommand = vi.fn(async (argv: string[]) => {
|
||||
if (argv[0] === "/bin/sh" && argv[1] === "sh" && argv[2] === "-c") {
|
||||
fs.writeFileSync(marker, "rewritten");
|
||||
}
|
||||
return createLocalRunResult();
|
||||
});
|
||||
const sendInvokeResult = vi.fn(async () => {});
|
||||
try {
|
||||
const invoke = await runSystemInvoke({
|
||||
preferMacAppExecHost: testCase.preferMacAppExecHost,
|
||||
command: ["env", "sh", "-c", "echo SAFE"],
|
||||
cwd: tmp,
|
||||
approved: true,
|
||||
security: "allowlist",
|
||||
ask: "on-miss",
|
||||
runCommand,
|
||||
sendInvokeResult,
|
||||
runViaResponse: testCase.preferMacAppExecHost
|
||||
? {
|
||||
ok: true,
|
||||
payload: {
|
||||
success: true,
|
||||
stdout: "app-ok",
|
||||
stderr: "",
|
||||
timedOut: false,
|
||||
exitCode: 0,
|
||||
error: null,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"preserves wrapper argv for approved env shell commands",
|
||||
async () => {
|
||||
for (const testCase of approvedEnvShellWrapperCases) {
|
||||
const tmp = createFixtureDir("openclaw-approved-wrapper-");
|
||||
const marker = path.join(tmp, "marker");
|
||||
const attackerScript = path.join(tmp, "sh");
|
||||
fs.writeFileSync(attackerScript, "#!/bin/sh\necho exploited > marker\n");
|
||||
fs.chmodSync(attackerScript, 0o755);
|
||||
const runCommand = vi.fn(async (argv: string[]) => {
|
||||
if (argv[0] === "/bin/sh" && argv[1] === "sh" && argv[2] === "-c") {
|
||||
fs.writeFileSync(marker, "rewritten");
|
||||
}
|
||||
return createLocalRunResult();
|
||||
});
|
||||
|
||||
if (testCase.preferMacAppExecHost) {
|
||||
const canonicalCwd = fs.realpathSync(tmp);
|
||||
expect(invoke.runCommand).not.toHaveBeenCalled();
|
||||
expect(invoke.runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
approvals: expect.anything(),
|
||||
request: expect.objectContaining({
|
||||
command: ["env", "sh", "-c", "echo SAFE"],
|
||||
rawCommand: 'env sh -c "echo SAFE"',
|
||||
cwd: canonicalCwd,
|
||||
}),
|
||||
const sendInvokeResult = vi.fn(async () => {});
|
||||
try {
|
||||
const invoke = await runSystemInvoke({
|
||||
preferMacAppExecHost: testCase.preferMacAppExecHost,
|
||||
command: ["env", "sh", "-c", "echo SAFE"],
|
||||
cwd: tmp,
|
||||
approved: true,
|
||||
security: "allowlist",
|
||||
ask: "on-miss",
|
||||
runCommand,
|
||||
sendInvokeResult,
|
||||
runViaResponse: testCase.preferMacAppExecHost
|
||||
? {
|
||||
ok: true,
|
||||
payload: {
|
||||
success: true,
|
||||
stdout: "app-ok",
|
||||
stderr: "",
|
||||
timedOut: false,
|
||||
exitCode: 0,
|
||||
error: null,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
expectInvokeOk(invoke.sendInvokeResult, { payloadContains: "app-ok" });
|
||||
return;
|
||||
}
|
||||
|
||||
const runArgs = vi.mocked(invoke.runCommand).mock.calls[0]?.[0] as string[] | undefined;
|
||||
expect(runArgs).toEqual(["env", "sh", "-c", "echo SAFE"]);
|
||||
expect(fs.existsSync(marker)).toBe(false);
|
||||
expectInvokeOk(invoke.sendInvokeResult);
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
if (testCase.preferMacAppExecHost) {
|
||||
const canonicalCwd = fs.realpathSync(tmp);
|
||||
expect(invoke.runCommand).not.toHaveBeenCalled();
|
||||
expect(invoke.runViaMacAppExecHost).toHaveBeenCalledWith({
|
||||
approvals: expect.anything(),
|
||||
request: expect.objectContaining({
|
||||
command: ["env", "sh", "-c", "echo SAFE"],
|
||||
rawCommand: 'env sh -c "echo SAFE"',
|
||||
cwd: canonicalCwd,
|
||||
}),
|
||||
});
|
||||
expectInvokeOk(invoke.sendInvokeResult, { payloadContains: "app-ok" });
|
||||
continue;
|
||||
}
|
||||
|
||||
const runArgs = vi.mocked(invoke.runCommand).mock.calls[0]?.[0] as string[] | undefined;
|
||||
expect(runArgs).toEqual(["env", "sh", "-c", "echo SAFE"]);
|
||||
expect(fs.existsSync(marker)).toBe(false);
|
||||
expectInvokeOk(invoke.sendInvokeResult);
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("handles transparent env wrappers in allowlist mode", async () => {
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
@@ -652,59 +651,68 @@ describe("handleSystemRunInvoke mac app exec host routing", () => {
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"pins PATH-token executable to canonical path for approval-based runs",
|
||||
"pins PATH-token executable to canonical path",
|
||||
async () => {
|
||||
await withPathTokenCommand({
|
||||
tmpPrefix: "openclaw-approval-path-pin-",
|
||||
run: async ({ expected }) => {
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
command: ["poccmd", "-n", "SAFE"],
|
||||
approved: true,
|
||||
security: "full",
|
||||
ask: "off",
|
||||
});
|
||||
expectCommandPinnedToCanonicalPath({
|
||||
runCommand,
|
||||
expected,
|
||||
commandTail: ["-n", "SAFE"],
|
||||
});
|
||||
expectInvokeOk(sendInvokeResult);
|
||||
for (const testCase of [
|
||||
{
|
||||
name: "approval-based run",
|
||||
tmpPrefix: "openclaw-approval-path-pin-",
|
||||
run: async (ctx: { expected: string }) => {
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
command: ["poccmd", "-n", "SAFE"],
|
||||
approved: true,
|
||||
security: "full",
|
||||
ask: "off",
|
||||
});
|
||||
expectCommandPinnedToCanonicalPath({
|
||||
runCommand,
|
||||
expected: ctx.expected,
|
||||
commandTail: ["-n", "SAFE"],
|
||||
});
|
||||
expectInvokeOk(sendInvokeResult);
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
{
|
||||
name: "prepared plan",
|
||||
tmpPrefix: "openclaw-prepare-run-path-pin-",
|
||||
run: async (ctx: { expected: string }) => {
|
||||
const prepared = buildSystemRunApprovalPlan({
|
||||
command: ["poccmd", "hello"],
|
||||
});
|
||||
expect(prepared.ok).toBe(true);
|
||||
if (!prepared.ok) {
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
"accepts prepared plans after PATH-token hardening rewrites argv",
|
||||
async () => {
|
||||
await withPathTokenCommand({
|
||||
tmpPrefix: "openclaw-prepare-run-path-pin-",
|
||||
run: async ({ expected }) => {
|
||||
const prepared = buildSystemRunApprovalPlan({
|
||||
command: ["poccmd", "hello"],
|
||||
});
|
||||
expect(prepared.ok).toBe(true);
|
||||
if (!prepared.ok) {
|
||||
throw new Error("unreachable");
|
||||
}
|
||||
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
command: prepared.plan.argv,
|
||||
rawCommand: prepared.plan.commandText,
|
||||
approved: true,
|
||||
security: "full",
|
||||
ask: "off",
|
||||
});
|
||||
expectCommandPinnedToCanonicalPath({
|
||||
runCommand,
|
||||
expected,
|
||||
commandTail: ["hello"],
|
||||
});
|
||||
expectInvokeOk(sendInvokeResult);
|
||||
const { runCommand, sendInvokeResult } = await runSystemInvoke({
|
||||
preferMacAppExecHost: false,
|
||||
command: prepared.plan.argv,
|
||||
rawCommand: prepared.plan.commandText,
|
||||
approved: true,
|
||||
security: "full",
|
||||
ask: "off",
|
||||
});
|
||||
expectCommandPinnedToCanonicalPath({
|
||||
runCommand,
|
||||
expected: ctx.expected,
|
||||
commandTail: ["hello"],
|
||||
});
|
||||
expectInvokeOk(sendInvokeResult);
|
||||
},
|
||||
},
|
||||
});
|
||||
] as const) {
|
||||
await withPathTokenCommand({
|
||||
tmpPrefix: testCase.tmpPrefix,
|
||||
run: async ({ expected }) => {
|
||||
try {
|
||||
await testCase.run({ expected });
|
||||
} catch (error) {
|
||||
throw new Error(`case failed: ${testCase.name}`, { cause: error });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -919,140 +919,81 @@ describe("chat view", () => {
|
||||
expect(container.textContent).not.toContain("MEDIA:https://example.com/photo.png");
|
||||
});
|
||||
|
||||
it("keeps user transcript images visible after history reload", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderGroupedMessage(
|
||||
container,
|
||||
{
|
||||
id: "user-history-image",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.png",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
"user",
|
||||
{
|
||||
it("renders allowed transcript images and skips blocked/non-image media", () => {
|
||||
const renderUserMedia = (message: unknown) => {
|
||||
const container = document.createElement("div");
|
||||
renderGroupedMessage(container, message, "user", {
|
||||
showToolCalls: false,
|
||||
basePath: "/openclaw",
|
||||
assistantAttachmentAuthToken: "session-token",
|
||||
localMediaPreviewRoots: ["/tmp/openclaw"],
|
||||
},
|
||||
);
|
||||
});
|
||||
return container;
|
||||
};
|
||||
|
||||
const image = container.querySelector<HTMLImageElement>(".chat-message-image");
|
||||
expect(image?.getAttribute("src")).toBe(
|
||||
let container = renderUserMedia({
|
||||
id: "user-history-image",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.png",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
expect(
|
||||
container.querySelector<HTMLImageElement>(".chat-message-image")?.getAttribute("src"),
|
||||
).toBe(
|
||||
"/openclaw/__openclaw__/assistant-media?source=%2Ftmp%2Fopenclaw%2Fuser-upload.png&token=session-token",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps transcript images visible when MIME falls back to application/octet-stream", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderGroupedMessage(
|
||||
container,
|
||||
{
|
||||
id: "user-history-image-octet-stream",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.png",
|
||||
MediaType: "application/octet-stream",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
"user",
|
||||
{
|
||||
showToolCalls: false,
|
||||
basePath: "/openclaw",
|
||||
assistantAttachmentAuthToken: "session-token",
|
||||
localMediaPreviewRoots: ["/tmp/openclaw"],
|
||||
},
|
||||
);
|
||||
|
||||
const image = container.querySelector<HTMLImageElement>(".chat-message-image");
|
||||
expect(image?.getAttribute("src")).toBe(
|
||||
container = renderUserMedia({
|
||||
id: "user-history-image-octet-stream",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.png",
|
||||
MediaType: "application/octet-stream",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
expect(
|
||||
container.querySelector<HTMLImageElement>(".chat-message-image")?.getAttribute("src"),
|
||||
).toBe(
|
||||
"/openclaw/__openclaw__/assistant-media?source=%2Ftmp%2Fopenclaw%2Fuser-upload.png&token=session-token",
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps plural user transcript images visible after history reload", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderGroupedMessage(
|
||||
container,
|
||||
{
|
||||
id: "user-history-images",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPaths: ["/tmp/openclaw/first.png", "/tmp/openclaw/second.jpg"],
|
||||
MediaTypes: ["image/png", "application/octet-stream"],
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
"user",
|
||||
{
|
||||
showToolCalls: false,
|
||||
basePath: "/openclaw",
|
||||
assistantAttachmentAuthToken: "session-token",
|
||||
localMediaPreviewRoots: ["/tmp/openclaw"],
|
||||
},
|
||||
);
|
||||
|
||||
const imageSources = [
|
||||
...container.querySelectorAll<HTMLImageElement>(".chat-message-image"),
|
||||
].map((image) => image.getAttribute("src"));
|
||||
expect(imageSources).toEqual([
|
||||
container = renderUserMedia({
|
||||
id: "user-history-images",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPaths: ["/tmp/openclaw/first.png", "/tmp/openclaw/second.jpg"],
|
||||
MediaTypes: ["image/png", "application/octet-stream"],
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
expect(
|
||||
[...container.querySelectorAll<HTMLImageElement>(".chat-message-image")].map((image) =>
|
||||
image.getAttribute("src"),
|
||||
),
|
||||
).toEqual([
|
||||
"/openclaw/__openclaw__/assistant-media?source=%2Ftmp%2Fopenclaw%2Ffirst.png&token=session-token",
|
||||
"/openclaw/__openclaw__/assistant-media?source=%2Ftmp%2Fopenclaw%2Fsecond.jpg&token=session-token",
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not render blocked local transcript image paths", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderGroupedMessage(
|
||||
container,
|
||||
{
|
||||
id: "user-history-image-blocked",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/Users/test/Documents/private.png",
|
||||
MediaType: "image/png",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
"user",
|
||||
{
|
||||
showToolCalls: false,
|
||||
basePath: "/openclaw",
|
||||
assistantAttachmentAuthToken: "session-token",
|
||||
localMediaPreviewRoots: ["/tmp/openclaw"],
|
||||
},
|
||||
);
|
||||
|
||||
container = renderUserMedia({
|
||||
id: "user-history-image-blocked",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/Users/test/Documents/private.png",
|
||||
MediaType: "image/png",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
expect(container.querySelector(".chat-message-image")).toBeNull();
|
||||
expect(container.querySelector(".chat-bubble")).toBeNull();
|
||||
});
|
||||
|
||||
it("skips non-image transcript media paths after history reload", () => {
|
||||
const container = document.createElement("div");
|
||||
|
||||
renderGroupedMessage(
|
||||
container,
|
||||
{
|
||||
id: "user-history-document",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.pdf",
|
||||
MediaType: "application/pdf",
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
"user",
|
||||
{
|
||||
showToolCalls: false,
|
||||
basePath: "/openclaw",
|
||||
assistantAttachmentAuthToken: "session-token",
|
||||
localMediaPreviewRoots: ["/tmp/openclaw"],
|
||||
},
|
||||
);
|
||||
|
||||
container = renderUserMedia({
|
||||
id: "user-history-document",
|
||||
role: "user",
|
||||
content: "",
|
||||
MediaPath: "/tmp/openclaw/user-upload.pdf",
|
||||
MediaType: "application/pdf",
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
expect(container.querySelector(".chat-message-image")).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user