mcp-warden 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # mcp-warden
2
2
 
3
- ![NPM Version](https://img.shields.io/npm/v/mcp-warden)
4
- ![License](https://img.shields.io/npm/l/mcp-warden)
5
- ![Bundle Size](https://img.shields.io/bundlephobia/min/mcp-warden)
3
+ [![NPM Version](https://img.shields.io/npm/v/mcp-warden)](https://www.npmjs.com/package/mcp-warden)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/vikrantwiz02/mcp-warden?tab=MIT-1-ov-file)
5
+ [![Bundle Size](https://img.shields.io/bundlephobia/min/mcp-warden)](https://bundlephobia.com/package/mcp-warden)
6
6
 
7
7
  High-performance security guardrails for MCP-compatible AI agents and tool execution.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - Policy-based tool authorization for MCP tool calls.
12
+ - Filesystem path enforcement for blocked paths defined in policy.
13
+ - Human-in-the-loop gating with `REQUIRES_APPROVAL` status when approval is mandated.
12
14
  - Prompt-injection scanning for high-risk control phrases.
13
15
  - Output redaction for email addresses, API keys, and IP addresses.
14
16
  - Built-in rate limiting and circuit-breaker protection.
@@ -30,6 +32,8 @@ AI agents can execute tools with real-world side effects: reading files, modifyi
30
32
 
31
33
  mcp-warden helps enforce a security boundary before and after tool execution:
32
34
  - Blocks unauthorized tools using explicit policy rules.
35
+ - Denies tool calls that target blocked filesystem paths from policy.
36
+ - Returns `REQUIRES_APPROVAL` before execution when `approvalRequired` is enabled.
33
37
  - Detects prompt-injection signatures in tool arguments.
34
38
  - Enforces rate limits to reduce abuse and runaway call storms.
35
39
  - Applies circuit-breaker protection for repeated tool failures.
@@ -107,7 +111,7 @@ mcp-warden init
107
111
  | allowedTools | Array<string \| RegExp> | Yes | Allowed tool names or regex matchers for tools/call requests. | ["read_file", /^search_/] |
108
112
  | restrictedPaths | Array<{ path: string; mode: "read-only" \| "blocked" }> | Yes | Directory access constraints used by filesystem-aware middleware. | [{ "path": "/", "mode": "blocked" }] |
109
113
  | maxCallsPerMinute | number | Yes | Rolling 60-second budget for tool calls. Requests above this limit are denied. | 60 |
110
- | approvalRequired | boolean | Yes | Indicates destructive actions should require explicit approval in your orchestration flow. | true |
114
+ | approvalRequired | boolean | Yes | When true, tool calls are paused with `REQUIRES_APPROVAL` until explicit human approval is granted by the host system. | true |
111
115
 
112
116
  ## CLI Commands
113
117
 
@@ -129,7 +133,7 @@ npm run build
129
133
 
130
134
  ## License
131
135
 
132
- MIT
136
+ [MIT](https://github.com/vikrantwiz02/mcp-warden?tab=MIT-1-ov-file)
133
137
 
134
138
  ## Security Disclaimer
135
139
 
package/dist/cli/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { promises as fs } from "node:fs";
3
3
  import * as path from "node:path";
4
+ import { fileURLToPath } from "node:url";
4
5
  import chalk from "chalk";
5
6
  import { Command } from "commander";
6
7
  import { GuardianPolicySchema } from "../types/policy.js";
@@ -173,15 +174,35 @@ async function runInit(outputPath, force) {
173
174
  await fs.writeFile(absolutePath, serialized, "utf8");
174
175
  console.log(chalk.green(`SAFE: Created ${outputPath}`));
175
176
  }
177
+ /**
178
+ * Resolves CLI version from package.json to avoid hardcoded drift.
179
+ */
180
+ async function resolveCliVersion() {
181
+ const currentFilePath = fileURLToPath(import.meta.url);
182
+ const currentDir = path.dirname(currentFilePath);
183
+ const packageJsonPath = path.resolve(currentDir, "../../package.json");
184
+ try {
185
+ const content = await fs.readFile(packageJsonPath, "utf8");
186
+ const parsed = JSON.parse(content);
187
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
188
+ return parsed.version;
189
+ }
190
+ }
191
+ catch {
192
+ // Fall back to a safe placeholder if package metadata cannot be read.
193
+ }
194
+ return "0.0.0";
195
+ }
176
196
  /**
177
197
  * Boots the mcp-warden CLI.
178
198
  */
179
199
  async function main() {
180
200
  const program = new Command();
201
+ const cliVersion = await resolveCliVersion();
181
202
  program
182
203
  .name("mcp-warden")
183
204
  .description("Security auditing and policy tooling for MCP servers")
184
- .version("0.1.0");
205
+ .version(cliVersion);
185
206
  program
186
207
  .command("audit")
187
208
  .description("Audit a claude_desktop_config.json or cursor-settings.json file")
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { promises as fs } from \"node:fs\";\nimport * as path from \"node:path\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { GuardianPolicySchema, type GuardianPolicy } from \"../types/policy.js\";\n\n/**\n * Minimal server configuration shape extracted from MCP client config files.\n */\ninterface McpServerConfig {\n args?: unknown;\n env?: unknown;\n permissions?: unknown;\n roots?: unknown;\n allowedPaths?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Named server entry used by the audit report.\n */\ninterface ServerEntry {\n name: string;\n config: McpServerConfig;\n}\n\n/**\n * Policy file created by `mcp-warden init`.\n */\nconst DEFAULT_POLICY: GuardianPolicy = {\n allowedTools: [\"list_dir\", \"read_file\", \"grep_search\"],\n restrictedPaths: [\n {\n path: \"/\",\n mode: \"blocked\"\n }\n ],\n maxCallsPerMinute: 60,\n approvalRequired: true\n};\n\n/**\n * True if a value is a plain object.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Converts unknown values to a string array.\n */\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\n/**\n * Best-effort extraction of MCP servers from common config layouts.\n */\nfunction extractServers(config: unknown): ServerEntry[] {\n if (!isRecord(config)) {\n return [];\n }\n\n const candidates: unknown[] = [\n config.mcpServers,\n isRecord(config.mcp) ? config.mcp.servers : undefined,\n config.servers\n ];\n\n for (const candidate of candidates) {\n if (!isRecord(candidate)) {\n continue;\n }\n\n return Object.entries(candidate)\n .filter(([, server]) => isRecord(server))\n .map(([name, server]) => ({\n name,\n config: server as McpServerConfig\n }));\n }\n\n return [];\n}\n\n/**\n * Returns true when a path represents broad filesystem access.\n */\nfunction isBroadPath(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n return (\n normalized === \"/\" ||\n normalized === \"~\" ||\n normalized === \"$home\" ||\n normalized === \"${home}\" ||\n normalized === \"c:\\\\\" ||\n normalized.startsWith(\"/users\")\n );\n}\n\n/**\n * Detects critical full-access permissions in a server config.\n */\nfunction getCriticalFindings(server: McpServerConfig): string[] {\n const findings: string[] = [];\n\n const permissions = toStringArray(server.permissions);\n if (\n permissions.some((entry) =>\n [\"*\", \"all\", \"full-disk-access\", \"filesystem:*\"].includes(entry.trim().toLowerCase())\n )\n ) {\n findings.push(\"Permission set grants full disk access.\");\n }\n\n const roots = toStringArray(server.roots);\n const allowedPaths = toStringArray(server.allowedPaths);\n const broadPath = [...roots, ...allowedPaths].find((entry) => isBroadPath(entry));\n if (broadPath) {\n findings.push(`Broad filesystem path is allowed: ${broadPath}`);\n }\n\n const args = toStringArray(server.args).map((arg) => arg.toLowerCase());\n if (\n args.some((arg) =>\n [\n \"--allow-all\",\n \"--dangerously-skip-permissions\",\n \"--full-disk-access\",\n \"--allow-read=/\",\n \"--allow-write=/\"\n ].some((flag) => arg.includes(flag))\n )\n ) {\n findings.push(\"Command-line flags indicate unrestricted filesystem access.\");\n }\n\n if (isRecord(server.env)) {\n const envPairs = Object.entries(server.env);\n const hasWideEnvAccess = envPairs.some(([key, rawValue]) => {\n if (typeof rawValue !== \"string\") {\n return false;\n }\n\n const envKey = key.toLowerCase();\n const envValue = rawValue.trim().toLowerCase();\n return (\n [\"full_disk_access\", \"allow_all\", \"dangerously_skip_permissions\"].includes(envKey) &&\n [\"1\", \"true\", \"yes\", \"on\"].includes(envValue)\n );\n });\n\n if (hasWideEnvAccess) {\n findings.push(\"Environment variables enable unrestricted permissions.\");\n }\n }\n\n return findings;\n}\n\n/**\n * Reads and parses a JSON configuration file from disk.\n */\nasync function readJsonFile(filePath: string): Promise<unknown> {\n const content = await fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as unknown;\n}\n\n/**\n * Runs audit mode and prints color-coded findings.\n */\nasync function runAudit(configPath: string): Promise<void> {\n const absolutePath = path.resolve(process.cwd(), configPath);\n const config = await readJsonFile(absolutePath);\n const servers = extractServers(config);\n\n console.log(chalk.bold(`Audit file: ${absolutePath}`));\n\n if (servers.length === 0) {\n console.log(chalk.red(\"CRITICAL: No MCP servers found. Verify config structure before deployment.\"));\n process.exitCode = 1;\n return;\n }\n\n let criticalCount = 0;\n\n for (const server of servers) {\n const findings = getCriticalFindings(server.config);\n if (findings.length === 0) {\n console.log(chalk.green(`SAFE: ${server.name}`));\n continue;\n }\n\n criticalCount += 1;\n console.log(chalk.red(`CRITICAL: ${server.name}`));\n for (const finding of findings) {\n console.log(chalk.red(` - ${finding}`));\n }\n }\n\n if (criticalCount > 0) {\n console.log(chalk.red(`\\nCritical servers: ${criticalCount}/${servers.length}`));\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.green(`\\nAll servers safe: ${servers.length}/${servers.length}`));\n}\n\n/**\n * Creates a default guardian policy JSON file.\n */\nasync function runInit(outputPath: string, force: boolean): Promise<void> {\n const absolutePath = path.resolve(process.cwd(), outputPath);\n\n if (!force) {\n try {\n await fs.access(absolutePath);\n console.log(chalk.red(`CRITICAL: ${outputPath} already exists. Use --force to overwrite.`));\n process.exitCode = 1;\n return;\n } catch {\n // File does not exist and can be created.\n }\n }\n\n const policy = GuardianPolicySchema.parse(DEFAULT_POLICY);\n const serialized = `${JSON.stringify(policy, null, 2)}\\n`;\n await fs.writeFile(absolutePath, serialized, \"utf8\");\n\n console.log(chalk.green(`SAFE: Created ${outputPath}`));\n}\n\n/**\n * Boots the mcp-warden CLI.\n */\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name(\"mcp-warden\")\n .description(\"Security auditing and policy tooling for MCP servers\")\n .version(\"0.1.0\");\n\n program\n .command(\"audit\")\n .description(\"Audit a claude_desktop_config.json or cursor-settings.json file\")\n .argument(\"<config-path>\", \"Path to a JSON config file\")\n .action(async (configPath: string) => {\n await runAudit(configPath);\n });\n\n program\n .command(\"init\")\n .description(\"Generate a default mcp-policy.json\")\n .option(\"-o, --output <path>\", \"Output path for generated policy\", \"mcp-policy.json\")\n .option(\"-f, --force\", \"Overwrite existing file\")\n .action(async (options: { output: string; force?: boolean }) => {\n await runInit(options.output, Boolean(options.force));\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : \"Unknown CLI error\";\n console.error(chalk.red(`CRITICAL: ${message}`));\n process.exitCode = 1;\n});\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { promises as fs } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { GuardianPolicySchema, type GuardianPolicy } from \"../types/policy.js\";\n\n/**\n * Minimal server configuration shape extracted from MCP client config files.\n */\ninterface McpServerConfig {\n args?: unknown;\n env?: unknown;\n permissions?: unknown;\n roots?: unknown;\n allowedPaths?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Named server entry used by the audit report.\n */\ninterface ServerEntry {\n name: string;\n config: McpServerConfig;\n}\n\n/**\n * Policy file created by `mcp-warden init`.\n */\nconst DEFAULT_POLICY: GuardianPolicy = {\n allowedTools: [\"list_dir\", \"read_file\", \"grep_search\"],\n restrictedPaths: [\n {\n path: \"/\",\n mode: \"blocked\"\n }\n ],\n maxCallsPerMinute: 60,\n approvalRequired: true\n};\n\n/**\n * True if a value is a plain object.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Converts unknown values to a string array.\n */\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((entry): entry is string => typeof entry === \"string\");\n}\n\n/**\n * Best-effort extraction of MCP servers from common config layouts.\n */\nfunction extractServers(config: unknown): ServerEntry[] {\n if (!isRecord(config)) {\n return [];\n }\n\n const candidates: unknown[] = [\n config.mcpServers,\n isRecord(config.mcp) ? config.mcp.servers : undefined,\n config.servers\n ];\n\n for (const candidate of candidates) {\n if (!isRecord(candidate)) {\n continue;\n }\n\n return Object.entries(candidate)\n .filter(([, server]) => isRecord(server))\n .map(([name, server]) => ({\n name,\n config: server as McpServerConfig\n }));\n }\n\n return [];\n}\n\n/**\n * Returns true when a path represents broad filesystem access.\n */\nfunction isBroadPath(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n return (\n normalized === \"/\" ||\n normalized === \"~\" ||\n normalized === \"$home\" ||\n normalized === \"${home}\" ||\n normalized === \"c:\\\\\" ||\n normalized.startsWith(\"/users\")\n );\n}\n\n/**\n * Detects critical full-access permissions in a server config.\n */\nfunction getCriticalFindings(server: McpServerConfig): string[] {\n const findings: string[] = [];\n\n const permissions = toStringArray(server.permissions);\n if (\n permissions.some((entry) =>\n [\"*\", \"all\", \"full-disk-access\", \"filesystem:*\"].includes(entry.trim().toLowerCase())\n )\n ) {\n findings.push(\"Permission set grants full disk access.\");\n }\n\n const roots = toStringArray(server.roots);\n const allowedPaths = toStringArray(server.allowedPaths);\n const broadPath = [...roots, ...allowedPaths].find((entry) => isBroadPath(entry));\n if (broadPath) {\n findings.push(`Broad filesystem path is allowed: ${broadPath}`);\n }\n\n const args = toStringArray(server.args).map((arg) => arg.toLowerCase());\n if (\n args.some((arg) =>\n [\n \"--allow-all\",\n \"--dangerously-skip-permissions\",\n \"--full-disk-access\",\n \"--allow-read=/\",\n \"--allow-write=/\"\n ].some((flag) => arg.includes(flag))\n )\n ) {\n findings.push(\"Command-line flags indicate unrestricted filesystem access.\");\n }\n\n if (isRecord(server.env)) {\n const envPairs = Object.entries(server.env);\n const hasWideEnvAccess = envPairs.some(([key, rawValue]) => {\n if (typeof rawValue !== \"string\") {\n return false;\n }\n\n const envKey = key.toLowerCase();\n const envValue = rawValue.trim().toLowerCase();\n return (\n [\"full_disk_access\", \"allow_all\", \"dangerously_skip_permissions\"].includes(envKey) &&\n [\"1\", \"true\", \"yes\", \"on\"].includes(envValue)\n );\n });\n\n if (hasWideEnvAccess) {\n findings.push(\"Environment variables enable unrestricted permissions.\");\n }\n }\n\n return findings;\n}\n\n/**\n * Reads and parses a JSON configuration file from disk.\n */\nasync function readJsonFile(filePath: string): Promise<unknown> {\n const content = await fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as unknown;\n}\n\n/**\n * Runs audit mode and prints color-coded findings.\n */\nasync function runAudit(configPath: string): Promise<void> {\n const absolutePath = path.resolve(process.cwd(), configPath);\n const config = await readJsonFile(absolutePath);\n const servers = extractServers(config);\n\n console.log(chalk.bold(`Audit file: ${absolutePath}`));\n\n if (servers.length === 0) {\n console.log(chalk.red(\"CRITICAL: No MCP servers found. Verify config structure before deployment.\"));\n process.exitCode = 1;\n return;\n }\n\n let criticalCount = 0;\n\n for (const server of servers) {\n const findings = getCriticalFindings(server.config);\n if (findings.length === 0) {\n console.log(chalk.green(`SAFE: ${server.name}`));\n continue;\n }\n\n criticalCount += 1;\n console.log(chalk.red(`CRITICAL: ${server.name}`));\n for (const finding of findings) {\n console.log(chalk.red(` - ${finding}`));\n }\n }\n\n if (criticalCount > 0) {\n console.log(chalk.red(`\\nCritical servers: ${criticalCount}/${servers.length}`));\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.green(`\\nAll servers safe: ${servers.length}/${servers.length}`));\n}\n\n/**\n * Creates a default guardian policy JSON file.\n */\nasync function runInit(outputPath: string, force: boolean): Promise<void> {\n const absolutePath = path.resolve(process.cwd(), outputPath);\n\n if (!force) {\n try {\n await fs.access(absolutePath);\n console.log(chalk.red(`CRITICAL: ${outputPath} already exists. Use --force to overwrite.`));\n process.exitCode = 1;\n return;\n } catch {\n // File does not exist and can be created.\n }\n }\n\n const policy = GuardianPolicySchema.parse(DEFAULT_POLICY);\n const serialized = `${JSON.stringify(policy, null, 2)}\\n`;\n await fs.writeFile(absolutePath, serialized, \"utf8\");\n\n console.log(chalk.green(`SAFE: Created ${outputPath}`));\n}\n\n/**\n * Resolves CLI version from package.json to avoid hardcoded drift.\n */\nasync function resolveCliVersion(): Promise<string> {\n const currentFilePath = fileURLToPath(import.meta.url);\n const currentDir = path.dirname(currentFilePath);\n const packageJsonPath = path.resolve(currentDir, \"../../package.json\");\n\n try {\n const content = await fs.readFile(packageJsonPath, \"utf8\");\n const parsed = JSON.parse(content) as { version?: unknown };\n if (typeof parsed.version === \"string\" && parsed.version.length > 0) {\n return parsed.version;\n }\n } catch {\n // Fall back to a safe placeholder if package metadata cannot be read.\n }\n\n return \"0.0.0\";\n}\n\n/**\n * Boots the mcp-warden CLI.\n */\nasync function main(): Promise<void> {\n const program = new Command();\n const cliVersion = await resolveCliVersion();\n\n program\n .name(\"mcp-warden\")\n .description(\"Security auditing and policy tooling for MCP servers\")\n .version(cliVersion);\n\n program\n .command(\"audit\")\n .description(\"Audit a claude_desktop_config.json or cursor-settings.json file\")\n .argument(\"<config-path>\", \"Path to a JSON config file\")\n .action(async (configPath: string) => {\n await runAudit(configPath);\n });\n\n program\n .command(\"init\")\n .description(\"Generate a default mcp-policy.json\")\n .option(\"-o, --output <path>\", \"Output path for generated policy\", \"mcp-policy.json\")\n .option(\"-f, --force\", \"Overwrite existing file\")\n .action(async (options: { output: string; force?: boolean }) => {\n await runInit(options.output, Boolean(options.force));\n });\n\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : \"Unknown CLI error\";\n console.error(chalk.red(`CRITICAL: ${message}`));\n process.exitCode = 1;\n});\n"]}
@@ -29,11 +29,12 @@ export interface JsonRpcErrorResponse {
29
29
  id: JsonRpcId;
30
30
  error: JsonRpcError;
31
31
  }
32
+ export type GuardianViolationCode = "PERMISSION_DENIED" | "REQUIRES_APPROVAL";
32
33
  /**
33
34
  * Security violation metadata emitted by the guardian engine.
34
35
  */
35
36
  export interface GuardianViolation {
36
- code: "PERMISSION_DENIED";
37
+ code: GuardianViolationCode;
37
38
  reason: string;
38
39
  method: string;
39
40
  toolName?: string;
@@ -51,6 +52,7 @@ export interface ValidationResult {
51
52
  */
52
53
  export interface MiddlewareDecision {
53
54
  allowed: boolean;
55
+ code?: GuardianViolationCode;
54
56
  reason?: string;
55
57
  }
56
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../../src/core/interceptor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../../src/core/interceptor.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,19 @@ import { RateLimiter } from "../security/rate-limiter.js";
7
7
  * Constant JSON-RPC code used for permission-denied failures.
8
8
  */
9
9
  const PERMISSION_DENIED_NUMERIC_CODE = -32001;
10
+ const REQUIRES_APPROVAL_NUMERIC_CODE = -32002;
11
+ const PATH_ARG_KEYS = new Set([
12
+ "path",
13
+ "paths",
14
+ "root",
15
+ "roots",
16
+ "directory",
17
+ "directories",
18
+ "dir",
19
+ "cwd",
20
+ "filepath",
21
+ "file_path"
22
+ ]);
10
23
  /**
11
24
  * Detects whether a request looks like an MCP tool call.
12
25
  */
@@ -56,13 +69,13 @@ function isJsonRpcErrorResponse(response) {
56
69
  /**
57
70
  * Creates a standardized JSON-RPC permission error response.
58
71
  */
59
- function createPermissionDeniedError(request, reason, toolName) {
72
+ function createPolicyError(request, code, reason, toolName) {
60
73
  return {
61
74
  jsonrpc: "2.0",
62
75
  id: request.id ?? null,
63
76
  error: {
64
- code: PERMISSION_DENIED_NUMERIC_CODE,
65
- message: "PERMISSION_DENIED",
77
+ code: code === "REQUIRES_APPROVAL" ? REQUIRES_APPROVAL_NUMERIC_CODE : PERMISSION_DENIED_NUMERIC_CODE,
78
+ message: code,
66
79
  data: {
67
80
  reason,
68
81
  method: request.method,
@@ -71,6 +84,38 @@ function createPermissionDeniedError(request, reason, toolName) {
71
84
  }
72
85
  };
73
86
  }
87
+ function normalizePolicyPath(value) {
88
+ let normalized = value.trim().replaceAll("\\", "/").toLowerCase();
89
+ if (normalized.length > 1 && normalized.endsWith("/")) {
90
+ normalized = normalized.slice(0, -1);
91
+ }
92
+ return normalized;
93
+ }
94
+ function matchesBlockedPath(candidatePath, blockedPath) {
95
+ if (blockedPath === "/") {
96
+ return candidatePath.startsWith("/");
97
+ }
98
+ return candidatePath === blockedPath || candidatePath.startsWith(`${blockedPath}/`);
99
+ }
100
+ function collectCandidatePaths(payload, parentKey) {
101
+ if (typeof payload === "string") {
102
+ if (parentKey && PATH_ARG_KEYS.has(parentKey)) {
103
+ return [payload];
104
+ }
105
+ return [];
106
+ }
107
+ if (Array.isArray(payload)) {
108
+ return payload.flatMap((entry) => collectCandidatePaths(entry, parentKey));
109
+ }
110
+ if (!payload || typeof payload !== "object") {
111
+ return [];
112
+ }
113
+ const result = [];
114
+ for (const [key, value] of Object.entries(payload)) {
115
+ result.push(...collectCandidatePaths(value, key.toLowerCase()));
116
+ }
117
+ return result;
118
+ }
74
119
  /**
75
120
  * Middleware that enforces `allowedTools` policy for tool calls.
76
121
  */
@@ -116,6 +161,50 @@ async function enforceInjectionPolicy(context, next, keywords) {
116
161
  }
117
162
  return next();
118
163
  }
164
+ /**
165
+ * Middleware that denies tool calls targeting blocked filesystem paths.
166
+ */
167
+ async function enforceRestrictedPaths(context, next) {
168
+ if (!isToolCallRequest(context.request)) {
169
+ return next();
170
+ }
171
+ const blockedPaths = context.policy.restrictedPaths
172
+ .filter((entry) => entry.mode === "blocked")
173
+ .map((entry) => normalizePolicyPath(entry.path));
174
+ if (blockedPaths.length === 0) {
175
+ return next();
176
+ }
177
+ const candidatePaths = collectCandidatePaths(context.toolArgs)
178
+ .map((value) => normalizePolicyPath(value))
179
+ .filter((value) => value.length > 0);
180
+ for (const candidatePath of candidatePaths) {
181
+ const matchedBlockedPath = blockedPaths.find((blockedPath) => matchesBlockedPath(candidatePath, blockedPath));
182
+ if (matchedBlockedPath) {
183
+ return {
184
+ allowed: false,
185
+ code: "PERMISSION_DENIED",
186
+ reason: `Tool arguments include restricted path '${candidatePath}' blocked by policy.`
187
+ };
188
+ }
189
+ }
190
+ return next();
191
+ }
192
+ /**
193
+ * Middleware that marks tool calls as requiring human approval.
194
+ */
195
+ async function enforceApprovalRequired(context, next) {
196
+ if (!isToolCallRequest(context.request)) {
197
+ return next();
198
+ }
199
+ if (!context.policy.approvalRequired) {
200
+ return next();
201
+ }
202
+ return {
203
+ allowed: false,
204
+ code: "REQUIRES_APPROVAL",
205
+ reason: "Human approval is required by policy before executing this tool call."
206
+ };
207
+ }
119
208
  /**
120
209
  * Middleware that blocks tools with an open circuit.
121
210
  */
@@ -184,6 +273,8 @@ export class McpGuardian {
184
273
  this.nowProvider = options.nowProvider ?? (() => Date.now());
185
274
  this.middlewares = [
186
275
  enforceAllowedTools,
276
+ enforceRestrictedPaths,
277
+ enforceApprovalRequired,
187
278
  async (context, next) => enforceRateLimit(context, next, this.rateLimiter, this.nowProvider),
188
279
  async (context, next) => enforceInjectionPolicy(context, next, this.injectionKeywords),
189
280
  async (context, next) => enforceCircuitState(context, next, this.circuitBreaker)
@@ -213,9 +304,13 @@ export class McpGuardian {
213
304
  if (decision.allowed) {
214
305
  return { isAllowed: true };
215
306
  }
307
+ const violationCode = decision.code ?? "PERMISSION_DENIED";
216
308
  const violation = {
217
- code: "PERMISSION_DENIED",
218
- reason: decision.reason ?? "Request violated guardian policy.",
309
+ code: violationCode,
310
+ reason: decision.reason ??
311
+ (violationCode === "REQUIRES_APPROVAL"
312
+ ? "Human approval is required before executing this request."
313
+ : "Request violated guardian policy."),
219
314
  method: request.method,
220
315
  ...(toolName ? { toolName } : {})
221
316
  };
@@ -229,7 +324,7 @@ export class McpGuardian {
229
324
  return {
230
325
  isAllowed: false,
231
326
  violation,
232
- error: createPermissionDeniedError(request, violation.reason, toolName)
327
+ error: createPolicyError(request, violation.code, violation.reason, toolName)
233
328
  };
234
329
  }
235
330
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/core/interceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { GuardianPolicySchema, type GuardianPolicy } from \"../types/policy.js\";\nimport {\n CircuitBreaker,\n type CircuitBreakerOptions\n} from \"../security/circuit-breaker.js\";\nimport {\n DEFAULT_INJECTION_KEYWORDS,\n scanForPromptInjection\n} from \"../security/injection-scanner.js\";\nimport { redactSensitiveData } from \"../security/pii-redactor.js\";\nimport { RateLimiter } from \"../security/rate-limiter.js\";\n\n/**\n * JSON-RPC request identifier shape.\n */\nexport type JsonRpcId = string | number | null;\n\n/**\n * Minimal JSON-RPC 2.0 request payload used by MCP transports.\n */\nexport interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: JsonRpcId;\n method: string;\n params?: unknown;\n}\n\n/**\n * Standardized JSON-RPC 2.0 error object.\n */\nexport interface JsonRpcError {\n code: number;\n message: string;\n data?: Record<string, unknown>;\n}\n\n/**\n * Standardized JSON-RPC 2.0 error response payload.\n */\nexport interface JsonRpcErrorResponse {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n error: JsonRpcError;\n}\n\n/**\n * Security violation metadata emitted by the guardian engine.\n */\nexport interface GuardianViolation {\n code: \"PERMISSION_DENIED\";\n reason: string;\n method: string;\n toolName?: string;\n}\n\n/**\n * Validation result produced by the guardian engine.\n */\nexport interface ValidationResult {\n isAllowed: boolean;\n error?: JsonRpcErrorResponse;\n violation?: GuardianViolation;\n}\n\n/**\n * Decision object returned by guardian middleware.\n */\nexport interface MiddlewareDecision {\n allowed: boolean;\n reason?: string;\n}\n\n/**\n * Execution context provided to each middleware stage.\n */\nexport interface GuardianContext {\n readonly request: JsonRpcRequest;\n readonly policy: GuardianPolicy;\n readonly isDryRun: boolean;\n readonly toolName?: string;\n readonly toolArgs?: unknown;\n}\n\n/**\n * Async middleware function signature used by the guardian engine.\n */\nexport type GuardianMiddleware = (\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n) => Promise<MiddlewareDecision>;\n\n/**\n * Logging function for policy violations.\n */\nexport type GuardianLogger = (violation: GuardianViolation) => void;\n\n/**\n * Configuration object for McpGuardian construction.\n */\nexport interface McpGuardianOptions {\n dryRun?: boolean;\n logger?: GuardianLogger;\n injectionKeywords?: readonly string[];\n circuitBreaker?: CircuitBreakerOptions;\n redactToolOutputs?: boolean;\n nowProvider?: () => number;\n}\n\n/**\n * Constant JSON-RPC code used for permission-denied failures.\n */\nconst PERMISSION_DENIED_NUMERIC_CODE = -32001;\n\n/**\n * Detects whether a request looks like an MCP tool call.\n */\nfunction isToolCallRequest(request: JsonRpcRequest): boolean {\n return request.method === \"tools/call\";\n}\n\n/**\n * Best-effort extraction of tool name from standard MCP params.\n */\nfunction extractToolName(params: unknown): string | undefined {\n if (!params || typeof params !== \"object\") {\n return undefined;\n }\n\n const payload = params as Record<string, unknown>;\n const candidate = payload.name ?? payload.toolName;\n return typeof candidate === \"string\" && candidate.length > 0 ? candidate : undefined;\n}\n\n/**\n * Best-effort extraction of tool arguments from standard MCP params.\n */\nfunction extractToolArgs(params: unknown): unknown {\n if (!params || typeof params !== \"object\") {\n return undefined;\n }\n\n const payload = params as Record<string, unknown>;\n if (\"arguments\" in payload) {\n return payload.arguments;\n }\n\n if (\"args\" in payload) {\n return payload.args;\n }\n\n if (\"input\" in payload) {\n return payload.input;\n }\n\n return undefined;\n}\n\n/**\n * Type guard for JSON-RPC error responses.\n */\nfunction isJsonRpcErrorResponse(response: unknown): response is JsonRpcErrorResponse {\n if (!response || typeof response !== \"object\") {\n return false;\n }\n\n const payload = response as Record<string, unknown>;\n return payload.jsonrpc === \"2.0\" && typeof payload.error === \"object\";\n}\n\n/**\n * Creates a standardized JSON-RPC permission error response.\n */\nfunction createPermissionDeniedError(\n request: JsonRpcRequest,\n reason: string,\n toolName?: string\n): JsonRpcErrorResponse {\n return {\n jsonrpc: \"2.0\",\n id: request.id ?? null,\n error: {\n code: PERMISSION_DENIED_NUMERIC_CODE,\n message: \"PERMISSION_DENIED\",\n data: {\n reason,\n method: request.method,\n toolName\n }\n }\n };\n}\n\n/**\n * Middleware that enforces `allowedTools` policy for tool calls.\n */\nasync function enforceAllowedTools(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n if (!context.toolName) {\n return {\n allowed: false,\n reason: \"Tool call did not include a valid tool name.\"\n };\n }\n\n const toolName = context.toolName;\n\n const isAllowed = context.policy.allowedTools.some((rule) => {\n if (typeof rule === \"string\") {\n return rule === toolName;\n }\n\n return rule.test(toolName);\n });\n\n if (!isAllowed) {\n return {\n allowed: false,\n reason: `Tool '${context.toolName}' is not allowed by policy.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that rejects requests when prompt-injection signatures are present.\n */\nasync function enforceInjectionPolicy(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n keywords: readonly string[]\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n const scanResult = scanForPromptInjection(context.toolArgs, keywords);\n if (scanResult.detected) {\n const signatures = scanResult.matchedKeywords.join(\", \");\n return {\n allowed: false,\n reason: `Prompt injection signature detected: ${signatures}.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that blocks tools with an open circuit.\n */\nasync function enforceCircuitState(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n circuitBreaker: CircuitBreaker\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request) || !context.toolName) {\n return next();\n }\n\n const decision = circuitBreaker.canExecute(context.toolName);\n if (!decision.allowed) {\n const retryMs = Math.max(0, decision.retryAfterMs ?? 0);\n const retrySeconds = Math.ceil(retryMs / 1000);\n return {\n allowed: false,\n reason: `Circuit breaker open for tool '${context.toolName}'. Retry in ${retrySeconds}s.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that enforces global max-calls-per-minute policy.\n */\nasync function enforceRateLimit(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n rateLimiter: RateLimiter,\n nowProvider: () => number\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n const decision = rateLimiter.consume(nowProvider());\n if (!decision.allowed) {\n const retryMs = Math.max(0, decision.retryAfterMs ?? 0);\n const retrySeconds = Math.ceil(retryMs / 1000);\n return {\n allowed: false,\n reason: `Rate limit exceeded. Retry in ${retrySeconds}s.`\n };\n }\n\n return next();\n}\n\n/**\n * Core policy guard for intercepting JSON-RPC tool calls.\n *\n * This class can be used as a standalone validator (`validateRequest`) or as a\n * transport wrapper through `wrapHandler` to gate downstream request handlers.\n */\nexport class McpGuardian {\n private readonly policy: GuardianPolicy;\n\n private readonly isDryRun: boolean;\n\n private readonly logger: GuardianLogger;\n\n private readonly middlewares: GuardianMiddleware[];\n\n private readonly circuitBreaker: CircuitBreaker;\n\n private readonly injectionKeywords: readonly string[];\n\n private readonly redactToolOutputs: boolean;\n\n private readonly rateLimiter: RateLimiter;\n\n private readonly nowProvider: () => number;\n\n /**\n * Creates a guardian instance with policy and optional runtime controls.\n */\n public constructor(policy: GuardianPolicy, options: McpGuardianOptions = {}) {\n this.policy = GuardianPolicySchema.parse(policy);\n this.isDryRun = options.dryRun ?? false;\n this.logger = options.logger ?? ((violation) => console.warn(\"[mcp-warden]\", violation));\n this.circuitBreaker = new CircuitBreaker(options.circuitBreaker);\n this.injectionKeywords = options.injectionKeywords ?? DEFAULT_INJECTION_KEYWORDS;\n this.redactToolOutputs = options.redactToolOutputs ?? true;\n this.rateLimiter = new RateLimiter({\n maxCallsPerMinute: this.policy.maxCallsPerMinute\n });\n this.nowProvider = options.nowProvider ?? (() => Date.now());\n\n this.middlewares = [\n enforceAllowedTools,\n async (context, next) =>\n enforceRateLimit(context, next, this.rateLimiter, this.nowProvider),\n async (context, next) => enforceInjectionPolicy(context, next, this.injectionKeywords),\n async (context, next) => enforceCircuitState(context, next, this.circuitBreaker)\n ];\n }\n\n /**\n * Registers additional middleware checks that run after the built-in policy check.\n */\n public use(middleware: GuardianMiddleware): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n /**\n * Validates an incoming JSON-RPC request against the configured guardrail chain.\n */\n public async validateRequest(request: JsonRpcRequest): Promise<ValidationResult> {\n const toolName = extractToolName(request.params);\n const toolArgs = extractToolArgs(request.params);\n const context: GuardianContext = {\n request,\n policy: this.policy,\n isDryRun: this.isDryRun,\n ...(toolName ? { toolName } : {}),\n ...(toolArgs !== undefined ? { toolArgs } : {})\n };\n\n const decision = await this.runMiddlewares(context);\n if (decision.allowed) {\n return { isAllowed: true };\n }\n\n const violation: GuardianViolation = {\n code: \"PERMISSION_DENIED\",\n reason: decision.reason ?? \"Request violated guardian policy.\",\n method: request.method,\n ...(toolName ? { toolName } : {})\n };\n\n this.logger(violation);\n if (this.isDryRun) {\n return {\n isAllowed: true,\n violation\n };\n }\n\n return {\n isAllowed: false,\n violation,\n error: createPermissionDeniedError(request, violation.reason, toolName)\n };\n }\n\n /**\n * Wraps a JSON-RPC handler and blocks requests that violate policy.\n */\n public wrapHandler<TResponse>(\n handler: (request: JsonRpcRequest) => Promise<TResponse | JsonRpcErrorResponse>\n ): (request: JsonRpcRequest) => Promise<TResponse | JsonRpcErrorResponse> {\n return async (request: JsonRpcRequest): Promise<TResponse | JsonRpcErrorResponse> => {\n const toolName = extractToolName(request.params);\n const shouldTrackCircuit = isToolCallRequest(request) && !!toolName;\n\n const result = await this.validateRequest(request);\n if (!result.isAllowed && result.error) {\n return result.error;\n }\n\n try {\n const response = await handler(request);\n\n if (shouldTrackCircuit && toolName) {\n if (isJsonRpcErrorResponse(response)) {\n this.circuitBreaker.recordFailure(toolName);\n } else {\n this.circuitBreaker.recordSuccess(toolName);\n }\n }\n\n if (!this.redactToolOutputs || !isToolCallRequest(request)) {\n return response;\n }\n\n return redactSensitiveData(response);\n } catch (error) {\n if (shouldTrackCircuit && toolName) {\n this.circuitBreaker.recordFailure(toolName);\n }\n\n throw error;\n }\n };\n }\n\n /**\n * Runs middleware chain using a deterministic Koa-style composition model.\n */\n private async runMiddlewares(context: GuardianContext): Promise<MiddlewareDecision> {\n const middlewares = this.middlewares;\n\n const dispatch = async (index: number): Promise<MiddlewareDecision> => {\n if (index >= middlewares.length) {\n return { allowed: true };\n }\n\n const middleware = middlewares[index];\n if (!middleware) {\n return { allowed: true };\n }\n\n return middleware(context, () => dispatch(index + 1));\n };\n\n return dispatch(0);\n }\n}"]}
1
+ {"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/core/interceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { GuardianPolicySchema, type GuardianPolicy } from \"../types/policy.js\";\nimport {\n CircuitBreaker,\n type CircuitBreakerOptions\n} from \"../security/circuit-breaker.js\";\nimport {\n DEFAULT_INJECTION_KEYWORDS,\n scanForPromptInjection\n} from \"../security/injection-scanner.js\";\nimport { redactSensitiveData } from \"../security/pii-redactor.js\";\nimport { RateLimiter } from \"../security/rate-limiter.js\";\n\n/**\n * JSON-RPC request identifier shape.\n */\nexport type JsonRpcId = string | number | null;\n\n/**\n * Minimal JSON-RPC 2.0 request payload used by MCP transports.\n */\nexport interface JsonRpcRequest {\n jsonrpc: \"2.0\";\n id?: JsonRpcId;\n method: string;\n params?: unknown;\n}\n\n/**\n * Standardized JSON-RPC 2.0 error object.\n */\nexport interface JsonRpcError {\n code: number;\n message: string;\n data?: Record<string, unknown>;\n}\n\n/**\n * Standardized JSON-RPC 2.0 error response payload.\n */\nexport interface JsonRpcErrorResponse {\n jsonrpc: \"2.0\";\n id: JsonRpcId;\n error: JsonRpcError;\n}\n\nexport type GuardianViolationCode = \"PERMISSION_DENIED\" | \"REQUIRES_APPROVAL\";\n\n/**\n * Security violation metadata emitted by the guardian engine.\n */\nexport interface GuardianViolation {\n code: GuardianViolationCode;\n reason: string;\n method: string;\n toolName?: string;\n}\n\n/**\n * Validation result produced by the guardian engine.\n */\nexport interface ValidationResult {\n isAllowed: boolean;\n error?: JsonRpcErrorResponse;\n violation?: GuardianViolation;\n}\n\n/**\n * Decision object returned by guardian middleware.\n */\nexport interface MiddlewareDecision {\n allowed: boolean;\n code?: GuardianViolationCode;\n reason?: string;\n}\n\n/**\n * Execution context provided to each middleware stage.\n */\nexport interface GuardianContext {\n readonly request: JsonRpcRequest;\n readonly policy: GuardianPolicy;\n readonly isDryRun: boolean;\n readonly toolName?: string;\n readonly toolArgs?: unknown;\n}\n\n/**\n * Async middleware function signature used by the guardian engine.\n */\nexport type GuardianMiddleware = (\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n) => Promise<MiddlewareDecision>;\n\n/**\n * Logging function for policy violations.\n */\nexport type GuardianLogger = (violation: GuardianViolation) => void;\n\n/**\n * Configuration object for McpGuardian construction.\n */\nexport interface McpGuardianOptions {\n dryRun?: boolean;\n logger?: GuardianLogger;\n injectionKeywords?: readonly string[];\n circuitBreaker?: CircuitBreakerOptions;\n redactToolOutputs?: boolean;\n nowProvider?: () => number;\n}\n\n/**\n * Constant JSON-RPC code used for permission-denied failures.\n */\nconst PERMISSION_DENIED_NUMERIC_CODE = -32001;\nconst REQUIRES_APPROVAL_NUMERIC_CODE = -32002;\n\nconst PATH_ARG_KEYS = new Set([\n \"path\",\n \"paths\",\n \"root\",\n \"roots\",\n \"directory\",\n \"directories\",\n \"dir\",\n \"cwd\",\n \"filepath\",\n \"file_path\"\n]);\n\n/**\n * Detects whether a request looks like an MCP tool call.\n */\nfunction isToolCallRequest(request: JsonRpcRequest): boolean {\n return request.method === \"tools/call\";\n}\n\n/**\n * Best-effort extraction of tool name from standard MCP params.\n */\nfunction extractToolName(params: unknown): string | undefined {\n if (!params || typeof params !== \"object\") {\n return undefined;\n }\n\n const payload = params as Record<string, unknown>;\n const candidate = payload.name ?? payload.toolName;\n return typeof candidate === \"string\" && candidate.length > 0 ? candidate : undefined;\n}\n\n/**\n * Best-effort extraction of tool arguments from standard MCP params.\n */\nfunction extractToolArgs(params: unknown): unknown {\n if (!params || typeof params !== \"object\") {\n return undefined;\n }\n\n const payload = params as Record<string, unknown>;\n if (\"arguments\" in payload) {\n return payload.arguments;\n }\n\n if (\"args\" in payload) {\n return payload.args;\n }\n\n if (\"input\" in payload) {\n return payload.input;\n }\n\n return undefined;\n}\n\n/**\n * Type guard for JSON-RPC error responses.\n */\nfunction isJsonRpcErrorResponse(response: unknown): response is JsonRpcErrorResponse {\n if (!response || typeof response !== \"object\") {\n return false;\n }\n\n const payload = response as Record<string, unknown>;\n return payload.jsonrpc === \"2.0\" && typeof payload.error === \"object\";\n}\n\n/**\n * Creates a standardized JSON-RPC permission error response.\n */\nfunction createPolicyError(\n request: JsonRpcRequest,\n code: GuardianViolationCode,\n reason: string,\n toolName?: string\n): JsonRpcErrorResponse {\n return {\n jsonrpc: \"2.0\",\n id: request.id ?? null,\n error: {\n code: code === \"REQUIRES_APPROVAL\" ? REQUIRES_APPROVAL_NUMERIC_CODE : PERMISSION_DENIED_NUMERIC_CODE,\n message: code,\n data: {\n reason,\n method: request.method,\n toolName\n }\n }\n };\n}\n\nfunction normalizePolicyPath(value: string): string {\n let normalized = value.trim().replaceAll(\"\\\\\", \"/\").toLowerCase();\n if (normalized.length > 1 && normalized.endsWith(\"/\")) {\n normalized = normalized.slice(0, -1);\n }\n\n return normalized;\n}\n\nfunction matchesBlockedPath(candidatePath: string, blockedPath: string): boolean {\n if (blockedPath === \"/\") {\n return candidatePath.startsWith(\"/\");\n }\n\n return candidatePath === blockedPath || candidatePath.startsWith(`${blockedPath}/`);\n}\n\nfunction collectCandidatePaths(payload: unknown, parentKey?: string): string[] {\n if (typeof payload === \"string\") {\n if (parentKey && PATH_ARG_KEYS.has(parentKey)) {\n return [payload];\n }\n\n return [];\n }\n\n if (Array.isArray(payload)) {\n return payload.flatMap((entry) => collectCandidatePaths(entry, parentKey));\n }\n\n if (!payload || typeof payload !== \"object\") {\n return [];\n }\n\n const result: string[] = [];\n for (const [key, value] of Object.entries(payload as Record<string, unknown>)) {\n result.push(...collectCandidatePaths(value, key.toLowerCase()));\n }\n\n return result;\n}\n\n/**\n * Middleware that enforces `allowedTools` policy for tool calls.\n */\nasync function enforceAllowedTools(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n if (!context.toolName) {\n return {\n allowed: false,\n reason: \"Tool call did not include a valid tool name.\"\n };\n }\n\n const toolName = context.toolName;\n\n const isAllowed = context.policy.allowedTools.some((rule) => {\n if (typeof rule === \"string\") {\n return rule === toolName;\n }\n\n return rule.test(toolName);\n });\n\n if (!isAllowed) {\n return {\n allowed: false,\n reason: `Tool '${context.toolName}' is not allowed by policy.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that rejects requests when prompt-injection signatures are present.\n */\nasync function enforceInjectionPolicy(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n keywords: readonly string[]\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n const scanResult = scanForPromptInjection(context.toolArgs, keywords);\n if (scanResult.detected) {\n const signatures = scanResult.matchedKeywords.join(\", \");\n return {\n allowed: false,\n reason: `Prompt injection signature detected: ${signatures}.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that denies tool calls targeting blocked filesystem paths.\n */\nasync function enforceRestrictedPaths(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n const blockedPaths = context.policy.restrictedPaths\n .filter((entry) => entry.mode === \"blocked\")\n .map((entry) => normalizePolicyPath(entry.path));\n\n if (blockedPaths.length === 0) {\n return next();\n }\n\n const candidatePaths = collectCandidatePaths(context.toolArgs)\n .map((value) => normalizePolicyPath(value))\n .filter((value) => value.length > 0);\n\n for (const candidatePath of candidatePaths) {\n const matchedBlockedPath = blockedPaths.find((blockedPath) =>\n matchesBlockedPath(candidatePath, blockedPath)\n );\n\n if (matchedBlockedPath) {\n return {\n allowed: false,\n code: \"PERMISSION_DENIED\",\n reason: `Tool arguments include restricted path '${candidatePath}' blocked by policy.`\n };\n }\n }\n\n return next();\n}\n\n/**\n * Middleware that marks tool calls as requiring human approval.\n */\nasync function enforceApprovalRequired(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n if (!context.policy.approvalRequired) {\n return next();\n }\n\n return {\n allowed: false,\n code: \"REQUIRES_APPROVAL\",\n reason: \"Human approval is required by policy before executing this tool call.\"\n };\n}\n\n/**\n * Middleware that blocks tools with an open circuit.\n */\nasync function enforceCircuitState(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n circuitBreaker: CircuitBreaker\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request) || !context.toolName) {\n return next();\n }\n\n const decision = circuitBreaker.canExecute(context.toolName);\n if (!decision.allowed) {\n const retryMs = Math.max(0, decision.retryAfterMs ?? 0);\n const retrySeconds = Math.ceil(retryMs / 1000);\n return {\n allowed: false,\n reason: `Circuit breaker open for tool '${context.toolName}'. Retry in ${retrySeconds}s.`\n };\n }\n\n return next();\n}\n\n/**\n * Middleware that enforces global max-calls-per-minute policy.\n */\nasync function enforceRateLimit(\n context: GuardianContext,\n next: () => Promise<MiddlewareDecision>,\n rateLimiter: RateLimiter,\n nowProvider: () => number\n): Promise<MiddlewareDecision> {\n if (!isToolCallRequest(context.request)) {\n return next();\n }\n\n const decision = rateLimiter.consume(nowProvider());\n if (!decision.allowed) {\n const retryMs = Math.max(0, decision.retryAfterMs ?? 0);\n const retrySeconds = Math.ceil(retryMs / 1000);\n return {\n allowed: false,\n reason: `Rate limit exceeded. Retry in ${retrySeconds}s.`\n };\n }\n\n return next();\n}\n\n/**\n * Core policy guard for intercepting JSON-RPC tool calls.\n *\n * This class can be used as a standalone validator (`validateRequest`) or as a\n * transport wrapper through `wrapHandler` to gate downstream request handlers.\n */\nexport class McpGuardian {\n private readonly policy: GuardianPolicy;\n\n private readonly isDryRun: boolean;\n\n private readonly logger: GuardianLogger;\n\n private readonly middlewares: GuardianMiddleware[];\n\n private readonly circuitBreaker: CircuitBreaker;\n\n private readonly injectionKeywords: readonly string[];\n\n private readonly redactToolOutputs: boolean;\n\n private readonly rateLimiter: RateLimiter;\n\n private readonly nowProvider: () => number;\n\n /**\n * Creates a guardian instance with policy and optional runtime controls.\n */\n public constructor(policy: GuardianPolicy, options: McpGuardianOptions = {}) {\n this.policy = GuardianPolicySchema.parse(policy);\n this.isDryRun = options.dryRun ?? false;\n this.logger = options.logger ?? ((violation) => console.warn(\"[mcp-warden]\", violation));\n this.circuitBreaker = new CircuitBreaker(options.circuitBreaker);\n this.injectionKeywords = options.injectionKeywords ?? DEFAULT_INJECTION_KEYWORDS;\n this.redactToolOutputs = options.redactToolOutputs ?? true;\n this.rateLimiter = new RateLimiter({\n maxCallsPerMinute: this.policy.maxCallsPerMinute\n });\n this.nowProvider = options.nowProvider ?? (() => Date.now());\n\n this.middlewares = [\n enforceAllowedTools,\n enforceRestrictedPaths,\n enforceApprovalRequired,\n async (context, next) =>\n enforceRateLimit(context, next, this.rateLimiter, this.nowProvider),\n async (context, next) => enforceInjectionPolicy(context, next, this.injectionKeywords),\n async (context, next) => enforceCircuitState(context, next, this.circuitBreaker)\n ];\n }\n\n /**\n * Registers additional middleware checks that run after the built-in policy check.\n */\n public use(middleware: GuardianMiddleware): this {\n this.middlewares.push(middleware);\n return this;\n }\n\n /**\n * Validates an incoming JSON-RPC request against the configured guardrail chain.\n */\n public async validateRequest(request: JsonRpcRequest): Promise<ValidationResult> {\n const toolName = extractToolName(request.params);\n const toolArgs = extractToolArgs(request.params);\n const context: GuardianContext = {\n request,\n policy: this.policy,\n isDryRun: this.isDryRun,\n ...(toolName ? { toolName } : {}),\n ...(toolArgs !== undefined ? { toolArgs } : {})\n };\n\n const decision = await this.runMiddlewares(context);\n if (decision.allowed) {\n return { isAllowed: true };\n }\n\n const violationCode = decision.code ?? \"PERMISSION_DENIED\";\n const violation: GuardianViolation = {\n code: violationCode,\n reason:\n decision.reason ??\n (violationCode === \"REQUIRES_APPROVAL\"\n ? \"Human approval is required before executing this request.\"\n : \"Request violated guardian policy.\"),\n method: request.method,\n ...(toolName ? { toolName } : {})\n };\n\n this.logger(violation);\n if (this.isDryRun) {\n return {\n isAllowed: true,\n violation\n };\n }\n\n return {\n isAllowed: false,\n violation,\n error: createPolicyError(request, violation.code, violation.reason, toolName)\n };\n }\n\n /**\n * Wraps a JSON-RPC handler and blocks requests that violate policy.\n */\n public wrapHandler<TResponse>(\n handler: (request: JsonRpcRequest) => Promise<TResponse | JsonRpcErrorResponse>\n ): (request: JsonRpcRequest) => Promise<TResponse | JsonRpcErrorResponse> {\n return async (request: JsonRpcRequest): Promise<TResponse | JsonRpcErrorResponse> => {\n const toolName = extractToolName(request.params);\n const shouldTrackCircuit = isToolCallRequest(request) && !!toolName;\n\n const result = await this.validateRequest(request);\n if (!result.isAllowed && result.error) {\n return result.error;\n }\n\n try {\n const response = await handler(request);\n\n if (shouldTrackCircuit && toolName) {\n if (isJsonRpcErrorResponse(response)) {\n this.circuitBreaker.recordFailure(toolName);\n } else {\n this.circuitBreaker.recordSuccess(toolName);\n }\n }\n\n if (!this.redactToolOutputs || !isToolCallRequest(request)) {\n return response;\n }\n\n return redactSensitiveData(response);\n } catch (error) {\n if (shouldTrackCircuit && toolName) {\n this.circuitBreaker.recordFailure(toolName);\n }\n\n throw error;\n }\n };\n }\n\n /**\n * Runs middleware chain using a deterministic Koa-style composition model.\n */\n private async runMiddlewares(context: GuardianContext): Promise<MiddlewareDecision> {\n const middlewares = this.middlewares;\n\n const dispatch = async (index: number): Promise<MiddlewareDecision> => {\n if (index >= middlewares.length) {\n return { allowed: true };\n }\n\n const middleware = middlewares[index];\n if (!middleware) {\n return { allowed: true };\n }\n\n return middleware(context, () => dispatch(index + 1));\n };\n\n return dispatch(0);\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-warden",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Policy enforcement and guardrails for MCP-compatible tool execution.",
5
5
  "type": "module",
6
6
  "license": "MIT",