Files
openclaw/extensions/mxc/test/config.test.ts
Paul Campbell 008f04a656 feat(mxc): add Windows MXC sandbox backend (#97086)
* feat(mxc): add Windows MXC sandbox backend

Add the official MXC sandbox plugin package with Windows ProcessContainer execution, plugin-owned MXC SDK dependency packaging, host-backed filesystem bridge support, and configured MXC policy file loading via mxcPolicyPaths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(mxc): preserve Windows binary override paths

* fix: remove stray sandbox barrel export

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9ea19539-b8ca-44fb-93bd-b8496e3deb2c

* fix(mxc): address sandbox review feedback

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): satisfy test type checks

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): clarify protected skill enforcement

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* test(mxc): align fail-closed expectations

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): satisfy extension lint

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(plugin-sdk): narrow fs-safe remove surface

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(mxc): repair rebased CI failures

* fix(scripts): declare shrinkwrap override normalizer

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-07-12 23:07:25 -07:00

115 lines
4.1 KiB
TypeScript

import { MAX_TIMER_TIMEOUT_SECONDS } from "openclaw/plugin-sdk/number-runtime";
import { describe, expect, test } from "vitest";
import { createMxcPluginConfigSchema, resolveConfig } from "../src/config.js";
describe("resolveConfig", () => {
test("uses defaults only when config is omitted", () => {
expect(resolveConfig(undefined)).toEqual({
mxcBinaryPath: undefined,
containment: "process",
network: "none",
timeoutSeconds: 120,
debug: false,
});
const config = resolveConfig({});
expect(config).toEqual({
mxcBinaryPath: undefined,
containment: "process",
network: "none",
timeoutSeconds: 120,
debug: false,
mxcPolicyPaths: undefined,
});
expect(config).not.toHaveProperty("timeoutSecondsConfigured");
});
test("applies valid overrides and preserves explicit timeout configuration", () => {
const config = resolveConfig({
mxcBinaryPath: " C:\\custom\\wxc-exec.exe ",
containment: "processcontainer",
network: "default",
timeoutSeconds: 60,
debug: true,
mxcPolicyPaths: [
" C:\\ProgramData\\openclaw\\mxc-machine-policy.json ",
" /opt/openclaw/mxc-user-policy.json ",
],
});
expect(config).toEqual({
mxcBinaryPath: "C:\\custom\\wxc-exec.exe",
containment: "processcontainer",
network: "default",
timeoutSeconds: 60,
timeoutSecondsConfigured: true,
debug: true,
mxcPolicyPaths: [
"C:\\ProgramData\\openclaw\\mxc-machine-policy.json",
"/opt/openclaw/mxc-user-policy.json",
],
});
});
test("rejects invalid root values and unknown keys", () => {
expect(() => resolveConfig(null)).toThrow(/Invalid mxc plugin config/u);
expect(() => resolveConfig("bad")).toThrow(/Invalid mxc plugin config/u);
expect(() => resolveConfig({ sandboxBaseline: {} })).toThrow(/sandboxBaseline/u);
});
test("rejects removed and malformed containment values", () => {
for (const containment of [
"windows_sandbox",
"wslc",
"microvm",
"seatbelt",
"isolation_session",
"lxc",
"invalid",
]) {
expect(() => resolveConfig({ containment })).toThrow(/containment/u);
}
});
test("rejects malformed enums and types instead of silently falling back", () => {
expect(() => resolveConfig({ network: "allow-all" })).toThrow(/network/u);
expect(() => resolveConfig({ debug: "true" })).toThrow(/debug/u);
expect(() => resolveConfig({ mxcBinaryPath: " " })).toThrow(/mxcBinaryPath/u);
});
test("enforces timeout bounds and only marks configured timeouts when supplied", () => {
expect(() => resolveConfig({ timeoutSeconds: 0 })).toThrow(/>= 1/u);
expect(() => resolveConfig({ timeoutSeconds: -5 })).toThrow(/>= 1/u);
expect(() => resolveConfig({ timeoutSeconds: "fast" })).toThrow(/timeoutSeconds/u);
expect(() => resolveConfig({ timeoutSeconds: MAX_TIMER_TIMEOUT_SECONDS + 1 })).toThrow(
new RegExp(`${MAX_TIMER_TIMEOUT_SECONDS}`, "u"),
);
const config = resolveConfig({ timeoutSeconds: MAX_TIMER_TIMEOUT_SECONDS });
expect(config.timeoutSeconds).toBe(MAX_TIMER_TIMEOUT_SECONDS);
expect(config.timeoutSecondsConfigured).toBe(true);
});
test("trims and validates mxcPolicyPaths as absolute paths", () => {
expect(() => resolveConfig({ mxcPolicyPaths: "C:\\policy.json" })).toThrow(/mxcPolicyPaths/u);
expect(() => resolveConfig({ mxcPolicyPaths: ["relative-policy.json"] })).toThrow(
/mxcPolicyPaths\[0\]/u,
);
expect(() => resolveConfig({ mxcPolicyPaths: [" "] })).toThrow(/mxcPolicyPaths/u);
expect(() => resolveConfig({ mxcPolicyPaths: [42] })).toThrow(/mxcPolicyPaths/u);
});
});
describe("createMxcPluginConfigSchema", () => {
test("publishes the same timeout cap in the plugin schema", () => {
const jsonSchema = createMxcPluginConfigSchema().jsonSchema as {
properties?: { timeoutSeconds?: unknown };
};
expect(jsonSchema.properties?.timeoutSeconds).toEqual({
type: "number",
minimum: 1,
maximum: MAX_TIMER_TIMEOUT_SECONDS,
});
});
});