mirror of https://github.com/openclaw/openclaw.git
116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { withEnv } from "../test-utils/env.js";
|
||
import { decodeCapturedOutputBuffer, parseWindowsCodePage, sanitizeEnv } from "./invoke.js";
|
||
import { buildNodeInvokeResultParams } from "./runner.js";
|
||
|
||
describe("node-host sanitizeEnv", () => {
|
||
it("ignores PATH overrides", () => {
|
||
withEnv({ PATH: "/usr/bin" }, () => {
|
||
const env = sanitizeEnv({ PATH: "/tmp/evil:/usr/bin" });
|
||
expect(env.PATH).toBe("/usr/bin");
|
||
});
|
||
});
|
||
|
||
it("blocks dangerous env keys/prefixes", () => {
|
||
withEnv(
|
||
{ PYTHONPATH: undefined, LD_PRELOAD: undefined, BASH_ENV: undefined, SHELLOPTS: undefined },
|
||
() => {
|
||
const env = sanitizeEnv({
|
||
PYTHONPATH: "/tmp/pwn",
|
||
LD_PRELOAD: "/tmp/pwn.so",
|
||
BASH_ENV: "/tmp/pwn.sh",
|
||
SHELLOPTS: "xtrace",
|
||
PS4: "$(touch /tmp/pwned)",
|
||
FOO: "bar",
|
||
});
|
||
expect(env.FOO).toBe("bar");
|
||
expect(env.PYTHONPATH).toBeUndefined();
|
||
expect(env.LD_PRELOAD).toBeUndefined();
|
||
expect(env.BASH_ENV).toBeUndefined();
|
||
expect(env.SHELLOPTS).toBeUndefined();
|
||
expect(env.PS4).toBeUndefined();
|
||
},
|
||
);
|
||
});
|
||
|
||
it("blocks dangerous override-only env keys", () => {
|
||
withEnv({ HOME: "/Users/trusted", ZDOTDIR: "/Users/trusted/.zdot" }, () => {
|
||
const env = sanitizeEnv({
|
||
HOME: "/tmp/evil-home",
|
||
ZDOTDIR: "/tmp/evil-zdotdir",
|
||
});
|
||
expect(env.HOME).toBe("/Users/trusted");
|
||
expect(env.ZDOTDIR).toBe("/Users/trusted/.zdot");
|
||
});
|
||
});
|
||
|
||
it("drops dangerous inherited env keys even without overrides", () => {
|
||
withEnv({ PATH: "/usr/bin:/bin", BASH_ENV: "/tmp/pwn.sh" }, () => {
|
||
const env = sanitizeEnv(undefined);
|
||
expect(env.PATH).toBe("/usr/bin:/bin");
|
||
expect(env.BASH_ENV).toBeUndefined();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe("node-host output decoding", () => {
|
||
it("parses code pages from chcp output text", () => {
|
||
expect(parseWindowsCodePage("Active code page: 936")).toBe(936);
|
||
expect(parseWindowsCodePage("活动代码页: 65001")).toBe(65001);
|
||
expect(parseWindowsCodePage("no code page")).toBeNull();
|
||
});
|
||
|
||
it("decodes GBK output on Windows when code page is known", () => {
|
||
let supportsGbk = true;
|
||
try {
|
||
void new TextDecoder("gbk");
|
||
} catch {
|
||
supportsGbk = false;
|
||
}
|
||
|
||
const raw = Buffer.from([0xb2, 0xe2, 0xca, 0xd4, 0xa1, 0xab, 0xa3, 0xbb]);
|
||
const decoded = decodeCapturedOutputBuffer({
|
||
buffer: raw,
|
||
platform: "win32",
|
||
windowsEncoding: "gbk",
|
||
});
|
||
|
||
if (!supportsGbk) {
|
||
expect(decoded).toContain("<22>");
|
||
return;
|
||
}
|
||
expect(decoded).toBe("测试~;");
|
||
});
|
||
});
|
||
|
||
describe("buildNodeInvokeResultParams", () => {
|
||
it("omits optional fields when null/undefined", () => {
|
||
const params = buildNodeInvokeResultParams(
|
||
{ id: "invoke-1", nodeId: "node-1", command: "system.run" },
|
||
{ ok: true, payloadJSON: null, error: null },
|
||
);
|
||
|
||
expect(params).toEqual({ id: "invoke-1", nodeId: "node-1", ok: true });
|
||
expect("payloadJSON" in params).toBe(false);
|
||
expect("error" in params).toBe(false);
|
||
});
|
||
|
||
it("includes payloadJSON when provided", () => {
|
||
const params = buildNodeInvokeResultParams(
|
||
{ id: "invoke-2", nodeId: "node-2", command: "system.run" },
|
||
{ ok: true, payloadJSON: '{"ok":true}' },
|
||
);
|
||
|
||
expect(params.payloadJSON).toBe('{"ok":true}');
|
||
});
|
||
|
||
it("includes payload when provided", () => {
|
||
const params = buildNodeInvokeResultParams(
|
||
{ id: "invoke-3", nodeId: "node-3", command: "system.run" },
|
||
{ ok: false, payload: { reason: "bad" } },
|
||
);
|
||
|
||
expect(params.payload).toEqual({ reason: "bad" });
|
||
});
|
||
});
|