mcptrustchecker 1.0.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 +379 -0
- package/dist/acquire/clientConfig.d.ts +47 -0
- package/dist/acquire/clientConfig.js +124 -0
- package/dist/acquire/discover.d.ts +12 -0
- package/dist/acquire/discover.js +53 -0
- package/dist/acquire/index.d.ts +24 -0
- package/dist/acquire/index.js +175 -0
- package/dist/acquire/live.d.ts +47 -0
- package/dist/acquire/live.js +357 -0
- package/dist/acquire/manifest.d.ts +10 -0
- package/dist/acquire/manifest.js +94 -0
- package/dist/acquire/npm.d.ts +10 -0
- package/dist/acquire/npm.js +55 -0
- package/dist/acquire/source.d.ts +16 -0
- package/dist/acquire/source.js +109 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.js +396 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +81 -0
- package/dist/data/capabilityLexicon.d.ts +51 -0
- package/dist/data/capabilityLexicon.js +204 -0
- package/dist/data/confusables.d.ts +17 -0
- package/dist/data/confusables.js +65 -0
- package/dist/data/injectionPatterns.d.ts +55 -0
- package/dist/data/injectionPatterns.js +216 -0
- package/dist/data/knownCves.d.ts +24 -0
- package/dist/data/knownCves.js +49 -0
- package/dist/data/protectedPackages.d.ts +30 -0
- package/dist/data/protectedPackages.js +89 -0
- package/dist/data/ruleCatalog.d.ts +16 -0
- package/dist/data/ruleCatalog.js +94 -0
- package/dist/data/sourcePatterns.d.ts +25 -0
- package/dist/data/sourcePatterns.js +92 -0
- package/dist/data/unicode.d.ts +34 -0
- package/dist/data/unicode.js +99 -0
- package/dist/detectors/capability.d.ts +14 -0
- package/dist/detectors/capability.js +281 -0
- package/dist/detectors/collision.d.ts +11 -0
- package/dist/detectors/collision.js +63 -0
- package/dist/detectors/flowGraph.d.ts +43 -0
- package/dist/detectors/flowGraph.js +100 -0
- package/dist/detectors/index.d.ts +18 -0
- package/dist/detectors/index.js +26 -0
- package/dist/detectors/injection.d.ts +9 -0
- package/dist/detectors/injection.js +178 -0
- package/dist/detectors/meta.d.ts +8 -0
- package/dist/detectors/meta.js +33 -0
- package/dist/detectors/posture.d.ts +9 -0
- package/dist/detectors/posture.js +128 -0
- package/dist/detectors/source.d.ts +12 -0
- package/dist/detectors/source.js +87 -0
- package/dist/detectors/supplyChain.d.ts +17 -0
- package/dist/detectors/supplyChain.js +243 -0
- package/dist/detectors/toxicFlow.d.ts +22 -0
- package/dist/detectors/toxicFlow.js +201 -0
- package/dist/detectors/unicode.d.ts +21 -0
- package/dist/detectors/unicode.js +244 -0
- package/dist/engine.d.ts +27 -0
- package/dist/engine.js +135 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +46 -0
- package/dist/lockfile.d.ts +30 -0
- package/dist/lockfile.js +87 -0
- package/dist/policy.d.ts +13 -0
- package/dist/policy.js +48 -0
- package/dist/report/badge.d.ts +14 -0
- package/dist/report/badge.js +18 -0
- package/dist/report/json.d.ts +4 -0
- package/dist/report/json.js +4 -0
- package/dist/report/markdown.d.ts +6 -0
- package/dist/report/markdown.js +110 -0
- package/dist/report/sarif.d.ts +7 -0
- package/dist/report/sarif.js +105 -0
- package/dist/report/terminal.d.ts +15 -0
- package/dist/report/terminal.js +252 -0
- package/dist/scoring/capability.d.ts +10 -0
- package/dist/scoring/capability.js +48 -0
- package/dist/scoring/index.d.ts +10 -0
- package/dist/scoring/index.js +96 -0
- package/dist/scoring/model.d.ts +50 -0
- package/dist/scoring/model.js +101 -0
- package/dist/types.d.ts +361 -0
- package/dist/types.js +12 -0
- package/dist/util/ansi.d.ts +26 -0
- package/dist/util/ansi.js +42 -0
- package/dist/util/capabilities.d.ts +17 -0
- package/dist/util/capabilities.js +68 -0
- package/dist/util/distance.d.ts +23 -0
- package/dist/util/distance.js +119 -0
- package/dist/util/hash.d.ts +20 -0
- package/dist/util/hash.js +80 -0
- package/dist/util/text.d.ts +45 -0
- package/dist/util/text.js +170 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.js +13 -0
- package/package.json +72 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* Capability lexicon — the vocabulary that maps a tool's name, description
|
|
4
|
+
* verbs, and parameter shape onto the roles used by the toxic-flow graph.
|
|
5
|
+
*
|
|
6
|
+
* The trifecta (the lethal-trifecta toxic flow) requires three roles
|
|
7
|
+
* to be simultaneously reachable in one agent session:
|
|
8
|
+
* UNTRUSTED_INPUT → SENSITIVE_SOURCE → EXTERNAL_SINK
|
|
9
|
+
* We deliberately keep UNTRUSTED_INPUT and SENSITIVE_SOURCE separate — merging
|
|
10
|
+
* them (as a naive "sensitive source") both over- and under-flags.
|
|
11
|
+
*/
|
|
12
|
+
export const CAPABILITY_SIGNALS = [
|
|
13
|
+
{
|
|
14
|
+
tag: 'untrusted-input',
|
|
15
|
+
keywords: [
|
|
16
|
+
'fetch_url',
|
|
17
|
+
'fetch url',
|
|
18
|
+
'fetch',
|
|
19
|
+
'browse',
|
|
20
|
+
'read_issue',
|
|
21
|
+
'read issue',
|
|
22
|
+
'read_pr',
|
|
23
|
+
'read pull request',
|
|
24
|
+
'read_email',
|
|
25
|
+
'read email',
|
|
26
|
+
'read_ticket',
|
|
27
|
+
'read_comment',
|
|
28
|
+
'read comment',
|
|
29
|
+
'web_search',
|
|
30
|
+
'web search',
|
|
31
|
+
'search web',
|
|
32
|
+
'get_page',
|
|
33
|
+
'scrape',
|
|
34
|
+
'crawl',
|
|
35
|
+
'read_message',
|
|
36
|
+
'read_dm',
|
|
37
|
+
'download',
|
|
38
|
+
'rss',
|
|
39
|
+
'read_webhook',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
tag: 'sensitive-source',
|
|
44
|
+
keywords: [
|
|
45
|
+
'read_file',
|
|
46
|
+
'read file',
|
|
47
|
+
'readfile',
|
|
48
|
+
'get_file_contents',
|
|
49
|
+
'cat_file',
|
|
50
|
+
'read_repo',
|
|
51
|
+
'read_env',
|
|
52
|
+
'read environment',
|
|
53
|
+
'get_env',
|
|
54
|
+
'query_db',
|
|
55
|
+
'query database',
|
|
56
|
+
'run_sql',
|
|
57
|
+
'execute_sql',
|
|
58
|
+
'sql_query',
|
|
59
|
+
'get_logs',
|
|
60
|
+
'read_logs',
|
|
61
|
+
'list_secrets',
|
|
62
|
+
'get_secret',
|
|
63
|
+
'read_secret',
|
|
64
|
+
'read_config',
|
|
65
|
+
'list_keys',
|
|
66
|
+
'get_credentials',
|
|
67
|
+
'get_token',
|
|
68
|
+
'dump',
|
|
69
|
+
'export_data',
|
|
70
|
+
'read_private',
|
|
71
|
+
'list_files',
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
tag: 'external-sink',
|
|
76
|
+
keywords: [
|
|
77
|
+
'http_request',
|
|
78
|
+
'http request',
|
|
79
|
+
'post',
|
|
80
|
+
'put_request',
|
|
81
|
+
'send_email',
|
|
82
|
+
'send email',
|
|
83
|
+
'sendmail',
|
|
84
|
+
'send_message',
|
|
85
|
+
'send message',
|
|
86
|
+
'post_message',
|
|
87
|
+
'webhook',
|
|
88
|
+
'create_pull_request',
|
|
89
|
+
'open_pr',
|
|
90
|
+
'create_issue',
|
|
91
|
+
'create_comment',
|
|
92
|
+
'publish',
|
|
93
|
+
'upload',
|
|
94
|
+
'curl',
|
|
95
|
+
'request',
|
|
96
|
+
'notify',
|
|
97
|
+
'tweet',
|
|
98
|
+
'slack_post',
|
|
99
|
+
'sms',
|
|
100
|
+
'call_api',
|
|
101
|
+
'forward',
|
|
102
|
+
'export_to',
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
tag: 'code-exec',
|
|
107
|
+
keywords: [
|
|
108
|
+
// NOTE: bare 'system'/'command'/'terminal' are intentionally excluded —
|
|
109
|
+
// they match benign text like "file system" or "the command completed".
|
|
110
|
+
// Command execution is caught by the specific verbs below plus the
|
|
111
|
+
// code-exec parameter-name signals.
|
|
112
|
+
'exec',
|
|
113
|
+
'execute command',
|
|
114
|
+
'execute code',
|
|
115
|
+
'execute a command',
|
|
116
|
+
'execute shell',
|
|
117
|
+
'execute the command',
|
|
118
|
+
'run_command',
|
|
119
|
+
'run command',
|
|
120
|
+
'shell',
|
|
121
|
+
'bash',
|
|
122
|
+
'eval',
|
|
123
|
+
'run_code',
|
|
124
|
+
'run_script',
|
|
125
|
+
'run_shell',
|
|
126
|
+
'spawn',
|
|
127
|
+
'subprocess',
|
|
128
|
+
'powershell',
|
|
129
|
+
'python_exec',
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
tag: 'file-write',
|
|
134
|
+
keywords: [
|
|
135
|
+
'write_file',
|
|
136
|
+
'write file',
|
|
137
|
+
'writefile',
|
|
138
|
+
'create_file',
|
|
139
|
+
'edit_file',
|
|
140
|
+
'delete_file',
|
|
141
|
+
'remove_file',
|
|
142
|
+
'move_file',
|
|
143
|
+
'append_file',
|
|
144
|
+
'save_file',
|
|
145
|
+
'overwrite',
|
|
146
|
+
'rmdir',
|
|
147
|
+
'unlink',
|
|
148
|
+
'put_object',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
/**
|
|
153
|
+
* Parameter-name → tag hints (schema-shape signals). Deliberately conservative:
|
|
154
|
+
* generic names like `body`/`data`/`content` are NOT mapped (an HTTP body is not
|
|
155
|
+
* a file write) — file mutation is detected from tool-name verbs instead, which
|
|
156
|
+
* avoids the most common capability false positive.
|
|
157
|
+
*/
|
|
158
|
+
export const PARAM_NAME_SIGNALS = [
|
|
159
|
+
{ tag: 'external-sink', names: ['url', 'endpoint', 'webhook', 'uri', 'recipient', 'destination'] },
|
|
160
|
+
{ tag: 'code-exec', names: ['command', 'cmd', 'script', 'shell', 'exec', 'eval'] },
|
|
161
|
+
// 'query' is intentionally omitted — a search "query" is not sensitive-data
|
|
162
|
+
// access; real DB reads are caught by tool-name verbs (query_db, run_sql, …).
|
|
163
|
+
{ tag: 'sensitive-source', names: ['path', 'filepath', 'filename', 'secret', 'sql'] },
|
|
164
|
+
];
|
|
165
|
+
/** URL/host-shaped parameter names — an unconstrained one is an SSRF precondition. */
|
|
166
|
+
export const SSRF_PARAM_NAMES = ['url', 'uri', 'host', 'endpoint', 'target', 'server', 'address', 'hostname'];
|
|
167
|
+
/** Path-shaped parameter names — an unconstrained one is a path-traversal precondition. */
|
|
168
|
+
export const PATH_PARAM_NAMES = ['path', 'filepath', 'filename', 'file', 'dir', 'directory'];
|
|
169
|
+
/** Command-shaped parameter names — an unconstrained one is a command-injection precondition. */
|
|
170
|
+
export const COMMAND_PARAM_NAMES = ['command', 'cmd', 'script', 'shell', 'exec', 'eval', 'code'];
|
|
171
|
+
/**
|
|
172
|
+
* Field names that must never be collected via MCP elicitation (the spec
|
|
173
|
+
* forbids it). Their presence alongside a declared elicitation capability is a
|
|
174
|
+
* credential-phishing signal.
|
|
175
|
+
*/
|
|
176
|
+
export const SECRET_FIELD_NAMES = [
|
|
177
|
+
'password',
|
|
178
|
+
'passwd',
|
|
179
|
+
'api_key',
|
|
180
|
+
'apikey',
|
|
181
|
+
'token',
|
|
182
|
+
'secret',
|
|
183
|
+
'ssn',
|
|
184
|
+
'seed_phrase',
|
|
185
|
+
'seedphrase',
|
|
186
|
+
'mnemonic',
|
|
187
|
+
'private_key',
|
|
188
|
+
'privatekey',
|
|
189
|
+
'credit_card',
|
|
190
|
+
'creditcard',
|
|
191
|
+
'cvv',
|
|
192
|
+
'pin',
|
|
193
|
+
];
|
|
194
|
+
/**
|
|
195
|
+
* The virtual "client built-in" tools. When `includeBuiltins` is set, the
|
|
196
|
+
* toxic-flow graph assumes the host agent also exposes generic web-fetch (an
|
|
197
|
+
* untrusted-input source) and generic file/network tools (sinks), which can
|
|
198
|
+
* complete a trifecta together with a single-capability server.
|
|
199
|
+
*/
|
|
200
|
+
export const CLIENT_BUILTINS = [
|
|
201
|
+
{ tool: '<client:web-fetch>', tags: ['untrusted-input'] },
|
|
202
|
+
{ tool: '<client:filesystem>', tags: ['sensitive-source', 'file-write'] },
|
|
203
|
+
{ tool: '<client:shell>', tags: ['code-exec', 'external-sink'] },
|
|
204
|
+
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* A curated subset of the UTS #39 confusables mapping (homoglyph → ASCII
|
|
4
|
+
* skeleton). This is intentionally small and high-precision: it covers the
|
|
5
|
+
* Latin-lookalike Cyrillic / Greek / fullwidth / digit-lookalike characters
|
|
6
|
+
* that matter for package-name and command-keyword squatting. It is NOT a full
|
|
7
|
+
* confusables.txt — the mixed-script detector (using Unicode script properties)
|
|
8
|
+
* catches the general case; this map exists to compute a `skeleton()` for the
|
|
9
|
+
* typosquat watchlist comparison.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONFUSABLE_MAP: Record<string, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Reduce a string to its confusable skeleton: NFKD-fold, then map each char
|
|
14
|
+
* through the confusable table, lowercased. Two strings with the same skeleton
|
|
15
|
+
* are visually confusable. Used only against the protected watchlist.
|
|
16
|
+
*/
|
|
17
|
+
export declare function skeleton(input: string): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* A curated subset of the UTS #39 confusables mapping (homoglyph → ASCII
|
|
4
|
+
* skeleton). This is intentionally small and high-precision: it covers the
|
|
5
|
+
* Latin-lookalike Cyrillic / Greek / fullwidth / digit-lookalike characters
|
|
6
|
+
* that matter for package-name and command-keyword squatting. It is NOT a full
|
|
7
|
+
* confusables.txt — the mixed-script detector (using Unicode script properties)
|
|
8
|
+
* catches the general case; this map exists to compute a `skeleton()` for the
|
|
9
|
+
* typosquat watchlist comparison.
|
|
10
|
+
*/
|
|
11
|
+
export const CONFUSABLE_MAP = {
|
|
12
|
+
// Cyrillic → Latin
|
|
13
|
+
а: 'a',
|
|
14
|
+
е: 'e',
|
|
15
|
+
о: 'o',
|
|
16
|
+
р: 'p',
|
|
17
|
+
с: 'c',
|
|
18
|
+
х: 'x',
|
|
19
|
+
у: 'y',
|
|
20
|
+
к: 'k',
|
|
21
|
+
м: 'm',
|
|
22
|
+
т: 't',
|
|
23
|
+
н: 'h',
|
|
24
|
+
в: 'b',
|
|
25
|
+
і: 'i',
|
|
26
|
+
ѕ: 's',
|
|
27
|
+
ј: 'j',
|
|
28
|
+
// Greek → Latin
|
|
29
|
+
ο: 'o',
|
|
30
|
+
ρ: 'p',
|
|
31
|
+
α: 'a',
|
|
32
|
+
ν: 'v',
|
|
33
|
+
τ: 't',
|
|
34
|
+
ι: 'i',
|
|
35
|
+
κ: 'k',
|
|
36
|
+
// Fullwidth → ASCII
|
|
37
|
+
a: 'a',
|
|
38
|
+
e: 'e',
|
|
39
|
+
i: 'i',
|
|
40
|
+
o: 'o',
|
|
41
|
+
r: 'r',
|
|
42
|
+
m: 'm',
|
|
43
|
+
c: 'c',
|
|
44
|
+
p: 'p',
|
|
45
|
+
// Digit / letter lookalikes
|
|
46
|
+
'0': 'o',
|
|
47
|
+
'1': 'l',
|
|
48
|
+
'3': 'e',
|
|
49
|
+
'5': 's',
|
|
50
|
+
'$': 's',
|
|
51
|
+
'@': 'a',
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Reduce a string to its confusable skeleton: NFKD-fold, then map each char
|
|
55
|
+
* through the confusable table, lowercased. Two strings with the same skeleton
|
|
56
|
+
* are visually confusable. Used only against the protected watchlist.
|
|
57
|
+
*/
|
|
58
|
+
export function skeleton(input) {
|
|
59
|
+
const normalized = input.normalize('NFKD').toLowerCase();
|
|
60
|
+
let out = '';
|
|
61
|
+
for (const ch of normalized) {
|
|
62
|
+
out += CONFUSABLE_MAP[ch] ?? ch;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* Prompt-injection / tool-poisoning pattern lexicon.
|
|
4
|
+
*
|
|
5
|
+
* These regexes model the *linguistic shape* of instructions aimed at the model
|
|
6
|
+
* rather than the human — the signature of tool poisoning and "line jumping".
|
|
7
|
+
* Keyword matching alone is deliberately weak (euphemism-based phrasing evades
|
|
8
|
+
* it); it is only one signal. The heavy lifting for real exfiltration is
|
|
9
|
+
* done by the capability / toxic-flow graph. These patterns catch the blatant
|
|
10
|
+
* cases and raise the *confidence* axis, never the whole grade on their own.
|
|
11
|
+
*/
|
|
12
|
+
import type { Confidence, Severity } from '../types.js';
|
|
13
|
+
/** Which text channel a pattern is meaningful in. */
|
|
14
|
+
export type InjectionChannel = 'tool-description' | 'param-description' | 'server-instructions';
|
|
15
|
+
export type InjectionKind = 'authority' | 'secrecy' | 'override' | 'sequencing' | 'sensitive-target' | 'exfil-param' | 'shadowing' | 'encoded-payload' | 'command-in-prose';
|
|
16
|
+
export interface InjectionPattern {
|
|
17
|
+
id: string;
|
|
18
|
+
kind: InjectionKind;
|
|
19
|
+
/** Source regex (compiled case-insensitive, global by the detector). */
|
|
20
|
+
pattern: RegExp;
|
|
21
|
+
baseSeverity: Severity;
|
|
22
|
+
baseConfidence: Confidence;
|
|
23
|
+
title: string;
|
|
24
|
+
channels: InjectionChannel[];
|
|
25
|
+
}
|
|
26
|
+
export declare const INJECTION_PATTERNS: InjectionPattern[];
|
|
27
|
+
/**
|
|
28
|
+
* Parameter *names* that front for hidden exfiltration channels. Kept
|
|
29
|
+
* high-precision: generic names like `context`/`metadata`/`feedback`/`notes`
|
|
30
|
+
* are excluded because they are overwhelmingly used legitimately.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SUSPICIOUS_PARAM_NAMES: string[];
|
|
33
|
+
/** ALL-CAPS run detector: >= this many consecutive shouted words is suspicious. */
|
|
34
|
+
export declare const ALLCAPS_RUN_WORDS = 3;
|
|
35
|
+
/**
|
|
36
|
+
* Hosts that are almost never a legitimate destination inside tool metadata —
|
|
37
|
+
* one-shot request/paste/webhook/exfil sinks and URL shorteners. A hardcoded
|
|
38
|
+
* link to one of these in a tool description is a data-exfiltration channel
|
|
39
|
+
* ("send the result to …"), the classic malicious-URL tool-poisoning move.
|
|
40
|
+
*/
|
|
41
|
+
export declare const SUSPICIOUS_URL_HOST: RegExp;
|
|
42
|
+
/** Webhook/bot endpoints whose PATH marks them as an exfil sink. */
|
|
43
|
+
export declare const SUSPICIOUS_URL_PATH: RegExp;
|
|
44
|
+
/** A bare IPv4/IPv6 literal as a URL host — a link to a raw address, not a name. */
|
|
45
|
+
export declare const URL_IP_LITERAL: RegExp;
|
|
46
|
+
/**
|
|
47
|
+
* High-precision secret-value patterns. A match is an actual embedded
|
|
48
|
+
* credential (not merely a reference to one), so it is `confirmed`. Evidence is
|
|
49
|
+
* always redacted to a short prefix — never echo the secret.
|
|
50
|
+
*/
|
|
51
|
+
export declare const SECRET_PATTERNS: {
|
|
52
|
+
id: string;
|
|
53
|
+
label: string;
|
|
54
|
+
pattern: RegExp;
|
|
55
|
+
}[];
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* Prompt-injection / tool-poisoning pattern lexicon.
|
|
4
|
+
*
|
|
5
|
+
* These regexes model the *linguistic shape* of instructions aimed at the model
|
|
6
|
+
* rather than the human — the signature of tool poisoning and "line jumping".
|
|
7
|
+
* Keyword matching alone is deliberately weak (euphemism-based phrasing evades
|
|
8
|
+
* it); it is only one signal. The heavy lifting for real exfiltration is
|
|
9
|
+
* done by the capability / toxic-flow graph. These patterns catch the blatant
|
|
10
|
+
* cases and raise the *confidence* axis, never the whole grade on their own.
|
|
11
|
+
*/
|
|
12
|
+
const ALL_CHANNELS = [
|
|
13
|
+
'tool-description',
|
|
14
|
+
'param-description',
|
|
15
|
+
'server-instructions',
|
|
16
|
+
];
|
|
17
|
+
export const INJECTION_PATTERNS = [
|
|
18
|
+
// --- Authority / secrecy / instruction-override ---
|
|
19
|
+
{
|
|
20
|
+
id: 'MTC-INJ-AUTH-1',
|
|
21
|
+
kind: 'authority',
|
|
22
|
+
pattern: /<?\bIMPORTANT\b>?|VERY IMPORTANT|\bATTENTION\b|\bMANDATORY\b/,
|
|
23
|
+
baseSeverity: 'low',
|
|
24
|
+
baseConfidence: 'heuristic',
|
|
25
|
+
title: 'Authority framing in tool metadata',
|
|
26
|
+
channels: ALL_CHANNELS,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'MTC-INJ-AUTH-2',
|
|
30
|
+
kind: 'override',
|
|
31
|
+
pattern: /(ignore|disregard|forget) (all |your |the |any )?(previous|prior|above|earlier)( instructions| directions| context)?|override (all|previous|any|the) (instructions|directives)/,
|
|
32
|
+
baseSeverity: 'high',
|
|
33
|
+
baseConfidence: 'strong',
|
|
34
|
+
title: 'Instruction-override directive',
|
|
35
|
+
channels: ALL_CHANNELS,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'MTC-INJ-SECRECY-1',
|
|
39
|
+
kind: 'secrecy',
|
|
40
|
+
pattern: /do not (tell|mention|inform|notify|reveal|show|display|print|log|output)( this| it)?( to)? the user|without (telling|informing|notifying) the user|do not mention this|never (tell|mention|reveal|present|inform|display)( the user| this)|(hide|conceal|keep)( this| it)? (hidden |secret )?from the user|concealed from the user/,
|
|
41
|
+
baseSeverity: 'high',
|
|
42
|
+
baseConfidence: 'strong',
|
|
43
|
+
title: 'Secrecy directive (conceal action from the user)',
|
|
44
|
+
channels: ALL_CHANNELS,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'MTC-INJ-SECRECY-2',
|
|
48
|
+
kind: 'authority',
|
|
49
|
+
pattern: /for compliance|internal requirement|as required by policy|per policy|for security reasons you must/,
|
|
50
|
+
baseSeverity: 'medium',
|
|
51
|
+
baseConfidence: 'heuristic',
|
|
52
|
+
title: 'Fabricated policy / compliance framing',
|
|
53
|
+
channels: ALL_CHANNELS,
|
|
54
|
+
},
|
|
55
|
+
// --- Sequencing / "do this first" line-jumping ---
|
|
56
|
+
{
|
|
57
|
+
id: 'MTC-INJ-SEQ-1',
|
|
58
|
+
kind: 'sequencing',
|
|
59
|
+
// "before using ANY/OTHER tool" is line-jumping; "before using THIS tool"
|
|
60
|
+
// is legitimate self-documentation of a prerequisite and must not match.
|
|
61
|
+
pattern: /before (executing|calling|using|running) (any|another|the other|other) (tool|command|function)|as the (first|very first) step,? (call|invoke|use)|always (consult|call|invoke) this tool( first| immediately)?|you must (also |always |first )?(call|invoke) (this|the )/,
|
|
62
|
+
// Medium: assertive "call X first" language is common in legitimate tool
|
|
63
|
+
// docs; the dangerous form (secrecy/target + sequencing) is escalated to
|
|
64
|
+
// critical by the compound-poisoning rule.
|
|
65
|
+
baseSeverity: 'medium',
|
|
66
|
+
baseConfidence: 'strong',
|
|
67
|
+
title: 'Forced-sequencing directive (line jumping)',
|
|
68
|
+
channels: ALL_CHANNELS,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'MTC-INJ-SEQ-2',
|
|
72
|
+
kind: 'sequencing',
|
|
73
|
+
pattern: /when (this tool |you )?(is invoked|call|use|are asked)|every time (you|the assistant)/,
|
|
74
|
+
baseSeverity: 'low',
|
|
75
|
+
baseConfidence: 'heuristic',
|
|
76
|
+
title: 'Conditional behavior directive',
|
|
77
|
+
channels: ALL_CHANNELS,
|
|
78
|
+
},
|
|
79
|
+
// --- Sensitive targets referenced in metadata ---
|
|
80
|
+
{
|
|
81
|
+
id: 'MTC-INJ-TARGET-1',
|
|
82
|
+
kind: 'sensitive-target',
|
|
83
|
+
// Only genuine credential-exfil paths (rare in legit tool docs). Bare
|
|
84
|
+
// mentions of API_KEY/.env/SECRET_KEY are ubiquitous in legit auth/env tools
|
|
85
|
+
// and were removed to avoid false trust hits.
|
|
86
|
+
pattern: /~\/\.ssh|\bid_rsa\b|~\/\.aws\/credentials|\.aws\/credentials|~\/\.cursor\/mcp\.json|~\/\.config\/[^\s]*\/credentials/,
|
|
87
|
+
baseSeverity: 'high',
|
|
88
|
+
baseConfidence: 'strong',
|
|
89
|
+
title: 'Reference to a credential-exfiltration path in metadata',
|
|
90
|
+
channels: ALL_CHANNELS,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'MTC-INJ-TARGET-2',
|
|
94
|
+
kind: 'sensitive-target',
|
|
95
|
+
// Soft signal: legitimate chat/agent/registry tools reference these; medium
|
|
96
|
+
// + heuristic so it can't force a critical grade on its own.
|
|
97
|
+
pattern: /system prompt|conversation history|chat history|previous messages|environment details|list of (all )?(available )?tools/,
|
|
98
|
+
baseSeverity: 'medium',
|
|
99
|
+
baseConfidence: 'heuristic',
|
|
100
|
+
title: 'Solicitation of model context / history',
|
|
101
|
+
channels: ALL_CHANNELS,
|
|
102
|
+
},
|
|
103
|
+
// --- Exfil-shaped parameters (evaluated against param descriptions) ---
|
|
104
|
+
{
|
|
105
|
+
id: 'MTC-INJ-EXFIL-1',
|
|
106
|
+
kind: 'exfil-param',
|
|
107
|
+
// Must reference a genuinely sensitive object — not just "include all"
|
|
108
|
+
// (which is ubiquitous in legit params like "include all commits").
|
|
109
|
+
pattern: /(append|include|attach|paste|send|add) (the )?(full |entire |complete )?(value|contents?) of (the )?(environment|system prompt|secret|credential|\.env|ssh key|api key|private key|access token)/,
|
|
110
|
+
baseSeverity: 'high',
|
|
111
|
+
baseConfidence: 'strong',
|
|
112
|
+
title: 'Parameter description solicits secrets/environment to be sent along',
|
|
113
|
+
channels: ['param-description'],
|
|
114
|
+
},
|
|
115
|
+
// --- Cross-tool shadowing (3 tiers by real threat, not just linguistic shape) ---
|
|
116
|
+
{
|
|
117
|
+
id: 'MTC-INJ-SHADOW-1',
|
|
118
|
+
kind: 'shadowing',
|
|
119
|
+
// HIGH: genuine redirection of a recipient/destination/credential — the
|
|
120
|
+
// exfiltration/hijack primitive. This is the only form that reroutes data
|
|
121
|
+
// through an attacker-chosen path.
|
|
122
|
+
pattern: /redirect (all|the|any) (email|message|credential|token)|route (all|the|any) (email|message) (to|through)/,
|
|
123
|
+
baseSeverity: 'high',
|
|
124
|
+
baseConfidence: 'strong',
|
|
125
|
+
title: 'Cross-tool redirection of a recipient/credential',
|
|
126
|
+
channels: ALL_CHANNELS,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'MTC-INJ-SHADOW-3',
|
|
130
|
+
kind: 'shadowing',
|
|
131
|
+
// HIGH: blanket SUPPRESSION of *all* sibling tools — a genuine tool-selection
|
|
132
|
+
// hijack that crowds out legitimate (incl. security) tools.
|
|
133
|
+
pattern: /do not (use|call) (the |any )?other tool|use this tool instead of (all )?other|ignore (the )?other tools/,
|
|
134
|
+
baseSeverity: 'high',
|
|
135
|
+
baseConfidence: 'strong',
|
|
136
|
+
title: 'Tool-selection hijack (suppress all other tools)',
|
|
137
|
+
channels: ALL_CHANNELS,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'MTC-INJ-SHADOW-4',
|
|
141
|
+
kind: 'shadowing',
|
|
142
|
+
// LOW: self-promotion / comparative preference ("this is the only correct
|
|
143
|
+
// tool", "use this instead of the analysis tool"). Ubiquitous in legitimate
|
|
144
|
+
// assertive docs — the disparaged claim is often factually true and points at
|
|
145
|
+
// the server's OWN tool with no sink. A confidence-axis nudge only; it still
|
|
146
|
+
// escalates to critical via the compound-poisoning rule when it co-occurs
|
|
147
|
+
// with a secrecy/override signal (e.g. "…and do not tell the user").
|
|
148
|
+
pattern: /prefer (this|the following) tool|(this is the )?only (correct|valid|safe|right) tool|always prefer this tool|use this (tool )?instead of the \w+ (tool|function|server)|replaces? the \w+ (tool|function|server)|instead of (calling|using|invoking) the \w+ (tool|function|server)/,
|
|
149
|
+
baseSeverity: 'low',
|
|
150
|
+
baseConfidence: 'heuristic',
|
|
151
|
+
title: 'Assertive tool self-preference (comparative)',
|
|
152
|
+
channels: ALL_CHANNELS,
|
|
153
|
+
},
|
|
154
|
+
// --- Encoded payloads ---
|
|
155
|
+
{
|
|
156
|
+
id: 'MTC-INJ-ENC-1',
|
|
157
|
+
kind: 'encoded-payload',
|
|
158
|
+
// Require a decode PAIRED with execution — a plain base64 utility only
|
|
159
|
+
// decodes and must not be flagged.
|
|
160
|
+
pattern: /decode (this|the following|the base64)[^.]{0,40}(execute|eval|run it|then run)|(base64|from base64)[^.]{0,25}(execute|eval|then run)|execute the (result|decoded|following)|\\x[0-9a-f]{2}(\\x[0-9a-f]{2}){2,}/,
|
|
161
|
+
baseSeverity: 'high',
|
|
162
|
+
baseConfidence: 'strong',
|
|
163
|
+
title: 'Encoded-payload decode/execute instruction',
|
|
164
|
+
channels: ALL_CHANNELS,
|
|
165
|
+
},
|
|
166
|
+
// --- Shell commands embedded in prose ---
|
|
167
|
+
{
|
|
168
|
+
id: 'MTC-INJ-CMD-1',
|
|
169
|
+
kind: 'command-in-prose',
|
|
170
|
+
pattern: /\b(rm -rf|chmod -R|curl [^\s]+ ?\| ?(sh|bash)|wget [^\s]+ ?\| ?(sh|bash)|cat ~\/|\/bin\/(sh|bash)\b|nc -e|bash -c)\b/,
|
|
171
|
+
baseSeverity: 'high',
|
|
172
|
+
baseConfidence: 'strong',
|
|
173
|
+
title: 'Shell command embedded in tool metadata',
|
|
174
|
+
channels: ALL_CHANNELS,
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
/**
|
|
178
|
+
* Parameter *names* that front for hidden exfiltration channels. Kept
|
|
179
|
+
* high-precision: generic names like `context`/`metadata`/`feedback`/`notes`
|
|
180
|
+
* are excluded because they are overwhelmingly used legitimately.
|
|
181
|
+
*/
|
|
182
|
+
export const SUSPICIOUS_PARAM_NAMES = [
|
|
183
|
+
'side_note',
|
|
184
|
+
'sidenote',
|
|
185
|
+
'sidechannel',
|
|
186
|
+
'summary_of_environment_details',
|
|
187
|
+
'environment_details',
|
|
188
|
+
];
|
|
189
|
+
/** ALL-CAPS run detector: >= this many consecutive shouted words is suspicious. */
|
|
190
|
+
export const ALLCAPS_RUN_WORDS = 3;
|
|
191
|
+
/**
|
|
192
|
+
* Hosts that are almost never a legitimate destination inside tool metadata —
|
|
193
|
+
* one-shot request/paste/webhook/exfil sinks and URL shorteners. A hardcoded
|
|
194
|
+
* link to one of these in a tool description is a data-exfiltration channel
|
|
195
|
+
* ("send the result to …"), the classic malicious-URL tool-poisoning move.
|
|
196
|
+
*/
|
|
197
|
+
export const SUSPICIOUS_URL_HOST = /(?:^|\.)(?:webhook\.site|requestbin\.(?:net|com)|requestbin\.io|hookb\.in|pipedream\.net|beeceptor\.com|ngrok(?:-free)?\.(?:io|app|dev)|trycloudflare\.com|pastebin\.com|hastebin\.com|dpaste\.(?:com|org)|ix\.io|0x0\.st|transfer\.sh|file\.io|bit\.ly|tinyurl\.com|is\.gd|t\.co|interactsh\.com|oast\.(?:fun|site|live|pro|me)|canarytokens\.com|burpcollaborator\.net)$/i;
|
|
198
|
+
/** Webhook/bot endpoints whose PATH marks them as an exfil sink. */
|
|
199
|
+
export const SUSPICIOUS_URL_PATH = /discord(?:app)?\.com\/api\/webhooks\/|api\.telegram\.org\/bot|hooks\.slack\.com\/services\//i;
|
|
200
|
+
/** A bare IPv4/IPv6 literal as a URL host — a link to a raw address, not a name. */
|
|
201
|
+
export const URL_IP_LITERAL = /^\d{1,3}(?:\.\d{1,3}){3}$|^\[[0-9a-f:]+\]$/i;
|
|
202
|
+
/**
|
|
203
|
+
* High-precision secret-value patterns. A match is an actual embedded
|
|
204
|
+
* credential (not merely a reference to one), so it is `confirmed`. Evidence is
|
|
205
|
+
* always redacted to a short prefix — never echo the secret.
|
|
206
|
+
*/
|
|
207
|
+
export const SECRET_PATTERNS = [
|
|
208
|
+
{ id: 'aws-access-key', label: 'AWS access key id', pattern: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
209
|
+
{ id: 'github-token', label: 'GitHub token', pattern: /\bgh[posru]_[0-9A-Za-z]{36}\b/ },
|
|
210
|
+
{ id: 'slack-token', label: 'Slack token', pattern: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/ },
|
|
211
|
+
{ id: 'google-api-key', label: 'Google API key', pattern: /\bAIza[0-9A-Za-z\-_]{35}\b/ },
|
|
212
|
+
{ id: 'stripe-secret', label: 'Stripe secret key', pattern: /\bsk_live_[0-9A-Za-z]{24,}\b/ },
|
|
213
|
+
{ id: 'openai-key', label: 'OpenAI key', pattern: /\bsk-[A-Za-z0-9]{20,}T3BlbkFJ[A-Za-z0-9]{20,}\b/ },
|
|
214
|
+
{ id: 'private-key', label: 'PEM private key', pattern: /-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/ },
|
|
215
|
+
{ id: 'jwt', label: 'JSON Web Token', pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/ },
|
|
216
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* Known-CVE / advisory version matcher for MCP client / proxy / server
|
|
4
|
+
* packages. A cheap, high-signal check that pure content scanners lack:
|
|
5
|
+
* if a config pins a package to a version in a known-vulnerable range, say so.
|
|
6
|
+
*
|
|
7
|
+
* Ranges are intentionally simple (`< x` or `<= x`). Keep this list current;
|
|
8
|
+
* it is data, not logic.
|
|
9
|
+
*/
|
|
10
|
+
import type { Severity } from '../types.js';
|
|
11
|
+
export interface KnownVuln {
|
|
12
|
+
id: string;
|
|
13
|
+
package: string;
|
|
14
|
+
registry: 'npm' | 'pypi';
|
|
15
|
+
/** Vulnerable when installed version < this (exclusive). */
|
|
16
|
+
ltExclusive?: string;
|
|
17
|
+
/** Vulnerable when installed version <= this (inclusive). */
|
|
18
|
+
leInclusive?: string;
|
|
19
|
+
severity: Severity;
|
|
20
|
+
cvss?: number;
|
|
21
|
+
title: string;
|
|
22
|
+
reference: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const KNOWN_VULNS: KnownVuln[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* Known-CVE / advisory version matcher for MCP client / proxy / server
|
|
4
|
+
* packages. A cheap, high-signal check that pure content scanners lack:
|
|
5
|
+
* if a config pins a package to a version in a known-vulnerable range, say so.
|
|
6
|
+
*
|
|
7
|
+
* Ranges are intentionally simple (`< x` or `<= x`). Keep this list current;
|
|
8
|
+
* it is data, not logic.
|
|
9
|
+
*/
|
|
10
|
+
export const KNOWN_VULNS = [
|
|
11
|
+
{
|
|
12
|
+
id: 'CVE-2025-6514',
|
|
13
|
+
package: 'mcp-remote',
|
|
14
|
+
registry: 'npm',
|
|
15
|
+
ltExclusive: '0.1.16',
|
|
16
|
+
severity: 'critical',
|
|
17
|
+
cvss: 9.6,
|
|
18
|
+
title: 'mcp-remote OS command injection via crafted authorization endpoint',
|
|
19
|
+
reference: 'https://nvd.nist.gov/vuln/detail/CVE-2025-6514',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'CVE-2025-49596',
|
|
23
|
+
package: '@modelcontextprotocol/inspector',
|
|
24
|
+
registry: 'npm',
|
|
25
|
+
ltExclusive: '0.14.1',
|
|
26
|
+
severity: 'critical',
|
|
27
|
+
cvss: 9.4,
|
|
28
|
+
title: 'MCP Inspector DNS-rebinding leads to RCE on the developer host',
|
|
29
|
+
reference: 'https://nvd.nist.gov/vuln/detail/CVE-2025-49596',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'GHSA-git-mcp-cyanheads',
|
|
33
|
+
package: '@cyanheads/git-mcp-server',
|
|
34
|
+
registry: 'npm',
|
|
35
|
+
leInclusive: '2.1.4',
|
|
36
|
+
severity: 'high',
|
|
37
|
+
title: 'git-mcp-server argument injection in git commands',
|
|
38
|
+
reference: 'https://github.com/advisories',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'GHSA-mcp-server-git',
|
|
42
|
+
package: 'mcp-server-git',
|
|
43
|
+
registry: 'pypi',
|
|
44
|
+
ltExclusive: '2025.12.18',
|
|
45
|
+
severity: 'high',
|
|
46
|
+
title: 'mcp-server-git path traversal / argument injection',
|
|
47
|
+
reference: 'https://github.com/advisories',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*! MCP Trust Checker · https://mcptrustchecker.com · support@mcptrustchecker.com · © 2026 Illia Haidar · MIT */
|
|
2
|
+
/**
|
|
3
|
+
* The curated "protected" MCP package list. Typosquatting is checked only
|
|
4
|
+
* against this anchor set (never all-pairs), which is both fast and precise.
|
|
5
|
+
* Weekly-download figures feed the download-anomaly gate: a near-miss on a
|
|
6
|
+
* high-traffic name with near-zero downloads of its own is malicious, not a
|
|
7
|
+
* coincidence.
|
|
8
|
+
*/
|
|
9
|
+
export interface ProtectedPackage {
|
|
10
|
+
name: string;
|
|
11
|
+
registry: 'npm' | 'pypi';
|
|
12
|
+
/** Approximate weekly downloads (order of magnitude, for the anomaly gate). */
|
|
13
|
+
weeklyDownloads?: number;
|
|
14
|
+
official?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const PROTECTED_PACKAGES: ProtectedPackage[];
|
|
17
|
+
/**
|
|
18
|
+
* Fake official-scope / homoglyph scope patterns. Any package whose scope
|
|
19
|
+
* matches one of these is impersonating the official namespace.
|
|
20
|
+
*/
|
|
21
|
+
export declare const FAKE_SCOPE_PATTERNS: RegExp[];
|
|
22
|
+
/** Bare (unscoped) names that shadow official *scoped* packages. */
|
|
23
|
+
export declare const UNSCOPED_SHADOWS: Set<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Pre-computed known squats mapping a malicious/confusable name to the real
|
|
26
|
+
* package it imitates (collapses several matchers to an instant hit).
|
|
27
|
+
*/
|
|
28
|
+
export declare const KNOWN_SQUATS: Record<string, string>;
|
|
29
|
+
/** Combosquat suffixes stripped before comparison (e.g. `foo-js` ≈ `foo`). */
|
|
30
|
+
export declare const COMBOSQUAT_SUFFIXES: string[];
|