shellward 0.5.16 → 0.6.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/README.md +95 -30
- package/dist/auto-check.d.ts +1 -0
- package/dist/auto-check.js +12 -1
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/index.js +7 -0
- package/dist/commands/scan-mcp.d.ts +2 -0
- package/dist/commands/scan-mcp.js +105 -0
- package/dist/core/engine.d.ts +35 -0
- package/dist/core/engine.js +225 -30
- package/dist/index.d.ts +4 -2
- package/dist/index.js +18 -3
- package/dist/mcp-baseline.d.ts +27 -0
- package/dist/mcp-baseline.js +73 -0
- package/dist/mcp-client.d.ts +29 -0
- package/dist/mcp-client.js +264 -0
- package/dist/mcp-server.js +64 -9
- package/dist/rules/dangerous-commands.js +6 -2
- package/dist/rules/injection-en.js +27 -2
- package/dist/rules/injection-zh.js +27 -4
- package/dist/rules/sensitive-patterns.d.ts +13 -1
- package/dist/rules/sensitive-patterns.js +32 -5
- package/dist/rules/tool-poisoning.d.ts +8 -0
- package/dist/rules/tool-poisoning.js +96 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +3 -1
- package/package.json +4 -2
- package/server.json +2 -2
- package/src/auto-check.ts +11 -1
- package/src/commands/index.ts +9 -1
- package/src/commands/scan-mcp.ts +118 -0
- package/src/core/engine.ts +250 -31
- package/src/index.ts +25 -5
- package/src/mcp-baseline.ts +97 -0
- package/src/mcp-client.ts +268 -0
- package/src/mcp-server.ts +71 -9
- package/src/rules/dangerous-commands.ts +6 -2
- package/src/rules/injection-en.ts +27 -2
- package/src/rules/injection-zh.ts +27 -4
- package/src/rules/sensitive-patterns.ts +37 -5
- package/src/rules/tool-poisoning.ts +108 -0
- package/src/types.ts +38 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/rules/tool-poisoning.ts — MCP tool-poisoning detection rules
|
|
2
|
+
//
|
|
3
|
+
// Tool poisoning = malicious instructions hidden in an MCP tool's *metadata*
|
|
4
|
+
// (description / parameter descriptions) that the LLM reads but the human never
|
|
5
|
+
// sees in the UI. These are distinct from generic prompt-injection in user text:
|
|
6
|
+
// they target the agent at tool-discovery time. Patterns below are tuned for the
|
|
7
|
+
// common public PoCs (Invariant Labs, MCP-Shield, Snyk agent-scan).
|
|
8
|
+
|
|
9
|
+
export interface ToolPoisonRule {
|
|
10
|
+
id: string
|
|
11
|
+
name: string
|
|
12
|
+
pattern: RegExp
|
|
13
|
+
riskScore: number
|
|
14
|
+
category: 'hidden_instruction' | 'data_access' | 'exfiltration' | 'concealment' | 'shadowing'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const TOOL_POISONING_RULES: ToolPoisonRule[] = [
|
|
18
|
+
// ===== Hidden instruction markup =====
|
|
19
|
+
{
|
|
20
|
+
id: 'tp_important_tag',
|
|
21
|
+
name: 'Hidden <IMPORTANT>/<system> directive in description',
|
|
22
|
+
pattern: /<\s*(?:important|system|secret|instructions?|admin)\s*>/i,
|
|
23
|
+
riskScore: 45,
|
|
24
|
+
category: 'hidden_instruction',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'tp_before_using',
|
|
28
|
+
name: 'Pre-tool instruction injection',
|
|
29
|
+
pattern: /before\s+(?:using|calling|invoking|running)\s+(?:any\s+other|this|the|another)\s+tool/i,
|
|
30
|
+
riskScore: 40,
|
|
31
|
+
category: 'hidden_instruction',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'tp_zh_before_using',
|
|
35
|
+
name: '工具描述内前置指令注入',
|
|
36
|
+
pattern: /(?:在使用|调用|执行)(?:任何)?(?:其他|这个|该)?工具(?:之前|前)/,
|
|
37
|
+
riskScore: 40,
|
|
38
|
+
category: 'hidden_instruction',
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// ===== Concealment ("don't tell the user") =====
|
|
42
|
+
{
|
|
43
|
+
id: 'tp_do_not_tell',
|
|
44
|
+
name: 'Instruction to hide activity from user',
|
|
45
|
+
pattern: /(?:do\s+not|don'?t|never)\s+(?:tell|inform|mention|notify|reveal|show)\s+(?:to\s+)?(?:the\s+)?(?:user|human|operator)/i,
|
|
46
|
+
riskScore: 45,
|
|
47
|
+
category: 'concealment',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'tp_zh_do_not_tell',
|
|
51
|
+
name: '指示对用户隐藏行为',
|
|
52
|
+
pattern: /(?:不要|不得|切勿|别)(?:告诉|告知|提示|通知|让)?(?:用户|使用者)(?:知道|看到|发现)?/,
|
|
53
|
+
riskScore: 45,
|
|
54
|
+
category: 'concealment',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'tp_without_user',
|
|
58
|
+
name: 'Act without user knowledge/consent',
|
|
59
|
+
pattern: /without\s+(?:the\s+)?(?:user'?s?\s+)?(?:knowledge|consent|awareness|noticing|telling)/i,
|
|
60
|
+
riskScore: 40,
|
|
61
|
+
category: 'concealment',
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// ===== Sensitive data access from a tool description =====
|
|
65
|
+
{
|
|
66
|
+
id: 'tp_read_secrets',
|
|
67
|
+
// Bare mention of a sensitive path is only weakly suspicious — legitimate
|
|
68
|
+
// tools (dotenv loaders, ssh managers) and security tools name these too.
|
|
69
|
+
// Scored below threshold so it must corroborate another signal to block.
|
|
70
|
+
name: 'Description references sensitive files',
|
|
71
|
+
pattern: /(?:~\/\.ssh|id_rsa|\.aws\/credentials|\.env\b|\.cursor\/mcp\.json|\.npmrc|\/etc\/passwd|\.config\/.*(?:token|secret|credential))/i,
|
|
72
|
+
riskScore: 25,
|
|
73
|
+
category: 'data_access',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'tp_pass_file_contents',
|
|
77
|
+
name: 'Description asks to pass file/secret contents as a parameter',
|
|
78
|
+
pattern: /(?:pass|include|read|send|provide|attach)\s+(?:the\s+)?(?:full\s+)?(?:contents?|content|value)\s+of\s+(?:the\s+)?(?:file|\S*(?:key|token|secret|password|credential))/i,
|
|
79
|
+
riskScore: 35,
|
|
80
|
+
category: 'data_access',
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// ===== Exfiltration hints =====
|
|
84
|
+
{
|
|
85
|
+
id: 'tp_exfil_url',
|
|
86
|
+
name: 'Description instructs sending data to a URL',
|
|
87
|
+
pattern: /(?:send|transmit|upload|post|exfiltrate|forward)\s+(?:it|this|the\s+\w+|data|results?)?\s*(?:to|via)\s+(?:https?:\/\/|the\s+(?:webhook|endpoint|server|url))/i,
|
|
88
|
+
riskScore: 40,
|
|
89
|
+
category: 'exfiltration',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'tp_exfiltrate_verb',
|
|
93
|
+
// "exfiltrate" in a tool description is almost never benign.
|
|
94
|
+
name: 'Exfiltration verb in description',
|
|
95
|
+
pattern: /\bexfiltrat(?:e|ion|ing)\b/i,
|
|
96
|
+
riskScore: 35,
|
|
97
|
+
category: 'exfiltration',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'tp_sidechannel',
|
|
101
|
+
// A bare side-channel hostname is weak on its own (could be documentation);
|
|
102
|
+
// scored below threshold so it must accompany another signal to block.
|
|
103
|
+
name: 'Known exfiltration side-channel keyword',
|
|
104
|
+
pattern: /\b(?:webhook\.site|requestbin|pastebin|ngrok\.io|burpcollaborator|interact\.sh|oast\b)/i,
|
|
105
|
+
riskScore: 25,
|
|
106
|
+
category: 'exfiltration',
|
|
107
|
+
},
|
|
108
|
+
]
|
package/src/types.ts
CHANGED
|
@@ -16,6 +16,41 @@ export interface ShellWardConfig {
|
|
|
16
16
|
sessionGuard: boolean
|
|
17
17
|
}
|
|
18
18
|
injectionThreshold: number
|
|
19
|
+
/** User-supplied rules merged on top of the built-ins (additive; allowedTools wins). */
|
|
20
|
+
customRules?: CustomRules
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A user-defined PII/secret pattern (regex source as a string for JSON-friendliness). */
|
|
24
|
+
export interface CustomSensitivePattern {
|
|
25
|
+
id: string
|
|
26
|
+
name: string
|
|
27
|
+
pattern: string
|
|
28
|
+
flags?: string
|
|
29
|
+
replacement?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A user-defined dangerous-command pattern. */
|
|
33
|
+
export interface CustomCommandRule {
|
|
34
|
+
id: string
|
|
35
|
+
pattern: string
|
|
36
|
+
flags?: string
|
|
37
|
+
description?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Extension points for any platform embedding ShellWard. All fields are optional
|
|
42
|
+
* and ADDITIVE on top of the built-in rules — except `allowedTools`, which always
|
|
43
|
+
* wins (a tool listed there is never blocked and is treated as low-risk).
|
|
44
|
+
*/
|
|
45
|
+
export interface CustomRules {
|
|
46
|
+
blockedTools?: string[]
|
|
47
|
+
allowedTools?: string[]
|
|
48
|
+
sensitiveTools?: string[]
|
|
49
|
+
outboundTools?: string[]
|
|
50
|
+
honeypotPaths?: string[]
|
|
51
|
+
sensitivePatterns?: CustomSensitivePattern[]
|
|
52
|
+
dangerousCommands?: CustomCommandRule[]
|
|
53
|
+
injectionRules?: InjectionRule[]
|
|
19
54
|
}
|
|
20
55
|
|
|
21
56
|
export type ResolvedLocale = 'zh' | 'en'
|
|
@@ -80,7 +115,9 @@ export const DEFAULT_CONFIG: ShellWardConfig = {
|
|
|
80
115
|
dataFlowGuard: true,
|
|
81
116
|
sessionGuard: true,
|
|
82
117
|
},
|
|
83
|
-
|
|
118
|
+
// 40 catches single high-confidence signals (one strong rule = a block) while
|
|
119
|
+
// keeping benign "act as…"-style phrasing (≤35) safe. Calibrated against bench/.
|
|
120
|
+
injectionThreshold: 40,
|
|
84
121
|
}
|
|
85
122
|
|
|
86
123
|
/**
|