Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
81 lines
3.1 KiB
JavaScript
81 lines
3.1 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("node:assert/strict");
|
|
const test = require("node:test");
|
|
|
|
const {
|
|
PLUGIN_JSON_MAX_DEPTH,
|
|
PLUGIN_JSON_MAX_NODES,
|
|
assertPluginJsonValue,
|
|
} = require("./jsonBoundary.cjs");
|
|
|
|
test("main-process JSON boundary matches the public contract budgets", async () => {
|
|
const contract = await import("@netcatty/plugin-contract");
|
|
const hostLimits = require("./constants.cjs");
|
|
assert.equal(PLUGIN_JSON_MAX_DEPTH, contract.PLUGIN_JSON_MAX_DEPTH);
|
|
assert.equal(PLUGIN_JSON_MAX_NODES, contract.PLUGIN_JSON_MAX_NODES);
|
|
assert.equal(hostLimits.PLUGIN_RPC_MAX_JSON_BYTES, contract.PLUGIN_RPC_MAX_JSON_BYTES);
|
|
assert.equal(
|
|
hostLimits.PLUGIN_STREAM_MAX_FRAME_JSON_BYTES,
|
|
contract.PLUGIN_STREAM_MAX_FRAME_JSON_BYTES,
|
|
);
|
|
});
|
|
|
|
test("main-process JSON boundary rejects deep, sparse, cyclic, and accessor values", () => {
|
|
let deep = null;
|
|
for (let index = 0; index <= PLUGIN_JSON_MAX_DEPTH; index += 1) deep = [deep];
|
|
assert.throws(() => assertPluginJsonValue(deep), /levels/);
|
|
const sparse = [];
|
|
sparse.length = 1;
|
|
assert.throws(() => assertPluginJsonValue(sparse), /dense/);
|
|
const cyclic = {};
|
|
cyclic.self = cyclic;
|
|
assert.throws(() => assertPluginJsonValue(cyclic), /cycles/);
|
|
const accessor = {};
|
|
Object.defineProperty(accessor, "value", { get: () => 1, enumerable: true });
|
|
assert.throws(() => assertPluginJsonValue(accessor), /data properties/);
|
|
});
|
|
|
|
test("main-process JSON boundary enforces an incremental UTF-8 byte budget", () => {
|
|
const value = { text: "你好\n\"", empty: [] };
|
|
const exactBytes = Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
assert.deepEqual(assertPluginJsonValue(value, { maxBytes: exactBytes }), value);
|
|
assert.throws(
|
|
() => assertPluginJsonValue(value, { maxBytes: exactBytes - 1 }),
|
|
new RegExp(`exceeds ${exactBytes - 1} bytes`),
|
|
);
|
|
assert.throws(() => assertPluginJsonValue({}, { maxBytes: 0 }), /positive safe integer/);
|
|
});
|
|
|
|
test("maximum broker raw payloads fit inside JSON-RPC envelopes", () => {
|
|
const { PLUGIN_RPC_MAX_RAW_BYTES, PLUGIN_RPC_MAX_JSON_BYTES } = require("./constants.cjs");
|
|
const { assertRpcMessage } = require("./contractValidator.cjs");
|
|
const data = Buffer.alloc(PLUGIN_RPC_MAX_RAW_BYTES).toString("base64");
|
|
const response = {
|
|
jsonrpc: "2.0",
|
|
id: 1,
|
|
result: {
|
|
url: `https://example.com/${"x".repeat(8_000)}`,
|
|
status: 200,
|
|
headers: { "x-budget": "y".repeat(64 * 1024) },
|
|
body: { encoding: "base64", data },
|
|
},
|
|
};
|
|
assert.ok(Buffer.byteLength(JSON.stringify(response)) < PLUGIN_RPC_MAX_JSON_BYTES);
|
|
assert.doesNotThrow(() => assertRpcMessage(response));
|
|
const request = {
|
|
jsonrpc: "2.0",
|
|
id: 2,
|
|
method: "network.request",
|
|
params: {
|
|
url: `https://example.com/${"x".repeat(8_000)}`,
|
|
headers: Object.fromEntries(Array.from({ length: 8 }, (_, index) => (
|
|
[`x-budget-${index}`, "\\".repeat(8 * 1024)]
|
|
))),
|
|
body: { encoding: "utf8", data: "\0".repeat(PLUGIN_RPC_MAX_RAW_BYTES) },
|
|
},
|
|
};
|
|
assert.ok(Buffer.byteLength(JSON.stringify(request)) < PLUGIN_RPC_MAX_JSON_BYTES);
|
|
assert.doesNotThrow(() => assertRpcMessage(request));
|
|
});
|