Cadrage capacités plugin SCM (#346): le manifeste plugin gagne permissions.workspaceExec.allow — règles d'exécution contrôlée par l'hôte (commande bare ASCII résolue sans shell, argv exact avec jeton `*` en suffixe, cwd project-relative sûr, effect read/write/network/destructive, 64 règles max) — au lieu de lectures ad hoc du store interne .git. Validation manifeste + exports runtime/index + docs README/manifest/ services + auto-check script étendu (règles valides acceptées, règles unsafe rejetées). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.0 KiB
JavaScript
62 lines
2.0 KiB
JavaScript
import assert from "node:assert/strict";
|
|
|
|
import { validatePluginManifest } from "../dist/index.js";
|
|
|
|
const base = {
|
|
ideaPluginManifestVersion: 1,
|
|
id: "dev.example.tools",
|
|
displayName: "Tools",
|
|
version: "1.0.0",
|
|
main: "dist/index.js",
|
|
trustLevel: "full",
|
|
capabilities: ["mcp"],
|
|
contributes: {
|
|
mcpServers: [
|
|
{
|
|
id: "server",
|
|
displayName: "Server",
|
|
command: "bin/server",
|
|
transport: "stdio"
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
assert.equal(validatePluginManifest(base).success, true, "tools metadata remains optional");
|
|
|
|
const withTools = structuredClone(base);
|
|
withTools.contributes.mcpServers[0].tools = [
|
|
{ name: "read_scene", description: "Read the active scene." },
|
|
{ name: "run_tests" }
|
|
];
|
|
assert.equal(validatePluginManifest(withTools).success, true, "valid tools metadata is accepted");
|
|
|
|
const duplicate = structuredClone(withTools);
|
|
duplicate.contributes.mcpServers[0].tools.push({ name: "read_scene" });
|
|
const duplicateResult = validatePluginManifest(duplicate);
|
|
assert.equal(duplicateResult.success, false, "duplicate tool metadata is rejected");
|
|
assert.ok(duplicateResult.errors.some((error) => error.includes("must be unique")));
|
|
|
|
const withWorkspaceExec = structuredClone(base);
|
|
withWorkspaceExec.permissions = {
|
|
workspaceExec: {
|
|
allow: [{ command: "tool", args: ["status", "*"], cwd: ".", effect: "read" }]
|
|
}
|
|
};
|
|
assert.equal(
|
|
validatePluginManifest(withWorkspaceExec).success,
|
|
true,
|
|
"valid generic workspace execution permissions are accepted"
|
|
);
|
|
|
|
for (const invalidRule of [
|
|
{ command: "sh -c", args: [], cwd: ".", effect: "read" },
|
|
{ command: "tool", args: ["sta*"], cwd: ".", effect: "read" },
|
|
{ command: "tool", args: [], cwd: "../outside", effect: "read" },
|
|
{ command: "tool", args: [], cwd: ".", effect: "git" }
|
|
]) {
|
|
const invalid = structuredClone(base);
|
|
invalid.permissions = { workspaceExec: { allow: [invalidRule] } };
|
|
assert.equal(validatePluginManifest(invalid).success, false, "unsafe workspace exec rule is rejected");
|
|
}
|