@mcp-index/sdk 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.
Files changed (55) hide show
  1. package/LICENSE +133 -0
  2. package/README.md +49 -0
  3. package/dist/src/canonical.d.ts +42 -0
  4. package/dist/src/canonical.d.ts.map +1 -0
  5. package/dist/src/canonical.js +231 -0
  6. package/dist/src/canonical.js.map +1 -0
  7. package/dist/src/errors.d.ts +39 -0
  8. package/dist/src/errors.d.ts.map +1 -0
  9. package/dist/src/errors.js +79 -0
  10. package/dist/src/errors.js.map +1 -0
  11. package/dist/src/gate.d.ts +128 -0
  12. package/dist/src/gate.d.ts.map +1 -0
  13. package/dist/src/gate.js +626 -0
  14. package/dist/src/gate.js.map +1 -0
  15. package/dist/src/index.d.ts +26 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/index.js +20 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/lexicon.d.ts +15 -0
  20. package/dist/src/lexicon.d.ts.map +1 -0
  21. package/dist/src/lexicon.js +70 -0
  22. package/dist/src/lexicon.js.map +1 -0
  23. package/dist/src/outcomes.d.ts +67 -0
  24. package/dist/src/outcomes.d.ts.map +1 -0
  25. package/dist/src/outcomes.js +159 -0
  26. package/dist/src/outcomes.js.map +1 -0
  27. package/dist/src/preflight.d.ts +54 -0
  28. package/dist/src/preflight.d.ts.map +1 -0
  29. package/dist/src/preflight.js +0 -0
  30. package/dist/src/preflight.js.map +1 -0
  31. package/dist/src/provenance.d.ts +95 -0
  32. package/dist/src/provenance.d.ts.map +1 -0
  33. package/dist/src/provenance.js +221 -0
  34. package/dist/src/provenance.js.map +1 -0
  35. package/dist/src/render.d.ts +10 -0
  36. package/dist/src/render.d.ts.map +1 -0
  37. package/dist/src/render.js +38 -0
  38. package/dist/src/render.js.map +1 -0
  39. package/dist/src/risk.d.ts +32 -0
  40. package/dist/src/risk.d.ts.map +1 -0
  41. package/dist/src/risk.js +179 -0
  42. package/dist/src/risk.js.map +1 -0
  43. package/dist/src/scan.d.ts +15 -0
  44. package/dist/src/scan.d.ts.map +1 -0
  45. package/dist/src/scan.js +88 -0
  46. package/dist/src/scan.js.map +1 -0
  47. package/dist/src/schemaDiff.d.ts +36 -0
  48. package/dist/src/schemaDiff.d.ts.map +1 -0
  49. package/dist/src/schemaDiff.js +448 -0
  50. package/dist/src/schemaDiff.js.map +1 -0
  51. package/dist/src/wrap.d.ts +77 -0
  52. package/dist/src/wrap.d.ts.map +1 -0
  53. package/dist/src/wrap.js +554 -0
  54. package/dist/src/wrap.js.map +1 -0
  55. package/package.json +44 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Provenance — the immutable record attached to EVERY surfaced finding, and the
