@aimarket/warden 0.3.0 → 0.4.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/dist/glob.js ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * `*`-glob matching, without a regular expression.
3
+ *
4
+ * WARDEN matches two kinds of glob against attacker-controlled text: threat-feed
5
+ * patterns (a signed publisher chooses the pattern, a hostile server chooses the
6
+ * haystack) and `policy.sensitiveToolPatterns` (the operator chooses the pattern,
7
+ * the server chooses the tool name). Both used to compile `*` into `.*` and run a
8
+ * regex, which is a denial of service:
9
+ *
10
+ * pattern "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*zzz" (32 characters)
11
+ * haystack "aaaa..." (220 characters)
12
+ * -> 112 SECONDS inside one RegExp.test
13
+ *
14
+ * The engine has to try every way of distributing the wildcards over the haystack
15
+ * before it can conclude "no match" - exponential in the number of `*`. That
16
+ * mattered more than an ordinary performance bug: the threat feed's whole trust
17
+ * story is that a compromised publisher can only ever ADD protection, never
18
+ * remove it, and a record that hangs `vet()` forever removes all of it. The same
19
+ * shape let a hostile server freeze the agent inspecting it, by advertising a
20
+ * long enough tool name against an operator's own `*a*b*c*` pattern.
21
+ *
22
+ * The two-pointer algorithm below is the standard wildcard matcher: it walks the
23
+ * haystack once and remembers only the most recent `*` to fall back to, so the
24
+ * work is bounded by `pattern.length x value.length` with no backtracking tree to
25
+ * explode. The same 32/220 case returns in microseconds.
26
+ *
27
+ * Semantics, matching what the regex did: `*` matches any run of zero or more
28
+ * UTF-16 code units (including newlines - the old regex needed the `s` flag for
29
+ * that), every other character is literal, and the match is anchored at both
30
+ * ends. Case folding is the caller's job: both sides arrive already lowercased.
31
+ */
32
+ export function wildcardMatch(pattern, value) {
33
+ const STAR = 0x2a;
34
+ let p = 0;
35
+ let v = 0;
36
+ let star = -1;
37
+ let resume = 0;
38
+ while (v < value.length) {
39
+ if (p < pattern.length && pattern.charCodeAt(p) === value.charCodeAt(v)) {
40
+ p++;
41
+ v++;
42
+ }
43
+ else if (p < pattern.length && pattern.charCodeAt(p) === STAR) {
44
+ // Remember this wildcard and let it match nothing for now.
45
+ star = p++;
46
+ resume = v;
47
+ }
48
+ else if (star >= 0) {
49
+ // Backtrack: hand one more character to the last wildcard. There is only
50
+ // ever one fallback point, which is what keeps this linear-bounded.
51
+ p = star + 1;
52
+ v = ++resume;
53
+ }
54
+ else {
55
+ return false;
56
+ }
57
+ }
58
+ // Trailing wildcards may still match the empty remainder.
59
+ while (p < pattern.length && pattern.charCodeAt(p) === STAR)
60
+ p++;
61
+ return p === pattern.length;
62
+ }
63
+ /**
64
+ * How many wildcards a pattern may carry.
65
+ *
66
+ * The matcher above is not exponential, but `*`-heavy patterns still cost
67
+ * `pattern x value` per match and buy nothing: a real threat pattern names a
68
+ * command, a path or a keyword. A record with more than this many wildcards is
69
+ * refused rather than run - refusing an *addition* to the deny-list is the safe
70
+ * direction, and it is reported loudly.
71
+ */
72
+ export const MAX_GLOB_WILDCARDS = 12;
73
+ /** Count `*` in a pattern, for the record validator. */
74
+ export function countWildcards(pattern) {
75
+ let n = 0;
76
+ for (let i = 0; i < pattern.length; i++) {
77
+ if (pattern.charCodeAt(i) === 0x2a)
78
+ n++;
79
+ }
80
+ return n;
81
+ }
82
+ /**
83
+ * Largest gap an INTERIOR `*` in a threat pattern may span.
84
+ *
85
+ * A field survey of 1 108 public MCP servers (see docs/mcp-survey.md) found every
86
+ * multi-segment built-in firing on unrelated words that merely happened to appear
87
+ * in the same paragraph: `*drain*wallet*` matched an anti-drainer scanner because
88
+ * it says "drainer" in one clause and "wallet" in another, and `*seed*phrase*`
89
+ * matched "for a **seed** topic, returns suggested search **phrases**". A real
90
+ * threat pattern names something whose parts sit next to each other; 24 characters
91
+ * is roughly "a few words apart" and comfortably covers "wallet seed or recovery
92
+ * phrase".
93
+ */
94
+ export const THREAT_MAX_GAP = 24;
95
+ /**
96
+ * Threat-pattern matching: `wildcardMatch` plus two constraints that only make
97
+ * sense for a deny-list.
98
+ *
99
+ * 1. **Interior gaps are bounded** to {@link THREAT_MAX_GAP}. Leading and trailing
100
+ * `*` stay unbounded — `*id_rsa*` must still find `id_rsa` anywhere.
101
+ * 2. **A segment that starts with a word character must start at a word boundary.**
102
+ * `*sweep*funds*` matched an ENS floor-sweeping tool because `funds` sits inside
103
+ * "re**funds**". Only the START is anchored: a pattern author writing `*drain*`
104
+ * means "drainer" too, so anchoring the end as well would break ordinary
105
+ * stemming.
106
+ *
107
+ * Kept separate from {@link wildcardMatch} on purpose. That function is also what
108
+ * `policy.sensitiveToolPatterns` runs on, where the operator wrote the pattern
109
+ * against their own tool names and plain glob semantics are what they expect.
110
+ *
111
+ * Completeness matters here in a way it does not for a display filter: a missed
112
+ * match is a deny-list entry that silently stopped working. So this explores every
113
+ * reachable position per segment (deduplicated, so the work stays bounded by
114
+ * `segments x value.length`) rather than taking the leftmost occurrence greedily,
115
+ * which would report "no match" for a pattern that does match further along.
116
+ */
117
+ export function threatMatch(pattern, value) {
118
+ if (!pattern.includes("*"))
119
+ return value.includes(pattern);
120
+ const parts = pattern.split("*");
121
+ const openStart = parts[0] === "";
122
+ const openEnd = parts[parts.length - 1] === "";
123
+ const segments = parts.filter((s) => s !== "");
124
+ if (segments.length === 0)
125
+ return true;
126
+ // Reachable end positions after matching the segments consumed so far.
127
+ let ends = [0];
128
+ for (let i = 0; i < segments.length; i++) {
129
+ const seg = segments[i];
130
+ // The first segment is anchored at 0 unless the pattern opened with `*`.
131
+ const unbounded = i === 0 ? openStart : false;
132
+ const next = new Set();
133
+ for (const from of ends) {
134
+ const limit = unbounded ? value.length : Math.min(value.length, from + THREAT_MAX_GAP + seg.length);
135
+ let at = from;
136
+ for (;;) {
137
+ const found = value.indexOf(seg, at);
138
+ if (found < 0 || found + seg.length > limit)
139
+ break;
140
+ if (i === 0 && !openStart && found !== 0)
141
+ break;
142
+ if (startsOnBoundary(seg, value, found))
143
+ next.add(found + seg.length);
144
+ at = found + 1;
145
+ }
146
+ }
147
+ if (next.size === 0)
148
+ return false;
149
+ ends = [...next];
150
+ }
151
+ return openEnd || ends.some((e) => e === value.length);
152
+ }
153
+ /**
154
+ * Is `seg` at `at` starting on a word boundary?
155
+ *
156
+ * Only asked when the segment's own first character is a word character — a
157
+ * pattern starting with `~`, `.` or `/` has no boundary to respect.
158
+ */
159
+ function startsOnBoundary(seg, value, at) {
160
+ if (!isWordChar(seg.charCodeAt(0)))
161
+ return true;
162
+ if (at === 0)
163
+ return true;
164
+ return !isWordChar(value.charCodeAt(at - 1));
165
+ }
166
+ /**
167
+ * Letters and digits only — `_` and `-` are BOUNDARIES here, not word characters.
168
+ *
169
+ * Tool schemas are written in snake_case and kebab-case, so a field named
170
+ * `seed_phrase` must still satisfy `*seed*phrase*`; treating `_` as a word
171
+ * character silently switched that built-in off. "re**funds**" is still rejected,
172
+ * because there the preceding character is a letter.
173
+ */
174
+ function isWordChar(c) {
175
+ return ((c >= 0x30 && c <= 0x39) || // 0-9
176
+ (c >= 0x41 && c <= 0x5a) || // A-Z
177
+ (c >= 0x61 && c <= 0x7a) // a-z
178
+ );
179
+ }
180
+ //# sourceMappingURL=glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { McpServerRef, PinStore, ToolDef, WardenGate, WardenLogger, WardenPolicy, WardenVerdict } from "./types.js";
2
2
  import { ThreatFeed } from "./threat-feed.js";
