feat(sdk): contrat permissions.workspaceExec pour l'exécution contrôlée via l'hôte — #346
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>
This commit is contained in:
@ -36,3 +36,26 @@ 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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user