3
+ * load-bearing `requireProvenance` choke. A port of Python
4
+ * `tooling/cse/provenance.py` + `guardrails.require_provenance`. NOTHING surfaces
5
+ * without a COMPLETE provenance record (the over-claim guard, fail-closed).
6
+ */
7
+ import { hashTool } from "./canonical.js";
8
+ import { mdText } from "./render.js";
9
+ import { Decision } from "./preflight.js";
10
+ import { ChangeKind } from "./schemaDiff.js";
11
+ export var VerdictScope;
12
+ (function (VerdictScope) {
13
+ VerdictScope["STRUCTURAL_ONLY"] = "structural_only";
14
+ VerdictScope["SEMANTIC_ONLY"] = "semantic_only";
15
+ VerdictScope["BEHAVIORAL"] = "behavioral";
16
+ })(VerdictScope || (VerdictScope = {}));
17
+ export var HonestFraming;
18
+ (function (HonestFraming) {
19
+ HonestFraming["CONTRACT_DIFF_NOT_SAFETY"] = "contract-diff not a safety verdict";
20
+ HonestFraming["BEHAVIORAL_NEEDED"] = "static inconclusive; behavioral validation needed";
21
+ HonestFraming["VISIBILITY_NOT_BLAME"] = "consumer-visibility not maintainer-blame";
22
+ HonestFraming["HEALTHY"] = "";
23
+ })(HonestFraming || (HonestFraming = {}));
24
+ const MAX_EVIDENCE_FIELD = 4096;
25
+ const TRUNC_MARK = "…[truncated]";
26
+ function bounded(s) {
27
+ if (s.length <= MAX_EVIDENCE_FIELD)
28
+ return s;
29
+ return s.slice(0, MAX_EVIDENCE_FIELD) + TRUNC_MARK;
30
+ }
31
+ /** The completeness predicate the load-bearing choke gates on (port of
32
+ * `Provenance.is_complete`). Each string field is trimmed before the check, so a
33
+ * whitespace-only value is rejected as empty. Evidence MAY be empty. */
34
+ export function isComplete(p) {
35
+ const s = p.subject;
36
+ const wf = p.whatFired;
37
+ return Boolean(s.serverId.trim() &&
38
+ s.tool.trim() &&
39
+ s.capturedAt.trim() &&
40
+ s.source.trim() &&
41
+ wf.classifierId.trim() &&
42
+ p.reproduceHandle.trim());
43
+ }
44
+ /** Render-safe one-line summary (does NOT emit raw evidence values). */
45
+ export function renderProvenance(p) {
46
+ const sid = mdText(p.subject.serverId);
47
+ const tool = mdText(p.subject.tool);
48
+ const kinds = [...new Set(p.whatFired.changeKinds.map((k) => k.toString()))].sort().join(", ");
49
+ const framing = p.honestFraming || "healthy";
50
+ return (`${sid}/${tool} — ${p.whatFired.verdict} ` +
51
+ `(tier ${p.whatFired.tierReached}, scope ${p.whatFired.verdictScope}): ` +
52
+ `${kinds || "no change"} — ${framing}`);
53
+ }
54
+ export function provenanceAsDict(p) {
55
+ return {
56
+ subject: {
57
+ server_id: p.subject.serverId,
58
+ tool: p.subject.tool,
59
+ definition_hash_old: p.subject.definitionHashOld,
60
+ definition_hash_new: p.subject.definitionHashNew,
61
+ captured_at: p.subject.capturedAt,
62
+ source: p.subject.source,
63
+ },
64
+ what_fired: {
65
+ change_kinds: p.whatFired.changeKinds.map((k) => k.toString()),
66
+ classifier_id: p.whatFired.classifierId,
67
+ tier_reached: p.whatFired.tierReached,
68
+ verdict: p.whatFired.verdict,
69
+ verdict_scope: p.whatFired.verdictScope,
70
+ },
71
+ evidence: p.evidence.map((e) => ({
72
+ path: e.path,
73
+ kind: e.kind.toString(),
74
+ before: e.before,
75
+ after: e.after,
76
+ })),
77
+ honest_framing: p.honestFraming,
78
+ reproduce_handle: p.reproduceHandle,
79
+ };
80
+ }
81
+ function scopeForTier(tier) {
82
+ if (tier >= 3)
83
+ return VerdictScope.BEHAVIORAL;
84
+ if (tier === 2)
85
+ return VerdictScope.SEMANTIC_ONLY;
86
+ return VerdictScope.STRUCTURAL_ONLY;
87
+ }
88
+ function framingFor(verdict) {
89
+ if (verdict.decision === Decision.INCONCLUSIVE)
90
+ return HonestFraming.BEHAVIORAL_NEEDED;
91
+ if (verdict.decision === Decision.HOLD || verdict.drift.length > 0)
92
+ return HonestFraming.CONTRACT_DIFF_NOT_SAFETY;
93
+ return HonestFraming.HEALTHY;
94
+ }
95
+ function evidenceFromDrift(drift, descBefore, descAfter) {
96
+ const fields = [];
97
+ const hasDescChange = drift.some((c) => c.kind === ChangeKind.DESCRIPTION_ONLY);
98
+ for (const c of drift) {
99
+ let before = null;
100
+ let after = null;
101
+ if (c.kind === ChangeKind.DESCRIPTION_ONLY) {
102
+ before = descBefore !== null ? bounded(descBefore) : null;
103
+ after = descAfter !== null ? bounded(descAfter) : null;
104
+ }
105
+ if (after === null && before === null) {
106
+ after = bounded(c.detail);
107
+ }
108
+ fields.push({ path: c.path, kind: c.kind, before, after });
109
+ }
110
+ if (!hasDescChange && (descBefore !== null || descAfter !== null)) {
111
+ fields.push({
112
+ path: "description",
113
+ kind: ChangeKind.DESCRIPTION_ONLY,
114
+ before: descBefore !== null ? bounded(descBefore) : null,
115
+ after: descAfter !== null ? bounded(descAfter) : null,
116
+ });
117
+ }
118
+ return fields;
119
+ }
120
+ function hashOrNull(defn) {
121
+ if (defn === null)
122
+ return null;
123
+ try {
124
+ return hashTool(defn);
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ /** Build a COMPLETE Provenance from a verdict + the contracts that produced it.
131
+ * Port of `provenance.from_verdict`. */
132
+ export function fromVerdict(verdict, opts) {
133
+ const classifierId = opts.classifierId ?? "gate.static-drift";
134
+ const source = opts.source ?? "gate";
135
+ const oldHash = hashOrNull(opts.oldSchema);
136
+ const newHash = hashOrNull(opts.newSchema);
137
+ const scope = scopeForTier(opts.tierReached);
138
+ const now = opts.capturedAt ?? new Date().toISOString();
139
+ const kinds = verdict.drift.map((c) => c.kind);
140
+ const evidence = evidenceFromDrift(verdict.drift, verdict.descBefore, verdict.descAfter);
141
+ const handle = `${classifierId}|tier=${opts.tierReached}|` +
142
+ `old=${oldHash ?? "∅"}|new=${newHash ?? "∅"}|` +
143
+ `verdict=${verdict.decision}`;
144
+ return {
145
+ subject: {
146
+ serverId: opts.serverId,
147
+ tool: opts.tool,
148
+ definitionHashOld: oldHash,
149
+ definitionHashNew: newHash,
150
+ capturedAt: now,
151
+ source,
152
+ },
153
+ whatFired: {
154
+ changeKinds: kinds,
155
+ classifierId,
156
+ tierReached: opts.tierReached,
157
+ verdict: verdict.decision,
158
+ verdictScope: scope,
159
+ },
160
+ evidence,
161
+ honestFraming: framingFor(verdict),
162
+ reproduceHandle: handle,
163
+ };
164
+ }
165
+ export class GuardrailError extends Error {
166
+ }
167
+ /** THE choke every surface path passes through. Returns the COMPLETE Provenance,
168
+ * or THROWS `GuardrailError` (fail-closed -> nothing surfaces). Port of
169
+ * `guardrails.require_provenance`. */
170
+ export function requireProvenance(prov) {
171
+ if (prov === null || prov === undefined) {
172
+ throw new GuardrailError("no provenance attached — fail-closed: nothing may surface without a complete provenance record");
173
+ }
174
+ if (!isComplete(prov)) {
175
+ throw new GuardrailError("provenance record is INCOMPLETE (missing a load-bearing field) — fail-closed, nothing surfaces");
176
+ }
177
+ return prov;
178
+ }
179
+ /** Build provenance for a USER-FACING surface and run it through the choke.
180
+ * Returns the COMPLETE record, or null when it cannot be built / the choke
181
+ * refuses (fail-closed). Port of `provenance.surface_provenance`. */
182
+ export function surfaceProvenance(verdict, opts) {
183
+ try {
184
+ const prov = fromVerdict(verdict, opts);
185
+ return requireProvenance(prov);
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
191
+ const GREEN_WORDS = ["safe", "verified", "clean", "pass", "passed", "all clear", "all-clear", "secure", "trusted"];
192
+ const GREEN_RE = new RegExp("(?<![\\w-])(" + GREEN_WORDS.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|") + ")(?![\\w-])", "i");
193
+ /** Fail-closed honest-voice check: throw if a consumer string uses a banned
194
+ * green word. Port of `guardrails.assert_no_green_words`. */
195
+ export function assertNoGreenWords(text) {
196
+ const m = GREEN_RE.exec(text);
197
+ if (m !== null) {
198
+ throw new GuardrailError(`consumer output contains a banned 'green word': '${m[1]}'`);
199
+ }
200
+ }
201
+ // The neutral placeholder a green word is REDACTED to (FIX 4 / M3). An attacker can
202
+ // NAME a tool with a green word ("all-clear-verified-scanner") to smuggle banned
203
+ // reassurance into a HOLD banner. We REDACT the offending token (NOT refuse the whole
204
+ // HOLD — a refusal would let an attacker SUPPRESS a legitimate HOLD by naming their
205
+ // tool "safe"). The placeholder carries no green word itself.
206
+ const REDACTED_GREEN_WORD = "[redacted]";
207
+ // A GLOBAL variant of the green-word matcher, built from the SAME `GREEN_WORDS`
208
+ // source so the redaction word-list can never drift from `assertNoGreenWords`. A
209
+ // separate object (with the `g` flag) is used for `.replace`, keeping the stateful
210
+ // `lastIndex` of a global regex out of `assertNoGreenWords`'s `.exec`.
211
+ const GREEN_RE_GLOBAL = new RegExp(GREEN_RE.source, "gi");
212
+ /** Replace every banned green word in a consumer-facing string with a neutral
213
+ * placeholder, so an ATTACKER-DERIVED token (a tool name interpolated into a HOLD
214
+ * banner) cannot surface banned reassurance verbatim (M3). Whole-word,
215
+ * case-insensitive — reuses the SAME word-list as `assertNoGreenWords`, so the two
216
+ * can never disagree. The output passes `assertNoGreenWords`. Port of
217
+ * `gate.redact_green_words`. */
218
+ export function redactGreenWords(text) {
219
+ return text.replace(GREEN_RE_GLOBAL, REDACTED_GREEN_WORD);
220
+ }
221
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../src/provenance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Render-safe text neutralization — a port of Python `report._md_text` +
3
+ * `_strip_control`. Strips ALL C0/C1/DEL control bytes (incl. ESC `\x1b`, the
4
+ * ANSI cursor/colour-escape banner-forgery vector) FIRST, then HTML-escapes,
5
+ * then backslash-escapes markdown metacharacters, then folds CR/LF to spaces.
6
+ * Always embed the result mid-line.
7
+ */
8
+ /** Neutralize untrusted text for inline embedding in a markdown/terminal banner. */
9
+ export declare function mdText(s: string): string;
10
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyBH,oFAAoF;AACpF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CASxC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Render-safe text neutralization — a port of Python `report._md_text` +
3
+ * `_strip_control`. Strips ALL C0/C1/DEL control bytes (incl. ESC `\x1b`, the
4
+ * ANSI cursor/colour-escape banner-forgery vector) FIRST, then HTML-escapes,
5
+ * then backslash-escapes markdown metacharacters, then folds CR/LF to spaces.
6
+ * Always embed the result mid-line.
7
+ */
8
+ // C0/C1 + DEL, EXCLUDING \t \n \r (handled as whitespace). ESC (\x1b) is in set.
9
+ // eslint-disable-next-line no-control-regex
10
+ const CONTROL_BYTES = /[\x00-\x08\x0b-\x1f\x7f]/g;
11
+ // Mirror Python report._MD_SPECIAL EXACTLY ("\\`*_[]()|~"). Escaping MORE than Python (e.g. `-`)
12
+ // breaks green-word redaction parity: `all-clear` would become `all\-clear`, which GREEN_RE no
13
+ // longer matches, so the green word survives un-redacted (TS diverging from Python). Keep this set
14
+ // identical to Python so both md-escaping AND green-word redaction stay in parity.
15
+ const MD_SPECIAL = ["\\", "`", "*", "_", "[", "]", "(", ")", "|", "~"];
16
+ function htmlEscape(s) {
17
+ return s
18
+ .replace(/&/g, "&amp;")
19
+ .replace(/</g, "&lt;")
20
+ .replace(/>/g, "&gt;")
21
+ .replace(/"/g, "&quot;")
22
+ .replace(/'/g, "&#x27;");
23
+ }
24
+ function stripControl(s) {
25
+ return s.replace(CONTROL_BYTES, "").replace(/\t/g, " ");
26
+ }
27
+ /** Neutralize untrusted text for inline embedding in a markdown/terminal banner. */
28
+ export function mdText(s) {
29
+ let out = stripControl(s);
30
+ out = htmlEscape(out);
31
+ // backslash must be escaped via MD_SPECIAL (it's first in the list), but
32
+ // htmlEscape already ran; replicate Python order (escape each MD special).
33
+ for (const ch of MD_SPECIAL) {
34
+ out = out.split(ch).join("\\" + ch);
35
+ }
36
+ return out.replace(/\r/g, " ").replace(/\n/g, " ");
37
+ }
38
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,iFAAiF;AACjF,4CAA4C;AAC5C,MAAM,aAAa,GAAG,2BAA2B,CAAC;AAElD,iGAAiG;AACjG,+FAA+F;AAC/F,mGAAmG;AACnG,mFAAmF;AACnF,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEvE,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,IAAI,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1B,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IACtB,yEAAyE;IACzE,2EAA2E;IAC3E,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Structural risk leg — a port of Python `tooling/cse/risk.py`. Pure,
3
+ * deterministic, total (never throws on a malformed/hostile schema), fail-closed
4
+ * (absence of a positive read signal resolves toward higher risk). The gate uses
5
+ * it for the Gate-3 risk-escalation check on an otherwise-benign drift.
6
+ */
7
+ export declare enum BlastRadius {
8
+ LOW = "low",
9
+ MED = "med",
10
+ HIGH = "high"
11
+ }
12
+ export declare enum DomainClass {
13
+ PAYMENTS = "payments",
14
+ INFRA = "infra",
15
+ COMMS = "comms",
16
+ DATA = "data",
17
+ UNKNOWN = "unknown"
18
+ }
19
+ export declare function blastRank(blast: BlastRadius): number;
20
+ export interface RiskAssessment {
21
+ readonly nameWriteish: boolean;
22
+ readonly hasWritePayloadParam: boolean;
23
+ readonly requiresCredential: boolean;
24
+ readonly sideEffectIntent: boolean;
25
+ readonly domainClass: DomainClass;
26
+ readonly blastRadius: BlastRadius;
27
+ readonly looksReadOnly: boolean;
28
+ readonly signals: readonly string[];
29
+ }
30
+ /** Structural risk assessment (port of `risk.assess`). */
31
+ export declare function assess(name: string, description: string, inputSchema: unknown): RiskAssessment;
32
+ //# sourceMappingURL=risk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk.d.ts","sourceRoot":"","sources":["../../src/risk.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2CH,oBAAY,WAAW;IACrB,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,IAAI,SAAS;CACd;AAED,oBAAY,WAAW;IACrB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,IAAI,SAAS;IACb,OAAO,YAAY;CACpB;AAQD,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEpD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAsED,0DAA0D;AAC1D,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,GACnB,cAAc,CAwDhB"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Structural risk leg — a port of Python `tooling/cse/risk.py`. Pure,
3
+ * deterministic, total (never throws on a malformed/hostile schema), fail-closed
4
+ * (absence of a positive read signal resolves toward higher risk). The gate uses
5
+ * it for the Gate-3 risk-escalation check on an otherwise-benign drift.
6
+ */
7
+ import { nameTokens, sideEffectStems, nameIsWriteish, hasSideEffectIntent } from "./lexicon.js";
8
+ const MAX_TEXT = 8192;
9
+ const WALK_MAX_NODES = 2000;
10
+ const WALK_HARD_DEPTH = 12;
11
+ const WRITE_PAYLOAD_PARAMS = new Set(("content body payload file attachment upload destination recipient amount " +
12
+ "email message sms subject html markdown document blob bytes " +
13
+ "command cmd code script exec eval sql shell expr").split(" "));
14
+ const CREDENTIAL_TOKENS = new Set(("token apikey credential credentials secret bearer oauth password passwd " +
15
+ "auth authorization key").split(" "));
16
+ const DOMAIN_TOKENS = {
17
+ payments: new Set(("pay payment charge invoice billing wallet refund transaction transfer " +
18
+ "deposit withdraw checkout purchase escrow disburse settle").split(" ")),
19
+ infra: new Set(("deploy provision server container cluster instance kubernetes k8s vm " +
20
+ "node terminate reboot scale infra infrastructure dns").split(" ")),
21
+ comms: new Set(("email mail message send notify notification sms slack webhook post tweet " +
22
+ "publish broadcast dm").split(" ")),
23
+ data: new Set(("read get list query fetch search lookup describe show view data record " +
24
+ "index dataset row table report metric stats history").split(" ")),
25
+ };
26
+ const DOMAIN_PRECEDENCE = ["payments", "infra", "comms", "data"];
27
+ export var BlastRadius;
28
+ (function (BlastRadius) {
29
+ BlastRadius["LOW"] = "low";
30
+ BlastRadius["MED"] = "med";
31
+ BlastRadius["HIGH"] = "high";
32
+ })(BlastRadius || (BlastRadius = {}));
33
+ export var DomainClass;
34
+ (function (DomainClass) {
35
+ DomainClass["PAYMENTS"] = "payments";
36
+ DomainClass["INFRA"] = "infra";
37
+ DomainClass["COMMS"] = "comms";
38
+ DomainClass["DATA"] = "data";
39
+ DomainClass["UNKNOWN"] = "unknown";
40
+ })(DomainClass || (DomainClass = {}));
41
+ const BLAST_RANK = {
42
+ [BlastRadius.LOW]: 0,
43
+ [BlastRadius.MED]: 1,
44
+ [BlastRadius.HIGH]: 2,
45
+ };
46
+ export function blastRank(blast) {
47
+ return BLAST_RANK[blast];
48
+ }
49
+ function stemmed(tokens) {
50
+ const out = new Set();
51
+ for (const t of tokens) {
52
+ for (const s of sideEffectStems(t))
53
+ out.add(s);
54
+ }
55
+ return out;
56
+ }
57
+ function intersect(a, b) {
58
+ const out = new Set();
59
+ for (const x of a)
60
+ if (b.has(x))
61
+ out.add(x);
62
+ return out;
63
+ }
64
+ function isDict(v) {
65
+ return v !== null && typeof v === "object" && !Array.isArray(v);
66
+ }
67
+ function schemaParamTokens(inputSchema) {
68
+ const tokens = new Set();
69
+ const budget = { n: WALK_MAX_NODES };
70
+ let truncated = false;
71
+ const walk = (node, depth) => {
72
+ if (budget.n <= 0 || depth >= WALK_HARD_DEPTH) {
73
+ truncated = true;
74
+ return;
75
+ }
76
+ budget.n -= 1;
77
+ if (!isDict(node))
78
+ return;
79
+ const props = node["properties"];
80
+ if (isDict(props)) {
81
+ for (const [key, sub] of Object.entries(props)) {
82
+ for (const t of nameTokens(key))
83
+ tokens.add(t);
84
+ walk(sub, depth + 1);
85
+ }
86
+ }
87
+ const items = node["items"];
88
+ if (isDict(items))
89
+ walk(items, depth + 1);
90
+ else if (Array.isArray(items))
91
+ for (const it of items)
92
+ walk(it, depth + 1);
93
+ for (const comp of ["oneOf", "anyOf", "allOf", "prefixItems"]) {
94
+ const branches = node[comp];
95
+ if (Array.isArray(branches))
96
+ for (const it of branches)
97
+ walk(it, depth + 1);
98
+ }
99
+ const ap = node["additionalProperties"];
100
+ if (isDict(ap))
101
+ walk(ap, depth + 1);
102
+ };
103
+ if (isDict(inputSchema))
104
+ walk(inputSchema, 0);
105
+ return [tokens, truncated];
106
+ }
107
+ function classifyDomain(ndTokens) {
108
+ const stems = stemmed(ndTokens);
109
+ for (const dom of DOMAIN_PRECEDENCE) {
110
+ if ([...stems].some((s) => DOMAIN_TOKENS[dom].has(s))) {
111
+ return dom;
112
+ }
113
+ }
114
+ return DomainClass.UNKNOWN;
115
+ }
116
+ function union(a, b) {
117
+ const out = new Set(a);
118
+ for (const x of b)
119
+ out.add(x);
120
+ return out;
121
+ }
122
+ /** Structural risk assessment (port of `risk.assess`). */
123
+ export function assess(name, description, inputSchema) {
124
+ name = name.slice(0, MAX_TEXT);
125
+ description = description.slice(0, MAX_TEXT);
126
+ const ndTokens = union(nameTokens(name), nameTokens(description));
127
+ const [paramTokens, truncated] = schemaParamTokens(inputSchema);
128
+ const nameWriteish = nameIsWriteish(name);
129
+ const sideEffectIntent = hasSideEffectIntent(name, description);
130
+ const matchedPayload = intersect(stemmed(paramTokens), WRITE_PAYLOAD_PARAMS);
131
+ const hasWritePayloadParam = matchedPayload.size > 0 || truncated;
132
+ const requiresCredential = intersect(stemmed(union(ndTokens, paramTokens)), CREDENTIAL_TOKENS).size > 0;
133
+ const domainClass = classifyDomain(ndTokens);
134
+ let blast;
135
+ if (domainClass === DomainClass.PAYMENTS || (nameWriteish && requiresCredential)) {
136
+ blast = BlastRadius.HIGH;
137
+ }
138
+ else if (nameWriteish ||
139
+ hasWritePayloadParam ||
140
+ sideEffectIntent ||
141
+ domainClass === DomainClass.INFRA ||
142
+ domainClass === DomainClass.COMMS ||
143
+ domainClass === DomainClass.UNKNOWN) {
144
+ blast = BlastRadius.MED;
145
+ }
146
+ else {
147
+ blast = BlastRadius.LOW;
148
+ }
149
+ const looksReadOnly = domainClass === DomainClass.DATA &&
150
+ !nameWriteish &&
151
+ !hasWritePayloadParam &&
152
+ !sideEffectIntent;
153
+ const signals = [];
154
+ if (nameWriteish)
155
+ signals.push("name carries a mutating verb");
156
+ if (matchedPayload.size > 0)
157
+ signals.push(`write/actionable param present ([${[...matchedPayload].sort().map((s) => `'${s}'`).join(", ")}])`);
158
+ if (truncated)
159
+ signals.push("schema too large/deep to fully analyze (fail-closed)");
160
+ if (sideEffectIntent)
161
+ signals.push("name/description carries side-effecting intent");
162
+ if (requiresCredential)
163
+ signals.push("requires a credential to act");
164
+ if (domainClass !== DomainClass.UNKNOWN)
165
+ signals.push(`domain=${domainClass}`);
166
+ if (looksReadOnly)
167
+ signals.push("structurally read-only (read-ish domain, no write signal)");
168
+ return {
169
+ nameWriteish,
170
+ hasWritePayloadParam,
171
+ requiresCredential,
172
+ sideEffectIntent,
173
+ domainClass,
174
+ blastRadius: blast,
175
+ looksReadOnly,
176
+ signals,
177
+ };
178
+ }
179
+ //# sourceMappingURL=risk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk.js","sourceRoot":"","sources":["../../src/risk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Static schema-content marker scan — a port of the boolean core of Python
3
+ * `trust/schema_scan.py`. We only need "is a hostile marker present?" for the
4
+ * Gate-4 marker check, so this returns a boolean rather than the full
5
+ * DimensionFinding (the Python `scan_schema(...) is not None` predicate).
6
+ *
7
+ * A TRIPWIRE, not a calibrated detector: catches KNOWN hostile markers at high
8
+ * precision, evadable by novel encodings. Same regexes, same string-key walk.
9
+ */
10
+ export declare const STRING_KEYS: ReadonlySet<string>;
11
+ /** True iff the input schema encodes a known hostile marker (credential-path,
12
+ * exfil instruction, or injection directive). Port of
13
+ * `scan_schema(...) is not None`. */
14
+ export declare function scanSchemaHasMarker(inputSchema: Record<string, unknown>): boolean;
15
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAS1C,CAAC;AA+BH;;qCAEqC;AACrC,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CASjF"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Static schema-content marker scan — a port of the boolean core of Python
3
+ * `trust/schema_scan.py`. We only need "is a hostile marker present?" for the
4
+ * Gate-4 marker check, so this returns a boolean rather than the full
5
+ * DimensionFinding (the Python `scan_schema(...) is not None` predicate).
6
+ *
7
+ * A TRIPWIRE, not a calibrated detector: catches KNOWN hostile markers at high
8
+ * precision, evadable by novel encodings. Same regexes, same string-key walk.
9
+ */
10
+ const CREDENTIAL_PATH = new RegExp("(?:[/\\\\~]\\.?(?:ssh|aws|gnupg)\\b)" +
11
+ "|(?:[/\\\\](?:credentials?|secrets?|vault|keys?|private[_-]?keys?)\\b)" +
12
+ "|(?:\\b(?:id_rsa|\\.pem|\\.env|/var/secrets|\\.config/credentials)\\b)", "i");
13
+ const EXFIL_INSTRUCTION = new RegExp("\\b(?:mirror|exfiltrat\\w*|forward|transmit|relay|smuggle|leak|harvest|replicate|stage)\\b" +
14
+ ".{0,48}?\\b(?:to|into|channel|webhook|endpoint|inbox|hook|server|outbound|operator)\\b" +
15
+ "|\\b(?:webhook-delta|slack-hook|discord-relay|teams-forward)\\b" +
16
+ "|https?://", "i");
17
+ const INJECTION = new RegExp("\\b(?:ignore (?:all )?(?:previous|prior)|disregard (?:previous|prior|the above))\\b" +
18
+ "|^\\s*(?:SYSTEM|ADMIN)\\s*:" +
19
+ "|\\[(?:ADMIN|SYSTEM)\\]" +
20
+ "|\\b(?:pre-?approved|mark (?:this|it) (?:as )?(?:safe|benign|trusted))\\b", "im");
21
+ // The schema sub-keys that carry literal strings a hostile author controls. We
22
+ // scan their string VALUES (not the structural keys themselves). MIRRORS the
23
+ // BROADENED Python `trust.schema_scan._STRING_KEYS` — broadened to also cover the
24
+ // agent-/judge-consumed annotation channels `examples`, `$comment`, and
25
+ // `markdownDescription` (a marker smuggled into any of them reaches the model just
26
+ // like one in `description`). Kept as a sorted, exported set so the parity test can
27
+ // assert TS covers EVERY key the live Python scanner covers (no fail-OPEN divergence).
28
+ export const STRING_KEYS = new Set([
29
+ "pattern",
30
+ "default",
31
+ "const",
32
+ "title",
33
+ "description",
34
+ "examples",
35
+ "$comment",
36
+ "markdownDescription",
37
+ ]);
38
+ function isDict(v) {
39
+ return v !== null && typeof v === "object" && !Array.isArray(v);
40
+ }
41
+ function walkStrings(node, out) {
42
+ if (isDict(node)) {
43
+ for (const [k, v] of Object.entries(node)) {
44
+ if (STRING_KEYS.has(k)) {
45
+ // A scanned key whose value is a bare string, OR a LIST of bare strings
46
+ // (e.g. `examples: ["...", "..."]`, `enum`-style value arrays). A hostile
47
+ // author can hide a marker in any list element, so scan each bare string.
48
+ if (typeof v === "string") {
49
+ out.push(v);
50
+ }
51
+ else if (Array.isArray(v)) {
52
+ for (const item of v)
53
+ if (typeof item === "string")
54
+ out.push(item);
55
+ }
56
+ else {
57
+ walkStrings(v, out);
58
+ }
59
+ }
60
+ else if (k === "enum" && Array.isArray(v)) {
61
+ for (const item of v)
62
+ if (typeof item === "string")
63
+ out.push(item);
64
+ }
65
+ else {
66
+ walkStrings(v, out);
67
+ }
68
+ }
69
+ }
70
+ else if (Array.isArray(node)) {
71
+ for (const item of node)
72
+ walkStrings(item, out);
73
+ }
74
+ }
75
+ /** True iff the input schema encodes a known hostile marker (credential-path,
76
+ * exfil instruction, or injection directive). Port of
77
+ * `scan_schema(...) is not None`. */
78
+ export function scanSchemaHasMarker(inputSchema) {
79
+ const strings = [];
80
+ walkStrings(inputSchema, strings);
81
+ for (const s of strings) {
82
+ if (CREDENTIAL_PATH.test(s) || EXFIL_INSTRUCTION.test(s) || INJECTION.test(s)) {
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/scan.ts"],"names":[],"mappings":"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"}