Add AST-based security middleware and enforcement wiring
This commit is contained in:
@@ -10,6 +10,10 @@ import type {
|
||||
SharedMcpConfigFile,
|
||||
SharedMcpServer,
|
||||
} from "./types.js";
|
||||
import {
|
||||
parseToolClearancePolicy,
|
||||
type ToolClearancePolicy,
|
||||
} from "../security/schemas.js";
|
||||
|
||||
export type McpHandlerUtils = {
|
||||
inferTransport: typeof inferTransport;
|
||||
@@ -114,6 +118,77 @@ function readBooleanConfigValue(
|
||||
return typeof value === "boolean" ? value : undefined;
|
||||
}
|
||||
|
||||
function readStringArray(value: unknown): string[] | undefined {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const output: string[] = [];
|
||||
for (const item of value) {
|
||||
if (typeof item !== "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalized = item.trim();
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output.push(normalized);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function dedupe(values: string[]): string[] {
|
||||
const output: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const value of values) {
|
||||
if (seen.has(value)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(value);
|
||||
output.push(value);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function applyToolClearanceToResult(
|
||||
result: McpHandlerResult,
|
||||
toolClearance: ToolClearancePolicy,
|
||||
): McpHandlerResult {
|
||||
if (!result.codex) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const codexConfig = result.codex;
|
||||
const existingEnabled = readStringArray(codexConfig.enabled_tools);
|
||||
const existingDisabled = readStringArray(codexConfig.disabled_tools) ?? [];
|
||||
const banlist = new Set(toolClearance.banlist);
|
||||
|
||||
let enabledTools = existingEnabled;
|
||||
if (toolClearance.allowlist.length > 0) {
|
||||
enabledTools = (enabledTools ?? toolClearance.allowlist).filter((tool) =>
|
||||
toolClearance.allowlist.includes(tool),
|
||||
);
|
||||
}
|
||||
|
||||
if (enabledTools) {
|
||||
enabledTools = enabledTools.filter((tool) => !banlist.has(tool));
|
||||
}
|
||||
|
||||
const disabledTools = dedupe([...existingDisabled, ...toolClearance.banlist]);
|
||||
|
||||
return {
|
||||
...result,
|
||||
codex: {
|
||||
...codexConfig,
|
||||
...(enabledTools ? { enabled_tools: enabledTools } : {}),
|
||||
...(disabledTools.length > 0 ? { disabled_tools: disabledTools } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function applyEnabledByDefault(input: McpHandlerBusinessLogicInput): McpHandlerResult {
|
||||
if (input.server.enabled !== undefined) {
|
||||
return input.baseResult;
|
||||
@@ -187,8 +262,9 @@ export class McpRegistry {
|
||||
server: SharedMcpServer;
|
||||
context: McpLoadContext;
|
||||
fullConfig: SharedMcpConfigFile;
|
||||
toolClearance?: ToolClearancePolicy;
|
||||
}): McpHandlerResult & { handlerId: string } {
|
||||
const { serverName, server, context, fullConfig } = input;
|
||||
const { serverName, server, context, fullConfig, toolClearance } = input;
|
||||
const handler = this.resolveHandler(serverName, server);
|
||||
const handlerConfig = {
|
||||
...(fullConfig.handlerSettings?.[handler.id] ?? {}),
|
||||
@@ -203,9 +279,12 @@ export class McpRegistry {
|
||||
fullConfig,
|
||||
utils,
|
||||
});
|
||||
const securedResult = toolClearance
|
||||
? applyToolClearanceToResult(result, parseToolClearancePolicy(toolClearance))
|
||||
: result;
|
||||
|
||||
return {
|
||||
...result,
|
||||
...securedResult,
|
||||
handlerId: handler.id,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user