3
3
  export { StaticScanGate, staticScanRuleset, staticScanRulesetRef, STATIC_SCAN_RULESET_VERSION, } from "./static-scan.js";
4
- export { PinningGate, canonicalToolsHash, tryCanonicalToolsHash, UNCANONICAL_TOOLS_HASH } from "./pinning.js";
4
+ export { PinningGate, canonicalToolsHash, tryCanonicalToolsHash, serverIdentityHash, UNCANONICAL_TOOLS_HASH } from "./pinning.js";
5
5
  export { OriginGate } from "./origin.js";
6
6
  export { ThreatFeed, ThreatGate, DEFAULT_FEED_MAX_AGE_MS, FEED_CLOCK_SKEW_MS } from "./threat-feed.js";
7
7
  export { EgressGuard, isSensitiveTool, classifyTools } from "./sandbox.js";
@@ -10,6 +10,8 @@ export type { CanonicalizationCode } from "./jcs.js";
10
10
  export type { StaticScanRule, StaticScanRuleset } from "./static-scan.js";
11
11
  export type { ThreatFeedOptions } from "./threat-feed.js";
12
12
  export { silentLogger } from "./logger.js";
13
+ export { displaySafe, DEFAULT_DISPLAY_MAX } from "./sanitize.js";
14
+ export { wildcardMatch } from "./glob.js";
13
15
  export * from "./types.js";
