mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
67f8108713
* feat(plugins): add privileged terminal data pipeline * fix(plugins): harden terminal interceptor lifecycle * fix(plugins): close terminal interceptor lifecycle gaps * fix(plugins): preserve terminal interceptor state transitions * fix(plugin): close terminal pipeline lifecycle gaps * fix(plugin): harden terminal interceptor startup * fix(plugin): isolate terminal output route attempts * fix(plugin): isolate reused terminal sessions * fix(plugin): harden terminal interceptor boundaries * fix(plugin): harden terminal replay metadata * fix(plugin): preserve terminal replay provenance * fix(plugin): close terminal pipeline lifecycle races * fix(plugin): harden terminal route handoff * fix(plugin): contain terminal handoff failures * fix(plugin): route terminal attachment through RPC * fix(plugin): validate terminal attachment RPC * fix(plugin): close unexpected runtime ports * fix(plugin): canonicalize terminal data frames * fix(plugin): close terminal pipeline lifecycle gaps * fix(plugin): close terminal interceptor review gaps * fix(plugin): harden terminal interceptor protocol handling * fix(plugin): preserve terminal flow compatibility * fix(plugin): preserve terminal protocol accounting * fix(plugin): bound terminal interceptor lifecycle * fix(plugin): close terminal interceptor review gaps --------- Co-authored-by: ryan-wong-coder <wangsiyuan19990317@gmail.com>
134 lines
4.5 KiB
JavaScript
134 lines
4.5 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("node:assert/strict");
|
|
const test = require("node:test");
|
|
|
|
const {
|
|
MAX_VISIBLE_FIELD_LENGTH,
|
|
MAX_VISIBLE_RESOURCES,
|
|
createNativePermissionDecisionProvider,
|
|
describePermissionRequest,
|
|
escapePermissionText,
|
|
} = require("./nativePermissionDecision.cjs");
|
|
|
|
function permissionRequest(overrides = {}) {
|
|
return {
|
|
requestId: "request-1",
|
|
pluginId: "com.example.permissions",
|
|
pluginName: "Permissions",
|
|
publisher: "example",
|
|
permission: "filesystem.read",
|
|
resources: ["/allowed"],
|
|
resourceKinds: ["directory"],
|
|
reason: "Read project files",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test("native permission decisions expose every host-owned grant scope", async () => {
|
|
for (const { sessionId, response, expected } of [
|
|
{ response: 0, expected: { requestId: "request-1", decision: "deny" } },
|
|
{ response: 1, expected: { requestId: "request-1", decision: "allow", scope: "once" } },
|
|
{ sessionId: "session-1", response: 2, expected: {
|
|
requestId: "request-1", decision: "allow", scope: "session",
|
|
} },
|
|
{ response: 2, expected: {
|
|
requestId: "request-1", decision: "allow", scope: "application",
|
|
} },
|
|
{ response: 3, expected: {
|
|
requestId: "request-1", decision: "allow", scope: "always",
|
|
} },
|
|
]) {
|
|
let captured;
|
|
const signal = new AbortController().signal;
|
|
const provider = createNativePermissionDecisionProvider({
|
|
dialog: {
|
|
async showMessageBox(_window, options) {
|
|
captured = options;
|
|
return { response };
|
|
},
|
|
},
|
|
window: {},
|
|
});
|
|
assert.deepEqual(await provider(permissionRequest({ sessionId }), { signal }), expected);
|
|
assert.equal(captured.signal, signal);
|
|
assert.equal(captured.cancelId, 0);
|
|
assert.match(captured.detail, /directory and descendants/u);
|
|
}
|
|
});
|
|
|
|
test("native permission decisions fail closed on cancellation and abort", async () => {
|
|
const provider = createNativePermissionDecisionProvider({
|
|
dialog: { showMessageBox: async () => ({ response: 999 }) },
|
|
});
|
|
assert.deepEqual(await provider(permissionRequest()), {
|
|
requestId: "request-1",
|
|
decision: "deny",
|
|
});
|
|
const controller = new AbortController();
|
|
controller.abort(new Error("runtime stopped"));
|
|
await assert.rejects(provider(permissionRequest(), { signal: controller.signal }), /runtime stopped/);
|
|
});
|
|
|
|
test("terminal interceptor permission prompts omit the unsupported one-use choice", async () => {
|
|
let captured;
|
|
const provider = createNativePermissionDecisionProvider({
|
|
dialog: {
|
|
async showMessageBox(options) {
|
|
captured = options;
|
|
return { response: 1 };
|
|
},
|
|
},
|
|
});
|
|
assert.deepEqual(await provider(permissionRequest({
|
|
sessionId: "session-1",
|
|
operationId: `terminal.interceptor.input:com.example.${"x".repeat(170)}`,
|
|
allowedScopes: ["session", "application", "always"],
|
|
})), {
|
|
requestId: "request-1",
|
|
decision: "allow",
|
|
scope: "session",
|
|
});
|
|
assert.deepEqual(captured.buttons, [
|
|
"Deny",
|
|
"Allow for Session",
|
|
"Allow for Application",
|
|
"Always Allow",
|
|
]);
|
|
});
|
|
|
|
test("native permission request details are bounded and preserve resource kinds", () => {
|
|
const resources = Array.from({ length: MAX_VISIBLE_RESOURCES + 5 }, (_, index) => `/path/${index}`);
|
|
const detail = describePermissionRequest(permissionRequest({
|
|
resources,
|
|
resourceKinds: resources.map(() => "exact"),
|
|
}));
|
|
assert.match(detail, /and 5 more/u);
|
|
assert.doesNotMatch(detail, /\/path\/24/u);
|
|
});
|
|
|
|
test("native permission dialogs visibly escape control and bidi characters", async () => {
|
|
let captured;
|
|
const provider = createNativePermissionDecisionProvider({
|
|
dialog: {
|
|
async showMessageBox(options) {
|
|
captured = options;
|
|
return { response: 0 };
|
|
},
|
|
},
|
|
});
|
|
await provider(permissionRequest({
|
|
pluginName: "Plugin\nPermission: vault.credentials",
|
|
publisher: "publisher\u202e",
|
|
reason: "Read this\r\nAlways Allow",
|
|
resources: ["/safe\nPermission: runtime.advanced"],
|
|
resourceKinds: ["exact"],
|
|
}));
|
|
assert.match(captured.message, /Plugin\\nPermission: vault\.credentials/u);
|
|
assert.match(captured.detail, /publisher\\u202e/u);
|
|
assert.match(captured.detail, /Read this\\r\\nAlways Allow/u);
|
|
assert.match(captured.detail, /\/safe\\nPermission: runtime\.advanced/u);
|
|
assert.equal(captured.detail.split("\n").filter((line) => line.startsWith("Permission:")).length, 1);
|
|
assert.equal(escapePermissionText("x".repeat(MAX_VISIBLE_FIELD_LENGTH + 20)).length, MAX_VISIBLE_FIELD_LENGTH);
|
|
});
|