mcp-warden 0.1.0
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/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +207 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/interceptor.d.ts +122 -0
- package/dist/core/interceptor.d.ts.map +1 -0
- package/dist/core/interceptor.js +287 -0
- package/dist/core/interceptor.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/security/circuit-breaker.d.ts +50 -0
- package/dist/security/circuit-breaker.d.ts.map +1 -0
- package/dist/security/circuit-breaker.js +79 -0
- package/dist/security/circuit-breaker.js.map +1 -0
- package/dist/security/injection-scanner.d.ts +16 -0
- package/dist/security/injection-scanner.d.ts.map +1 -0
- package/dist/security/injection-scanner.js +52 -0
- package/dist/security/injection-scanner.js.map +1 -0
- package/dist/security/pii-redactor.d.ts +20 -0
- package/dist/security/pii-redactor.d.ts.map +1 -0
- package/dist/security/pii-redactor.js +62 -0
- package/dist/security/pii-redactor.js.map +1 -0
- package/dist/security/rate-limiter.d.ts +33 -0
- package/dist/security/rate-limiter.d.ts.map +1 -0
- package/dist/security/rate-limiter.js +43 -0
- package/dist/security/rate-limiter.js.map +1 -0
- package/dist/types/policy.d.ts +94 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +49 -0
- package/dist/types/policy.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder used when sensitive values are removed.
|
|
3
|
+
*/
|
|
4
|
+
export const REDACTION_TOKEN = "[REDACTED]";
|
|
5
|
+
/**
|
|
6
|
+
* Finds common email address patterns.
|
|
7
|
+
*/
|
|
8
|
+
const EMAIL_REGEX = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
9
|
+
/**
|
|
10
|
+
* Finds key-like secrets with common prefixes such as sk- and key-.
|
|
11
|
+
*/
|
|
12
|
+
const API_KEY_REGEX = /\b(?:sk|key|api|token)-[A-Z0-9_-]{8,}\b/gi;
|
|
13
|
+
/**
|
|
14
|
+
* Finds IPv4 addresses and excludes impossible octets.
|
|
15
|
+
*/
|
|
16
|
+
const IPV4_REGEX = /\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b/g;
|
|
17
|
+
/**
|
|
18
|
+
* Redacts sensitive values from a plain string.
|
|
19
|
+
*/
|
|
20
|
+
export function redactSensitiveText(input) {
|
|
21
|
+
let redactedCount = 0;
|
|
22
|
+
const replacer = () => {
|
|
23
|
+
redactedCount += 1;
|
|
24
|
+
return REDACTION_TOKEN;
|
|
25
|
+
};
|
|
26
|
+
let value = input.replace(EMAIL_REGEX, replacer);
|
|
27
|
+
value = value.replace(API_KEY_REGEX, replacer);
|
|
28
|
+
value = value.replace(IPV4_REGEX, replacer);
|
|
29
|
+
return {
|
|
30
|
+
value,
|
|
31
|
+
redactedCount
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Recursively redacts sensitive content from arbitrary JSON-like payloads.
|
|
36
|
+
*/
|
|
37
|
+
export function redactSensitiveData(value) {
|
|
38
|
+
const seen = new WeakMap();
|
|
39
|
+
const transform = (candidate) => {
|
|
40
|
+
if (typeof candidate === "string") {
|
|
41
|
+
return redactSensitiveText(candidate).value;
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(candidate)) {
|
|
44
|
+
return candidate.map((entry) => transform(entry));
|
|
45
|
+
}
|
|
46
|
+
if (!candidate || typeof candidate !== "object") {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
if (seen.has(candidate)) {
|
|
50
|
+
return seen.get(candidate);
|
|
51
|
+
}
|
|
52
|
+
const inputRecord = candidate;
|
|
53
|
+
const outputRecord = {};
|
|
54
|
+
seen.set(candidate, outputRecord);
|
|
55
|
+
for (const [key, entry] of Object.entries(inputRecord)) {
|
|
56
|
+
outputRecord[key] = transform(entry);
|
|
57
|
+
}
|
|
58
|
+
return outputRecord;
|
|
59
|
+
};
|
|
60
|
+
return transform(value);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=pii-redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pii-redactor.js","sourceRoot":"","sources":["../../src/security/pii-redactor.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,GAAG,6CAA6C,CAAC;AAElE;;GAEG;AACH,MAAM,aAAa,GAAG,2CAA2C,CAAC;AAElE;;GAEG;AACH,MAAM,UAAU,GACd,sFAAsF,CAAC;AAUzF;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,MAAM,QAAQ,GAAG,GAAW,EAAE;QAC5B,aAAa,IAAI,CAAC,CAAC;QACnB,OAAO,eAAe,CAAC;IACzB,CAAC,CAAC;IAEF,IAAI,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACjD,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC/C,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAE5C,OAAO;QACL,KAAK;QACL,aAAa;KACd,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAI,KAAQ;IAC7C,MAAM,IAAI,GAAG,IAAI,OAAO,EAAmB,CAAC;IAE5C,MAAM,SAAS,GAAG,CAAC,SAAkB,EAAW,EAAE;QAChD,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,mBAAmB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC;QAC9C,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;QAED,MAAM,WAAW,GAAG,SAAoC,CAAC;QACzD,MAAM,YAAY,GAA4B,EAAE,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAElC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,YAAY,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC;IAEF,OAAO,SAAS,CAAC,KAAK,CAAM,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * Placeholder used when sensitive values are removed.\n */\nexport const REDACTION_TOKEN = \"[REDACTED]\";\n\n/**\n * Finds common email address patterns.\n */\nconst EMAIL_REGEX = /\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\b/gi;\n\n/**\n * Finds key-like secrets with common prefixes such as sk- and key-.\n */\nconst API_KEY_REGEX = /\\b(?:sk|key|api|token)-[A-Z0-9_-]{8,}\\b/gi;\n\n/**\n * Finds IPv4 addresses and excludes impossible octets.\n */\nconst IPV4_REGEX =\n /\\b(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\b/g;\n\n/**\n * Summary of a single text redaction operation.\n */\nexport interface RedactionSummary {\n value: string;\n redactedCount: number;\n}\n\n/**\n * Redacts sensitive values from a plain string.\n */\nexport function redactSensitiveText(input: string): RedactionSummary {\n let redactedCount = 0;\n\n const replacer = (): string => {\n redactedCount += 1;\n return REDACTION_TOKEN;\n };\n\n let value = input.replace(EMAIL_REGEX, replacer);\n value = value.replace(API_KEY_REGEX, replacer);\n value = value.replace(IPV4_REGEX, replacer);\n\n return {\n value,\n redactedCount\n };\n}\n\n/**\n * Recursively redacts sensitive content from arbitrary JSON-like payloads.\n */\nexport function redactSensitiveData<T>(value: T): T {\n const seen = new WeakMap<object, unknown>();\n\n const transform = (candidate: unknown): unknown => {\n if (typeof candidate === \"string\") {\n return redactSensitiveText(candidate).value;\n }\n\n if (Array.isArray(candidate)) {\n return candidate.map((entry) => transform(entry));\n }\n\n if (!candidate || typeof candidate !== \"object\") {\n return candidate;\n }\n\n if (seen.has(candidate)) {\n return seen.get(candidate);\n }\n\n const inputRecord = candidate as Record<string, unknown>;\n const outputRecord: Record<string, unknown> = {};\n seen.set(candidate, outputRecord);\n\n for (const [key, entry] of Object.entries(inputRecord)) {\n outputRecord[key] = transform(entry);\n }\n\n return outputRecord;\n };\n\n return transform(value) as T;\n}"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sliding-window limiter configuration.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimiterOptions {
|
|
5
|
+
maxCallsPerMinute: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Result of evaluating a rate-limited request.
|
|
9
|
+
*/
|
|
10
|
+
export interface RateLimitDecision {
|
|
11
|
+
allowed: boolean;
|
|
12
|
+
retryAfterMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* In-memory sliding-window limiter for tool call volume.
|
|
16
|
+
*/
|
|
17
|
+
export declare class RateLimiter {
|
|
18
|
+
private readonly maxCallsPerMinute;
|
|
19
|
+
private readonly callTimestampsMs;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new limiter using calls per rolling minute as the quota.
|
|
22
|
+
*/
|
|
23
|
+
constructor(options: RateLimiterOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether a request can proceed and records it when allowed.
|
|
26
|
+
*/
|
|
27
|
+
consume(now?: number): RateLimitDecision;
|
|
28
|
+
/**
|
|
29
|
+
* Returns current limiter occupancy for diagnostics and testing.
|
|
30
|
+
*/
|
|
31
|
+
getCount(): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/security/rate-limiter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAW;IAE5C;;OAEG;gBACgB,OAAO,EAAE,kBAAkB;IAK9C;;OAEG;IACI,OAAO,CAAC,GAAG,GAAE,MAAmB,GAAG,iBAAiB;IAwB3D;;OAEG;IACI,QAAQ,IAAI,MAAM;CAG1B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory sliding-window limiter for tool call volume.
|
|
3
|
+
*/
|
|
4
|
+
export class RateLimiter {
|
|
5
|
+
maxCallsPerMinute;
|
|
6
|
+
callTimestampsMs;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new limiter using calls per rolling minute as the quota.
|
|
9
|
+
*/
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.maxCallsPerMinute = Math.max(1, options.maxCallsPerMinute);
|
|
12
|
+
this.callTimestampsMs = [];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks whether a request can proceed and records it when allowed.
|
|
16
|
+
*/
|
|
17
|
+
consume(now = Date.now()) {
|
|
18
|
+
const oneMinuteAgo = now - 60_000;
|
|
19
|
+
while (this.callTimestampsMs.length > 0) {
|
|
20
|
+
const head = this.callTimestampsMs[0];
|
|
21
|
+
if (head === undefined || head > oneMinuteAgo) {
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
this.callTimestampsMs.shift();
|
|
25
|
+
}
|
|
26
|
+
if (this.callTimestampsMs.length >= this.maxCallsPerMinute) {
|
|
27
|
+
const oldest = this.callTimestampsMs[0] ?? now;
|
|
28
|
+
return {
|
|
29
|
+
allowed: false,
|
|
30
|
+
retryAfterMs: Math.max(0, 60_000 - (now - oldest))
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
this.callTimestampsMs.push(now);
|
|
34
|
+
return { allowed: true };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns current limiter occupancy for diagnostics and testing.
|
|
38
|
+
*/
|
|
39
|
+
getCount() {
|
|
40
|
+
return this.callTimestampsMs.length;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/security/rate-limiter.ts"],"names":[],"mappings":"AAeA;;GAEG;AACH,MAAM,OAAO,WAAW;IACL,iBAAiB,CAAS;IAE1B,gBAAgB,CAAW;IAE5C;;OAEG;IACH,YAAmB,OAA2B;QAC5C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAChE,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;QACrC,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;QAElC,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,GAAG,YAAY,EAAE,CAAC;gBAC9C,MAAM;YACR,CAAC;YAED,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAChC,CAAC;QAED,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;YAC/C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;aACnD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;IACtC,CAAC;CACF","sourcesContent":["/**\n * Sliding-window limiter configuration.\n */\nexport interface RateLimiterOptions {\n maxCallsPerMinute: number;\n}\n\n/**\n * Result of evaluating a rate-limited request.\n */\nexport interface RateLimitDecision {\n allowed: boolean;\n retryAfterMs?: number;\n}\n\n/**\n * In-memory sliding-window limiter for tool call volume.\n */\nexport class RateLimiter {\n private readonly maxCallsPerMinute: number;\n\n private readonly callTimestampsMs: number[];\n\n /**\n * Creates a new limiter using calls per rolling minute as the quota.\n */\n public constructor(options: RateLimiterOptions) {\n this.maxCallsPerMinute = Math.max(1, options.maxCallsPerMinute);\n this.callTimestampsMs = [];\n }\n\n /**\n * Checks whether a request can proceed and records it when allowed.\n */\n public consume(now: number = Date.now()): RateLimitDecision {\n const oneMinuteAgo = now - 60_000;\n\n while (this.callTimestampsMs.length > 0) {\n const head = this.callTimestampsMs[0];\n if (head === undefined || head > oneMinuteAgo) {\n break;\n }\n\n this.callTimestampsMs.shift();\n }\n\n if (this.callTimestampsMs.length >= this.maxCallsPerMinute) {\n const oldest = this.callTimestampsMs[0] ?? now;\n return {\n allowed: false,\n retryAfterMs: Math.max(0, 60_000 - (now - oldest))\n };\n }\n\n this.callTimestampsMs.push(now);\n return { allowed: true };\n }\n\n /**\n * Returns current limiter occupancy for diagnostics and testing.\n */\n public getCount(): number {\n return this.callTimestampsMs.length;\n }\n}"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Enumerates the supported access modes for protected filesystem locations.
|
|
4
|
+
* - `read-only`: reads are allowed and write/delete operations are denied.
|
|
5
|
+
* - `blocked`: all access is denied.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PathRestrictionModeSchema: z.ZodEnum<["read-only", "blocked"]>;
|
|
8
|
+
/**
|
|
9
|
+
* Declares a protected filesystem path and the enforcement mode applied to it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PathRestrictionSchema: z.ZodObject<{
|
|
12
|
+
/**
|
|
13
|
+
* Directory path to protect. Absolute paths are recommended for reliability.
|
|
14
|
+
*/
|
|
15
|
+
path: z.ZodString;
|
|
16
|
+
/**
|
|
17
|
+
* Restriction mode for the directory.
|
|
18
|
+
*/
|
|
19
|
+
mode: z.ZodEnum<["read-only", "blocked"]>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
path: string;
|
|
22
|
+
mode: "read-only" | "blocked";
|
|
23
|
+
}, {
|
|
24
|
+
path: string;
|
|
25
|
+
mode: "read-only" | "blocked";
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Rule that identifies which tools are allowed by policy.
|
|
29
|
+
* - A string value matches a specific tool name.
|
|
30
|
+
* - A regular expression supports pattern-based matching.
|
|
31
|
+
*/
|
|
32
|
+
export declare const ToolRuleSchema: z.ZodUnion<[z.ZodString, z.ZodType<RegExp, z.ZodTypeDef, RegExp>]>;
|
|
33
|
+
/**
|
|
34
|
+
* Defines the complete guardian policy used to authorize and throttle tool calls.
|
|
35
|
+
*/
|
|
36
|
+
export declare const GuardianPolicySchema: z.ZodObject<{
|
|
37
|
+
/**
|
|
38
|
+
* Tools that an agent is allowed to execute.
|
|
39
|
+
* Supports exact matches and regex-based matching.
|
|
40
|
+
*/
|
|
41
|
+
allowedTools: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodType<RegExp, z.ZodTypeDef, RegExp>]>, "many">;
|
|
42
|
+
/**
|
|
43
|
+
* Filesystem directories with explicit read-only or blocked behavior.
|
|
44
|
+
*/
|
|
45
|
+
restrictedPaths: z.ZodArray<z.ZodObject<{
|
|
46
|
+
/**
|
|
47
|
+
* Directory path to protect. Absolute paths are recommended for reliability.
|
|
48
|
+
*/
|
|
49
|
+
path: z.ZodString;
|
|
50
|
+
/**
|
|
51
|
+
* Restriction mode for the directory.
|
|
52
|
+
*/
|
|
53
|
+
mode: z.ZodEnum<["read-only", "blocked"]>;
|
|
54
|
+
}, "strip", z.ZodTypeAny, {
|
|
55
|
+
path: string;
|
|
56
|
+
mode: "read-only" | "blocked";
|
|
57
|
+
}, {
|
|
58
|
+
path: string;
|
|
59
|
+
mode: "read-only" | "blocked";
|
|
60
|
+
}>, "many">;
|
|
61
|
+
/**
|
|
62
|
+
* Maximum number of tool calls allowed in a rolling one-minute window.
|
|
63
|
+
*/
|
|
64
|
+
maxCallsPerMinute: z.ZodNumber;
|
|
65
|
+
/**
|
|
66
|
+
* If true, destructive actions (for example: delete/write) require explicit approval.
|
|
67
|
+
*/
|
|
68
|
+
approvalRequired: z.ZodBoolean;
|
|
69
|
+
}, "strip", z.ZodTypeAny, {
|
|
70
|
+
allowedTools: (string | RegExp)[];
|
|
71
|
+
restrictedPaths: {
|
|
72
|
+
path: string;
|
|
73
|
+
mode: "read-only" | "blocked";
|
|
74
|
+
}[];
|
|
75
|
+
maxCallsPerMinute: number;
|
|
76
|
+
approvalRequired: boolean;
|
|
77
|
+
}, {
|
|
78
|
+
allowedTools: (string | RegExp)[];
|
|
79
|
+
restrictedPaths: {
|
|
80
|
+
path: string;
|
|
81
|
+
mode: "read-only" | "blocked";
|
|
82
|
+
}[];
|
|
83
|
+
maxCallsPerMinute: number;
|
|
84
|
+
approvalRequired: boolean;
|
|
85
|
+
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Type-safe representation of a single path restriction entry.
|
|
88
|
+
*/
|
|
89
|
+
export type PathRestriction = z.infer<typeof PathRestrictionSchema>;
|
|
90
|
+
/**
|
|
91
|
+
* Type-safe representation of the guardian policy payload.
|
|
92
|
+
*/
|
|
93
|
+
export type GuardianPolicy = z.infer<typeof GuardianPolicySchema>;
|
|
94
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/types/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,qCAAmC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,qBAAqB;IAChC;;OAEG;;IAEH;;OAEG;;;;;;;;EAEH,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,oEAAqD,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,oBAAoB;IAC/B;;;OAGG;;IAGH;;OAEG;;QA7BH;;WAEG;;QAEH;;WAEG;;;;;;;;;IA0BH;;OAEG;;IAGH;;OAEG;;;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Enumerates the supported access modes for protected filesystem locations.
|
|
4
|
+
* - `read-only`: reads are allowed and write/delete operations are denied.
|
|
5
|
+
* - `blocked`: all access is denied.
|
|
6
|
+
*/
|
|
7
|
+
export const PathRestrictionModeSchema = z.enum(["read-only", "blocked"]);
|
|
8
|
+
/**
|
|
9
|
+
* Declares a protected filesystem path and the enforcement mode applied to it.
|
|
10
|
+
*/
|
|
11
|
+
export const PathRestrictionSchema = z.object({
|
|
12
|
+
/**
|
|
13
|
+
* Directory path to protect. Absolute paths are recommended for reliability.
|
|
14
|
+
*/
|
|
15
|
+
path: z.string().min(1, "path cannot be empty"),
|
|
16
|
+
/**
|
|
17
|
+
* Restriction mode for the directory.
|
|
18
|
+
*/
|
|
19
|
+
mode: PathRestrictionModeSchema
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Rule that identifies which tools are allowed by policy.
|
|
23
|
+
* - A string value matches a specific tool name.
|
|
24
|
+
* - A regular expression supports pattern-based matching.
|
|
25
|
+
*/
|
|
26
|
+
export const ToolRuleSchema = z.union([z.string().min(1), z.instanceof(RegExp)]);
|
|
27
|
+
/**
|
|
28
|
+
* Defines the complete guardian policy used to authorize and throttle tool calls.
|
|
29
|
+
*/
|
|
30
|
+
export const GuardianPolicySchema = z.object({
|
|
31
|
+
/**
|
|
32
|
+
* Tools that an agent is allowed to execute.
|
|
33
|
+
* Supports exact matches and regex-based matching.
|
|
34
|
+
*/
|
|
35
|
+
allowedTools: z.array(ToolRuleSchema).min(1, "at least one tool rule is required"),
|
|
36
|
+
/**
|
|
37
|
+
* Filesystem directories with explicit read-only or blocked behavior.
|
|
38
|
+
*/
|
|
39
|
+
restrictedPaths: z.array(PathRestrictionSchema),
|
|
40
|
+
/**
|
|
41
|
+
* Maximum number of tool calls allowed in a rolling one-minute window.
|
|
42
|
+
*/
|
|
43
|
+
maxCallsPerMinute: z.number().int().positive(),
|
|
44
|
+
/**
|
|
45
|
+
* If true, destructive actions (for example: delete/write) require explicit approval.
|
|
46
|
+
*/
|
|
47
|
+
approvalRequired: z.boolean()
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/types/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;AAE1E;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C;;OAEG;IACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,sBAAsB,CAAC;IAC/C;;OAEG;IACH,IAAI,EAAE,yBAAyB;CAChC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAEjF;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C;;;OAGG;IACH,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;IAElF;;OAEG;IACH,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC;IAE/C;;OAEG;IACH,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAE9C;;OAEG;IACH,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE;CAC9B,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Enumerates the supported access modes for protected filesystem locations.\n * - `read-only`: reads are allowed and write/delete operations are denied.\n * - `blocked`: all access is denied.\n */\nexport const PathRestrictionModeSchema = z.enum([\"read-only\", \"blocked\"]);\n\n/**\n * Declares a protected filesystem path and the enforcement mode applied to it.\n */\nexport const PathRestrictionSchema = z.object({\n /**\n * Directory path to protect. Absolute paths are recommended for reliability.\n */\n path: z.string().min(1, \"path cannot be empty\"),\n /**\n * Restriction mode for the directory.\n */\n mode: PathRestrictionModeSchema\n});\n\n/**\n * Rule that identifies which tools are allowed by policy.\n * - A string value matches a specific tool name.\n * - A regular expression supports pattern-based matching.\n */\nexport const ToolRuleSchema = z.union([z.string().min(1), z.instanceof(RegExp)]);\n\n/**\n * Defines the complete guardian policy used to authorize and throttle tool calls.\n */\nexport const GuardianPolicySchema = z.object({\n /**\n * Tools that an agent is allowed to execute.\n * Supports exact matches and regex-based matching.\n */\n allowedTools: z.array(ToolRuleSchema).min(1, \"at least one tool rule is required\"),\n\n /**\n * Filesystem directories with explicit read-only or blocked behavior.\n */\n restrictedPaths: z.array(PathRestrictionSchema),\n\n /**\n * Maximum number of tool calls allowed in a rolling one-minute window.\n */\n maxCallsPerMinute: z.number().int().positive(),\n\n /**\n * If true, destructive actions (for example: delete/write) require explicit approval.\n */\n approvalRequired: z.boolean()\n});\n\n/**\n * Type-safe representation of a single path restriction entry.\n */\nexport type PathRestriction = z.infer<typeof PathRestrictionSchema>;\n\n/**\n * Type-safe representation of the guardian policy payload.\n */\nexport type GuardianPolicy = z.infer<typeof GuardianPolicySchema>;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-warden",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Policy enforcement and guardrails for MCP-compatible tool execution.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"mcp-warden": "dist/cli/index.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"guardian",
|
|
30
|
+
"policy",
|
|
31
|
+
"security"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"chalk": "^5.6.0",
|
|
38
|
+
"commander": "^14.0.1",
|
|
39
|
+
"zod": "^3.24.2"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^24.0.0",
|
|
43
|
+
"typescript": "^5.8.3",
|
|
44
|
+
"vitest": "^3.1.4"
|
|
45
|
+
}
|
|
46
|
+
}
|