Add AST-based security middleware and enforcement wiring

This commit is contained in:
2026-02-23 14:21:22 -05:00
parent 9b4216dda9
commit ef2a25b5fb
28 changed files with 1936 additions and 37 deletions

View File

@@ -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,
};
}