14
16
  export interface WardenInit {
15
17
  gates: WardenGate[];
@@ -33,10 +35,17 @@ export interface WardenCreateDeps {
33
35
  *
34
36
  * Every MCP server is vetted through an ordered gate chain before its tools are
35
37
  * exposed to the agent: cheap static scanning first, then the known-bad threat
36
- * feed, then network reputation, then drift/pinning. Findings accumulate across
37
- * gates; the verdict is allow/block plus a composite 0..1 safety score and a
38
- * per-tool partition so a mostly-trusted server can have one poisoned tool
39
- * quarantined without severing the whole connection.
38
+ * feed, then origin (declared by the operator, or offered by a remote catalog),
39
+ * then drift/pinning. Findings accumulate across gates; the verdict is allow/block
40
+ * plus a composite 0..1 safety score and a per-tool partition so a mostly-trusted
41
+ * server can have one poisoned tool quarantined without severing the whole
42
+ * connection.
43
+ *
44
+ * The score is WARDEN's OWN — the product of what each gate contributed, computed
45
+ * from local facts. No reputation oracle is consulted and no socket is opened while
46
+ * vetting. Slot three used to be a reputation gate that asked an oracle for a score
47
+ * it had no data to compute; it is gone, and test/no-phantom-gate.test.ts fails if
48
+ * anything like it comes back.
40
49
  */
41
50
  export declare class Warden {
42
51
  private readonly gates;
@@ -44,11 +53,19 @@ export declare class Warden {
44
53
  private readonly log;
45
54
  constructor(init: WardenInit);
46
55
  /**
47
- * Guard against config typos that silently disable blocking. If blockAtSeverity
48
- * isn't a valid Severity key, SEVERITY_RANK[bad] is undefined and every
49
- * comparison `number >= undefined` is false — zero blocks, zero warnings.
56
+ * Guard against config typos that silently disable blocking, on a COPY.
57
+ *
58
+ * If `blockAtSeverity` isn't a valid Severity key, `SEVERITY_RANK[bad]` is
59
+ * undefined and every comparison `number >= undefined` is false — zero blocks,
60
+ * zero warnings — so the value is repaired. Repairing it in place used to write
61
+ * into the host's own policy object, which is both a surprise (their config
62
+ * silently changes under them, and any other Warden sharing the object sees it)
63
+ * and a crash: `Object.freeze(policy)` is a reasonable thing for a host to do,
64
+ * and assigning to a frozen property throws a TypeError out of the constructor
65
+ * under ESM strict mode. A firewall that refuses to be constructed because the
66
+ * config was immutable is not a firewall.
50
67
  */
51
- private validatePolicy;
68
+ private normalizePolicy;
52
69
  /** Build the standard gate chain: static → threat → origin → pinning. */
53
70
  static create(deps: WardenCreateDeps): Warden;
54
71
  /**
@@ -57,6 +74,22 @@ export declare class Warden {
57
74
  * is fatal or any finding reaches policy.blockAtSeverity.
58
75
  */
59
76
  vet(server: McpServerRef, tools: ToolDef[]): Promise<WardenVerdict>;
77
+ /**
78
+ * Run one gate, converting a thrown error into a blocking finding.
79
+ *
80
+ * A gate that crashed did not clear the server, so the honest result is a
81
+ * finding at `high` with a zero score — which blocks at the default threshold
82
+ * and, under a report-only policy (`blockAtSeverity: "critical"`), is reported
83
+ * without blocking, exactly like every other high-severity finding.
84
+ *
85
+ * Letting the exception escape instead was worse than it looks: `vet()`
86
+ * rejected, so the host got no verdict at all — not the findings the earlier
87
+ * gates had already produced, not the per-tool partition — and whether that
88
+ * ended in a blocked connection or an unhandled rejection was up to the host.
89
+ * A store with a full disk or a custom gate with a bug should not be able to
90
+ * decide that.
91
+ */
92
+ private runGate;
60
93
  /**
61
94
  * Record the user's approval: pin the current tool defs so future drift is
62
95
  * detected. Idempotent — re-approving just refreshes the snapshot.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EAER,OAAO,EAEP,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAI1D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC9G,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACvG,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,cAAc,YAAY,CAAC;AAU3B,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE,UAAU;IAO5B;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAYtB,yEAAyE;IACzE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM;IAU7C;;;;OAIG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IAuDzE;;;OAGG;IACG,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpE;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,cAAc;CAGvB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EAER,OAAO,EAEP,UAAU,EAEV,YAAY,EACZ,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAK1D,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAClI,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACvG,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,cAAc,YAAY,CAAC;AAU3B,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,kEAAkE;IAClE,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE,UAAU;IAM5B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,eAAe;IAWvB,yEAAyE;IACzE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM;IAU7C;;;;OAIG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IAqDzE;;;;;;;;;;;;;;OAcG;YACW,OAAO;IAiCrB;;;OAGG;IACG,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUpE;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,cAAc;CAGvB"}
package/dist/index.js CHANGED
@@ -4,13 +4,16 @@ import { OriginGate } from "./origin.js";
4
4
  import { ThreatGate } from "./threat-feed.js";
5
5
  import { classifyTools } from "./sandbox.js";
6
6
  import { silentLogger } from "./logger.js";
7
+ import { displaySafe } from "./sanitize.js";
7
8
  export { StaticScanGate, staticScanRuleset, staticScanRulesetRef, STATIC_SCAN_RULESET_VERSION, } from "./static-scan.js";
8
- export { PinningGate, canonicalToolsHash, tryCanonicalToolsHash, UNCANONICAL_TOOLS_HASH } from "./pinning.js";
9
+ export { PinningGate, canonicalToolsHash, tryCanonicalToolsHash, serverIdentityHash, UNCANONICAL_TOOLS_HASH } from "./pinning.js";
9
10
  export { OriginGate } from "./origin.js";
10
11
  export { ThreatFeed, ThreatGate, DEFAULT_FEED_MAX_AGE_MS, FEED_CLOCK_SKEW_MS } from "./threat-feed.js";
11
12
  export { EgressGuard, isSensitiveTool, classifyTools } from "./sandbox.js";
12
13
  export { canonicalize, parseJsonStrict, CanonicalizationError, MAX_SAFE_JSON_INTEGER } from "./jcs.js";
13
14
  export { silentLogger } from "./logger.js";
15
+ export { displaySafe, DEFAULT_DISPLAY_MAX } from "./sanitize.js";
16
+ export { wildcardMatch } from "./glob.js";
14
17
  export * from "./types.js";
15
18
  const SEVERITY_RANK = {
16
19
  info: 0,
@@ -24,39 +27,53 @@ const SEVERITY_RANK = {
24
27
  *
25
28
  * Every MCP server is vetted through an ordered gate chain before its tools are
26
29
  * exposed to the agent: cheap static scanning first, then the known-bad threat
27
- * feed, then network reputation, then drift/pinning. Findings accumulate across
28
- * gates; the verdict is allow/block plus a composite 0..1 safety score and a
29
- * per-tool partition so a mostly-trusted server can have one poisoned tool
30
- * quarantined without severing the whole connection.
30
+ * feed, then origin (declared by the operator, or offered by a remote catalog),
31
+ * then drift/pinning. Findings accumulate across gates; the verdict is allow/block
32
+ * plus a composite 0..1 safety score and a per-tool partition so a mostly-trusted
33
+ * server can have one poisoned tool quarantined without severing the whole
34
+ * connection.
35
+ *
36
+ * The score is WARDEN's OWN — the product of what each gate contributed, computed
37
+ * from local facts. No reputation oracle is consulted and no socket is opened while
38
+ * vetting. Slot three used to be a reputation gate that asked an oracle for a score
39
+ * it had no data to compute; it is gone, and test/no-phantom-gate.test.ts fails if
40
+ * anything like it comes back.
31
41
  */
32
42
  export class Warden {
33
43
  gates;
34
44
  policy;
35
45
  log;
36
46
  constructor(init) {
37
- this.gates = init.gates;
38
- this.policy = init.policy;
47
+ this.gates = [...init.gates];
39
48
  this.log = (init.log ?? silentLogger()).child("warden");
40
- this.validatePolicy();
49
+ this.policy = this.normalizePolicy(init.policy);
41
50
  }
42
51
  /**
43
- * Guard against config typos that silently disable blocking. If blockAtSeverity
44
- * isn't a valid Severity key, SEVERITY_RANK[bad] is undefined and every
45
- * comparison `number >= undefined` is false — zero blocks, zero warnings.
52
+ * Guard against config typos that silently disable blocking, on a COPY.
53
+ *
54
+ * If `blockAtSeverity` isn't a valid Severity key, `SEVERITY_RANK[bad]` is
55
+ * undefined and every comparison `number >= undefined` is false — zero blocks,
56
+ * zero warnings — so the value is repaired. Repairing it in place used to write
57
+ * into the host's own policy object, which is both a surprise (their config
58
+ * silently changes under them, and any other Warden sharing the object sees it)
59
+ * and a crash: `Object.freeze(policy)` is a reasonable thing for a host to do,
60
+ * and assigning to a frozen property throws a TypeError out of the constructor
61
+ * under ESM strict mode. A firewall that refuses to be constructed because the
62
+ * config was immutable is not a firewall.
46
63
  */
47
- validatePolicy() {
64
+ normalizePolicy(policy) {
48
65
  const valid = ["info", "low", "medium", "high", "critical"];
49
- if (!valid.includes(this.policy.blockAtSeverity)) {
50
- const fallback = "high";
51
- this.log.warn(`WARDEN: invalid blockAtSeverity "${String(this.policy.blockAtSeverity)}" ` +
52
- `(expected one of ${valid.join(", ")}) — falling back to "${fallback}" to keep blocking enabled`);
53
- this.policy.blockAtSeverity = fallback;
54
- }
66
+ if (valid.includes(policy.blockAtSeverity))
67
+ return { ...policy };
68
+ const fallback = "high";
69
+ this.log.warn(`WARDEN: invalid blockAtSeverity "${displaySafe(policy.blockAtSeverity, 40)}" ` +
70
+ `(expected one of ${valid.join(", ")}) — falling back to "${fallback}" to keep blocking enabled`);
71
+ return { ...policy, blockAtSeverity: fallback };
55
72
  }
56
73
  /** Build the standard gate chain: static → threat → origin → pinning. */
57
74
  static create(deps) {
58
75
  const gates = [
59
- new StaticScanGate(),
76
+ new StaticScanGate(deps.log),
60
77
  new ThreatGate(deps.threatFeed),
61
78
  new OriginGate(),
62
79
  new PinningGate(deps.store),
@@ -75,12 +92,7 @@ export class Warden {
75
92
  let decidedBy;
76
93
  const blockThreshold = SEVERITY_RANK[this.policy.blockAtSeverity];
77
94
  for (const gate of this.gates) {
78
- const result = await gate.evaluate({
79
- server,
80
- tools,
81
- prior: [...findings],
82
- policy: this.policy,
83
- });
95
+ const result = await this.runGate(gate, server, tools, findings);
84
96
  findings.push(...result.findings);
85
97
  scores.push(clamp01(result.score));
86
98
  // Advisory findings never block, at any threshold — see WardenFinding.advisory.
@@ -91,7 +103,7 @@ export class Warden {
91
103
  decidedBy = gate.name;
92
104
  }
93
105
  if (result.fatal) {
94
- this.log.warn(`gate "${gate.name}" returned fatal for server ${server.id}`);
106
+ this.log.warn(`gate "${displaySafe(gate.name, 60)}" returned fatal for server ${displaySafe(server.id)}`);
95
107
  break; // short-circuit only on an explicit fatal
96
108
  }
97
109
  }
@@ -100,10 +112,11 @@ export class Warden {
100
112
  const score = scores.reduce((acc, s) => acc * s, 1);
101
113
  const { allowedTools, blockedTools } = this.partitionTools(tools, findings, blockThreshold);
102
114
  if (!allow) {
103
- this.log.warn(`BLOCK ${server.id} (decidedBy=${decidedBy}, score=${score.toFixed(3)}, findings=${findings.length})`);
115
+ this.log.warn(`BLOCK ${displaySafe(server.id)} (decidedBy=${displaySafe(decidedBy ?? "-", 60)}, ` +
116
+ `score=${score.toFixed(3)}, findings=${findings.length})`);
104
117
  }
105
118
  else {
106
- this.log.info(`ALLOW ${server.id} (score=${score.toFixed(3)}, sensitive=${blockedTools.length === 0 ? this.sensitiveCount(tools) : "?"})`);
119
+ this.log.info(`ALLOW ${displaySafe(server.id)} (score=${score.toFixed(3)}, sensitive=${blockedTools.length === 0 ? this.sensitiveCount(tools) : "?"})`);
107
120
  }
108
121
  return {
109
122
  allow,
@@ -115,6 +128,48 @@ export class Warden {
115
128
  rulesets: { staticScan: staticScanRulesetRef() },
116
129
  };
117
130
  }
131
+ /**
132
+ * Run one gate, converting a thrown error into a blocking finding.
133
+ *
134
+ * A gate that crashed did not clear the server, so the honest result is a
135
+ * finding at `high` with a zero score — which blocks at the default threshold
136
+ * and, under a report-only policy (`blockAtSeverity: "critical"`), is reported
137
+ * without blocking, exactly like every other high-severity finding.
138
+ *
139
+ * Letting the exception escape instead was worse than it looks: `vet()`
140
+ * rejected, so the host got no verdict at all — not the findings the earlier
141
+ * gates had already produced, not the per-tool partition — and whether that
142
+ * ended in a blocked connection or an unhandled rejection was up to the host.
143
+ * A store with a full disk or a custom gate with a bug should not be able to
144
+ * decide that.
145
+ */
146
+ async runGate(gate, server, tools, prior) {
147
+ try {
148
+ const result = await gate.evaluate({ server, tools, prior: [...prior], policy: this.policy });
149
+ // A third-party gate is not obliged to be well-behaved either.
150
+ return {
151
+ findings: Array.isArray(result?.findings) ? result.findings : [],
152
+ score: typeof result?.score === "number" ? result.score : 0,
153
+ ...(result?.fatal ? { fatal: true } : {}),
154
+ };
155
+ }
156
+ catch (err) {
157
+ const detail = err instanceof Error ? err.message : String(err);
158
+ this.log.error(`gate "${displaySafe(gate.name, 60)}" threw: ${displaySafe(detail, 300)}`);
159
+ return {
160
+ findings: [
161
+ {
162
+ gate: gate.name,
163
+ severity: "high",
164
+ code: "GATE_ERROR",
165
+ message: `Gate "${displaySafe(gate.name, 60)}" failed to complete (${displaySafe(detail, 300)}), so this ` +
166
+ `server was not checked by it.`,
167
+ },
168
+ ],
169
+ score: 0,
170
+ };
171
+ }
172
+ }
118
173
  /**
119
174
  * Record the user's approval: pin the current tool defs so future drift is
120
175
  * detected. Idempotent — re-approving just refreshes the snapshot.
@@ -126,7 +181,7 @@ export class Warden {
126
181
  return;
127
182
  }
128
183
  await pinning.pin(server, tools);
129
- this.log.info(`pinned tool defs for ${server.id} (${tools.length} tools)`);
184
+ this.log.info(`pinned tool defs for ${displaySafe(server.id)} (${tools.length} tools)`);
130
185
  }
131
186
  /**
132
187
  * A tool is blocked if a finding naming it reaches the block threshold.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,UAAU,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAW,YAAW,UAAU;IAC3C,QAAQ,CAAC,IAAI,YAAY;IAGnB,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAqBlE"}
1
+ {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,UAAU,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAW,YAAW,UAAU;IAC3C,QAAQ,CAAC,IAAI,YAAY;IAGnB,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAyBlE"}
package/dist/origin.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { displaySafe } from "./sanitize.js";
1
2
  /**
2
3
  * Origin gate — enforces `policy.allowUnknownServers` against where a server
3
4
  * declaration came from.
@@ -27,14 +28,18 @@ export class OriginGate {
27
28
  return { findings: [], score: 1 };
28
29
  }
29
30
  const strict = input.policy.allowUnknownServers !== true;
31
+ // Both the id and the catalog name can come from a remote catalog entry, and
32
+ // this message is printed to the operator's terminal.
33
+ const id = displaySafe(input.server.id);
34
+ const from = displaySafe(catalog);
30
35
  const finding = {
31
36
  gate: this.name,
32
37
  severity: strict ? "high" : "info",
33
38
  code: "SERVER_UNDECLARED",
34
39
  message: strict
35
- ? `Server "${input.server.id}" was discovered from catalog "${catalog}" and is not declared under mcp.servers; ` +
40
+ ? `Server "${id}" was discovered from catalog "${from}" and is not declared under mcp.servers; ` +
36
41
  `policy forbids undeclared servers (allowUnknownServers=false) — blocking. Add it to mcp.servers to allow it.`
37
- : `Server "${input.server.id}" was discovered from catalog "${catalog}", not declared under mcp.servers.`,
42
+ : `Server "${id}" was discovered from catalog "${from}", not declared under mcp.servers.`,
38
43
  };
39
44
  // Catalog provenance on its own is not a defect, so the permissive path does
40
45
  // not tax the composite score; it only says where the server came from.
@@ -1 +1 @@
1
- {"version":3,"file":"origin.js","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,UAAU;IACZ,IAAI,GAAG,QAAQ,CAAC;IAEzB,uFAAuF;IACvF,KAAK,CAAC,QAAQ,CAAC,KAAsB;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,iEAAiE;YACjE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACpC,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,mBAAmB,KAAK,IAAI,CAAC;QACzD,MAAM,OAAO,GAAkB;YAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YAClC,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,MAAM;gBACb,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,kCAAkC,OAAO,2CAA2C;oBAC9G,8GAA8G;gBAChH,CAAC,CAAC,WAAW,KAAK,CAAC,MAAM,CAAC,EAAE,kCAAkC,OAAO,oCAAoC;SAC5G,CAAC;QACF,6EAA6E;QAC7E,wEAAwE;QACxE,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACvE,CAAC;CACF"}
1
+ {"version":3,"file":"origin.js","sourceRoot":"","sources":["../src/origin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQ5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,UAAU;IACZ,IAAI,GAAG,QAAQ,CAAC;IAEzB,uFAAuF;IACvF,KAAK,CAAC,QAAQ,CAAC,KAAsB;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,iEAAiE;YACjE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACpC,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,mBAAmB,KAAK,IAAI,CAAC;QACzD,6EAA6E;QAC7E,sDAAsD;QACtD,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,OAAO,GAAkB;YAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YAClC,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,MAAM;gBACb,CAAC,CAAC,WAAW,EAAE,kCAAkC,IAAI,2CAA2C;oBAC9F,8GAA8G;gBAChH,CAAC,CAAC,WAAW,EAAE,kCAAkC,IAAI,oCAAoC;SAC5F,CAAC;QACF,6EAA6E;QAC7E,wEAAwE;QACxE,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACvE,CAAC;CACF"}
package/dist/pinning.d.ts CHANGED
@@ -14,6 +14,13 @@ export declare class PinningGate implements WardenGate {
14
14
  readonly name = "pinning";
15
15
  constructor(store: PinStore);
16
16
  evaluate(input: WardenGateInput): Promise<WardenGateResult>;
17
+ /**
18
+ * Has the program behind this server changed since approval?
19
+ *
20
+ * Silent when the pin predates {@link PinnedServer.identityHash}: an absent
21
+ * value is "not recorded", never "changed". The next `approve()` records it.
22
+ */
23
+ private identityDrift;
17
24
  /**
18
25
  * The tool-def set has no canonical form (see {@link canonicalToolsHash}), so no
19
26
  * hash can be produced for it. What that means depends on whether a pin exists:
@@ -70,4 +77,15 @@ export declare function canonicalToolsHash(tools: ToolDef[]): string;
70
77
  * throwing, for call sites that record a hash rather than enforce one.
71
78
  */
72
79
  export declare function tryCanonicalToolsHash(tools: ToolDef[]): string | undefined;
80
+ /**
81
+ * sha256 over the server's launch identity: transport, command, args, url, name.
82
+ *
83
+ * Deliberately NOT the whole `McpServerRef`: `catalog` is provenance rather than
84
+ * identity (the origin gate is what has an opinion about it), and `env` holds
85
+ * secrets that must not be hashed into a value stored on disk next to the pin.
86
+ *
87
+ * Missing fields serialize as empty rather than being omitted, so a server that
88
+ * gains a `url` reads as a change instead of hashing the same as before.
89
+ */
90
+ export declare function serverIdentityHash(server: McpServerRef): string;
73
91
  //# sourceMappingURL=pinning.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pinning.d.ts","sourceRoot":"","sources":["../src/pinning.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EAER,OAAO,EAEP,UAAU,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,UAAU;IAGhC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,QAAQ,CAAC,IAAI,aAAa;gBAEG,KAAK,EAAE,QAAQ;IAEtC,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+CjE;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IAgCnB;;;;;;;OAOG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CASjE;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,wCAAwC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAS3D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS,CAO1E"}
1
+ {"version":3,"file":"pinning.d.ts","sourceRoot":"","sources":["../src/pinning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EACZ,QAAQ,EAER,OAAO,EAEP,UAAU,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,UAAU;IAGhC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,QAAQ,CAAC,IAAI,aAAa;gBAEG,KAAK,EAAE,QAAQ;IAEtC,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqDjE;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAerB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IAgCnB;;;;;;;OAOG;IACG,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAUjE;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,wCAAwC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAS3D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS,CAO1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAS/D"}