[Init] Initial commit - NetMesh terminal manager
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

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,187 @@
"use strict";
const assert = require("node:assert/strict");
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const test = require("node:test");
const {
PluginProtocol,
decodeRequestPath,
readContainedFile,
} = require("./pluginProtocol.cjs");
function createRoots(context) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "netcatty-plugin-protocol-"));
context.after(() => fs.rmSync(root, { recursive: true, force: true }));
const roots = {
packageRoot: path.join(root, "package"),
runtimeDirectory: path.join(root, "runtime"),
sdkDirectory: path.join(root, "sdk"),
contractDirectory: path.join(root, "contract"),
};
for (const directory of Object.values(roots)) fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(roots.packageRoot, "index.js"), "export default {};\n");
fs.mkdirSync(path.join(roots.packageRoot, ".runtime"));
fs.writeFileSync(path.join(roots.packageRoot, ".runtime", "index.js"), "export default {};\n");
fs.writeFileSync(path.join(roots.runtimeDirectory, "browserRuntime.mjs"), "export {};\n");
fs.writeFileSync(path.join(roots.sdkDirectory, "index.js"), "export {};\n");
fs.writeFileSync(path.join(roots.contractDirectory, "index.js"), "export {};\n");
return roots;
}
test("plugin URL decoding rejects traversal and platform separators", () => {
assert.deepEqual(decodeRequestPath("/package/dist/index.js"), ["package", "dist", "index.js"]);
for (const unsafe of ["/package/../escape", "/package/%2e%2e/escape", "/package/a%5cb", "/package//x"]) {
assert.throws(() => decodeRequestPath(unsafe));
}
});
test("contained resource reads reject symlink escape", async (context) => {
if (process.platform === "win32") return;
const roots = createRoots(context);
const outside = path.join(path.dirname(roots.packageRoot), "outside.js");
fs.writeFileSync(outside, "secret");
fs.symlinkSync(outside, path.join(roots.packageRoot, "linked.js"));
await assert.rejects(readContainedFile(roots.packageRoot, ["linked.js"]), /symlink outside/);
});
test("runtime-scoped protocol serves only registered package and host resources", async (context) => {
const roots = createRoots(context);
let handler;
let unhandled = false;
const fakeSession = {
protocol: {
handle(scheme, callback) {
assert.equal(scheme, "netcatty-plugin");
handler = callback;
},
unhandle(scheme) {
assert.equal(scheme, "netcatty-plugin");
unhandled = true;
},
},
};
const protocol = new PluginProtocol(roots);
const sessionRegistration = protocol.registerSession(fakeSession);
assert.throws(() => protocol.registerSession(fakeSession), /already registered/);
const config = { pluginId: "com.example.test" };
const registration = protocol.registerRuntime({
pluginId: "com.example.test",
packageRoot: roots.packageRoot,
config,
});
const page = await handler({ method: "GET", url: registration.url });
assert.equal(page.status, 200);
assert.doesNotMatch(page.headers.get("content-security-policy"), /unsafe-inline/);
assert.match(page.headers.get("content-security-policy"), /nonce-/);
assert.match(page.headers.get("content-security-policy"), /worker-src 'none'/);
assert.equal(page.headers.get("x-dns-prefetch-control"), "off");
assert.match(page.headers.get("permissions-policy"), /camera=\(\)/);
const packageResponse = await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/package/index.js`,
});
assert.equal(await packageResponse.text(), "export default {};\n");
const dotPackageResponse = await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/package/.runtime/index.js`,
});
assert.equal(dotPackageResponse.status, 200);
assert.equal(await dotPackageResponse.text(), "export default {};\n");
const configResponse = await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/__host/runtime/config.json`,
});
assert.deepEqual(await configResponse.json(), config);
const otherRuntime = await handler({ method: "GET", url: "netcatty-plugin://unknown/package/index.js" });
assert.equal(otherRuntime.status, 404);
registration.dispose();
assert.equal((await handler({ method: "GET", url: registration.url })).status, 404);
sessionRegistration.dispose();
sessionRegistration.dispose();
assert.equal(unhandled, true);
});
test("browser runtime import maps accept reviewed host modules without protocol changes", async (context) => {
const roots = createRoots(context);
const uiDirectory = path.join(path.dirname(roots.packageRoot), "ui");
fs.mkdirSync(uiDirectory);
fs.writeFileSync(path.join(uiDirectory, "index.js"), "export const ui = true;\n");
let handler;
const protocol = new PluginProtocol({
runtimeDirectory: roots.runtimeDirectory,
moduleResources: [
{ specifier: "@netcatty/plugin-sdk", directory: roots.sdkDirectory },
{ specifier: "@netcatty/plugin-contract", directory: roots.contractDirectory },
{ specifier: "@netcatty/plugin-ui", directory: uiDirectory },
],
});
protocol.registerSession({
protocol: {
handle(_scheme, callback) { handler = callback; },
unhandle() {},
},
});
const registration = protocol.registerRuntime({
pluginId: "com.example.modules",
packageRoot: roots.packageRoot,
config: {},
});
const page = await (await handler({ method: "GET", url: registration.url })).text();
assert.match(page, /@netcatty\/plugin-ui/);
assert.match(page, /__host\/modules\/m2\/index\.js/);
const moduleResponse = await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/__host/modules/m2/index.js`,
});
assert.equal(await moduleResponse.text(), "export const ui = true;\n");
});
test("custom views load packaged resources only with a network-denying document policy", async (context) => {
const roots = createRoots(context);
fs.writeFileSync(path.join(roots.packageRoot, "view.html"), "<!doctype html><script src=\"view.js\"></script>\n");
fs.writeFileSync(path.join(roots.packageRoot, "view.js"), "globalThis.ready = true;\n");
fs.writeFileSync(path.join(roots.packageRoot, "view.svg"), "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>\n");
let handler;
const protocol = new PluginProtocol(roots);
protocol.registerSession({
protocol: {
handle(_scheme, callback) { handler = callback; },
unhandle() {},
},
});
const registration = protocol.registerView({
pluginId: "com.example.view",
packageRoot: roots.packageRoot,
entry: "view.html",
});
const page = await handler({ method: "GET", url: registration.url });
assert.equal(page.status, 200);
assert.match(page.headers.get("content-security-policy"), /connect-src 'none'/u);
assert.match(page.headers.get("content-security-policy"), /frame-src 'none'/u);
assert.match(page.headers.get("permissions-policy"), /clipboard-write=\(\)/u);
const svgRegistration = protocol.registerView({
pluginId: "com.example.svg-view",
packageRoot: roots.packageRoot,
entry: "view.svg",
});
const svgPage = await handler({ method: "GET", url: svgRegistration.url });
assert.equal(svgPage.headers.get("content-type"), "image/svg+xml");
assert.match(svgPage.headers.get("content-security-policy"), /worker-src 'none'/u);
assert.match(svgPage.headers.get("permissions-policy"), /camera=\(\)/u);
assert.equal((await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/package/view.js`,
})).status, 200);
assert.equal((await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/__host/runtime/browserRuntime.mjs`,
})).status, 404);
assert.equal((await handler({
method: "GET",
url: `netcatty-plugin://${registration.token}/index.html`,
})).status, 404);
});