74 lines
2.8 KiB
JavaScript
74 lines
2.8 KiB
JavaScript
const { roleAllows } = require("./permissions");
|
|
|
|
const ROLE_RANK = Object.freeze({ user: 1, mod: 2, admin: 3 });
|
|
const FORBIDDEN_DEFINITION_KEYS = Object.freeze([
|
|
"shell",
|
|
"sql",
|
|
"filesystem",
|
|
"network",
|
|
"code_execution",
|
|
"eval"
|
|
]);
|
|
|
|
function registerManagedTool(registry, metadata, definition) {
|
|
validateManagedDefinition(metadata, definition);
|
|
const metadataRole = metadataRequiredRole(metadata.permissions);
|
|
const backendRole = normalizeRole(definition.required_role);
|
|
const requiredRole = stricterRole(metadataRole, backendRole);
|
|
const backendPermissionCheck = definition.permission_check;
|
|
const mutating = definition.mutating === true ||
|
|
["sensitive", "high", "destructive"].includes(String(definition.risk_level || metadata.risk_level || "").toLowerCase());
|
|
return registry.register({
|
|
...definition,
|
|
owning_plugin: metadata.tool_id,
|
|
required_role: requiredRole,
|
|
required_permission: String(definition.required_permission || `${metadata.tool_id}.use`),
|
|
audit_category: String(definition.audit_category || metadata.tool_type || "ai_tool"),
|
|
confirmation_required: mutating || metadata.confirmation_required === true
|
|
? true
|
|
: definition.confirmation_required !== false,
|
|
permission_check: (input) => {
|
|
const actualRole = input.user?.isAdmin ? "admin" : input.user?.isMod ? "mod" : "user";
|
|
if (!roleAllows(actualRole, requiredRole)) return false;
|
|
return backendPermissionCheck(input) === true;
|
|
}
|
|
});
|
|
}
|
|
|
|
function validateManagedDefinition(metadata, definition) {
|
|
if (!definition || typeof definition !== "object") throw new Error("AI tool definition is required.");
|
|
if (!String(definition.tool_id || "").startsWith(`${metadata.tool_id}.`)) {
|
|
throw new Error(`Registered tool IDs must use the ${metadata.tool_id}. namespace.`);
|
|
}
|
|
if (typeof definition.permission_check !== "function" || typeof definition.workflow_handler !== "function") {
|
|
throw new Error("Managed AI tools require backend permission and workflow handlers.");
|
|
}
|
|
for (const key of FORBIDDEN_DEFINITION_KEYS) {
|
|
if (definition[key] != null) throw new Error(`Managed AI tools cannot request generic ${key} access.`);
|
|
}
|
|
}
|
|
|
|
function metadataRequiredRole(permissions) {
|
|
if (typeof permissions === "string") return normalizeRole(permissions);
|
|
if (Array.isArray(permissions)) {
|
|
return permissions.map(normalizeRole).sort((a, b) => ROLE_RANK[b] - ROLE_RANK[a])[0] || "user";
|
|
}
|
|
return normalizeRole(permissions?.required_role || permissions?.role);
|
|
}
|
|
|
|
function normalizeRole(value) {
|
|
return Object.hasOwn(ROLE_RANK, value) ? value : "user";
|
|
}
|
|
|
|
function stricterRole(left, right) {
|
|
return ROLE_RANK[left] >= ROLE_RANK[right] ? left : right;
|
|
}
|
|
|
|
module.exports = {
|
|
FORBIDDEN_DEFINITION_KEYS,
|
|
registerManagedTool,
|
|
validateManagedDefinition,
|
|
metadataRequiredRole,
|
|
stricterRole
|
|
};
|