@elliotllliu/agent-shield 0.3.1
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 +297 -0
- package/README.zh-CN.md +130 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +265 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +91 -0
- package/dist/config.js.map +1 -0
- package/dist/discover.d.ts +9 -0
- package/dist/discover.js +143 -0
- package/dist/discover.js.map +1 -0
- package/dist/llm/anthropic.d.ts +10 -0
- package/dist/llm/anthropic.js +67 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/index.d.ts +10 -0
- package/dist/llm/index.js +41 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/ollama.d.ts +9 -0
- package/dist/llm/ollama.js +61 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +10 -0
- package/dist/llm/openai.js +66 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/prompt.d.ts +3 -0
- package/dist/llm/prompt.js +31 -0
- package/dist/llm/prompt.js.map +1 -0
- package/dist/llm/types.d.ts +23 -0
- package/dist/llm/types.js +3 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/llm-analyzer.d.ts +13 -0
- package/dist/llm-analyzer.js +169 -0
- package/dist/llm-analyzer.js.map +1 -0
- package/dist/reporter/badge.d.ts +7 -0
- package/dist/reporter/badge.js +50 -0
- package/dist/reporter/badge.js.map +1 -0
- package/dist/reporter/json.d.ts +3 -0
- package/dist/reporter/json.js +5 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/reporter/terminal.d.ts +2 -0
- package/dist/reporter/terminal.js +64 -0
- package/dist/reporter/terminal.js.map +1 -0
- package/dist/rules/backdoor.d.ts +2 -0
- package/dist/rules/backdoor.js +57 -0
- package/dist/rules/backdoor.js.map +1 -0
- package/dist/rules/credential-hardcode.d.ts +2 -0
- package/dist/rules/credential-hardcode.js +57 -0
- package/dist/rules/credential-hardcode.js.map +1 -0
- package/dist/rules/crypto-mining.d.ts +2 -0
- package/dist/rules/crypto-mining.js +41 -0
- package/dist/rules/crypto-mining.js.map +1 -0
- package/dist/rules/data-exfil.d.ts +2 -0
- package/dist/rules/data-exfil.js +61 -0
- package/dist/rules/data-exfil.js.map +1 -0
- package/dist/rules/env-leak.d.ts +2 -0
- package/dist/rules/env-leak.js +43 -0
- package/dist/rules/env-leak.js.map +1 -0
- package/dist/rules/excessive-perms.d.ts +2 -0
- package/dist/rules/excessive-perms.js +50 -0
- package/dist/rules/excessive-perms.js.map +1 -0
- package/dist/rules/hidden-files.d.ts +2 -0
- package/dist/rules/hidden-files.js +52 -0
- package/dist/rules/hidden-files.js.map +1 -0
- package/dist/rules/index.d.ts +5 -0
- package/dist/rules/index.js +53 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/mcp-manifest.d.ts +2 -0
- package/dist/rules/mcp-manifest.js +270 -0
- package/dist/rules/mcp-manifest.js.map +1 -0
- package/dist/rules/network-ssrf.d.ts +2 -0
- package/dist/rules/network-ssrf.js +51 -0
- package/dist/rules/network-ssrf.js.map +1 -0
- package/dist/rules/obfuscation.d.ts +2 -0
- package/dist/rules/obfuscation.js +51 -0
- package/dist/rules/obfuscation.js.map +1 -0
- package/dist/rules/phone-home.d.ts +2 -0
- package/dist/rules/phone-home.js +38 -0
- package/dist/rules/phone-home.js.map +1 -0
- package/dist/rules/privilege.d.ts +2 -0
- package/dist/rules/privilege.js +111 -0
- package/dist/rules/privilege.js.map +1 -0
- package/dist/rules/prompt-injection.d.ts +2 -0
- package/dist/rules/prompt-injection.js +323 -0
- package/dist/rules/prompt-injection.js.map +1 -0
- package/dist/rules/reverse-shell.d.ts +2 -0
- package/dist/rules/reverse-shell.js +53 -0
- package/dist/rules/reverse-shell.js.map +1 -0
- package/dist/rules/sensitive-read.d.ts +2 -0
- package/dist/rules/sensitive-read.js +53 -0
- package/dist/rules/sensitive-read.js.map +1 -0
- package/dist/rules/skill-risks.d.ts +2 -0
- package/dist/rules/skill-risks.js +148 -0
- package/dist/rules/skill-risks.js.map +1 -0
- package/dist/rules/supply-chain.d.ts +6 -0
- package/dist/rules/supply-chain.js +105 -0
- package/dist/rules/supply-chain.js.map +1 -0
- package/dist/rules/tool-shadowing.d.ts +2 -0
- package/dist/rules/tool-shadowing.js +129 -0
- package/dist/rules/tool-shadowing.js.map +1 -0
- package/dist/rules/toxic-flow.d.ts +2 -0
- package/dist/rules/toxic-flow.js +160 -0
- package/dist/rules/toxic-flow.js.map +1 -0
- package/dist/rules/typosquatting.d.ts +2 -0
- package/dist/rules/typosquatting.js +56 -0
- package/dist/rules/typosquatting.js.map +1 -0
- package/dist/scanner/files.d.ts +5 -0
- package/dist/scanner/files.js +105 -0
- package/dist/scanner/files.js.map +1 -0
- package/dist/scanner/index.d.ts +6 -0
- package/dist/scanner/index.js +198 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/score.d.ts +14 -0
- package/dist/score.js +35 -0
- package/dist/score.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/yaml-simple.d.ts +6 -0
- package/dist/yaml-simple.js +98 -0
- package/dist/yaml-simple.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: mcp-manifest
|
|
3
|
+
* Validates MCP (Model Context Protocol) server configurations.
|
|
4
|
+
*
|
|
5
|
+
* Checks:
|
|
6
|
+
* 1. Declared tools/resources vs actual code behavior
|
|
7
|
+
* 2. Overly broad tool descriptions that could mislead agents
|
|
8
|
+
* 3. Undeclared file system / network / exec capabilities
|
|
9
|
+
* 4. Suspicious tool names or descriptions
|
|
10
|
+
*/
|
|
11
|
+
// Patterns indicating MCP server tool registration
|
|
12
|
+
const TOOL_REGISTER_RE = /\.tool\s*\(|addTool\s*\(|registerTool\s*\(|server\.setRequestHandler.*ListTools|tools:\s*\[/;
|
|
13
|
+
// Patterns for MCP resource registration
|
|
14
|
+
const RESOURCE_REGISTER_RE = /\.resource\s*\(|addResource\s*\(|registerResource\s*\(|server\.setRequestHandler.*ListResources|resources:\s*\[/;
|
|
15
|
+
// Dangerous patterns in tool implementations
|
|
16
|
+
const DANGEROUS_TOOL_PATTERNS = [
|
|
17
|
+
{ pattern: /child_process|execSync|exec\(|spawn\(/, desc: "Tool executes shell commands", severity: "critical" },
|
|
18
|
+
{ pattern: /fs\.unlink|fs\.rmdir|fs\.rm\b|rimraf/, desc: "Tool deletes files", severity: "warning" },
|
|
19
|
+
{ pattern: /fs\.writeFile|fs\.appendFile|fs\.createWriteStream/, desc: "Tool writes to file system", severity: "warning" },
|
|
20
|
+
{ pattern: /fetch\s*\(|axios|http\.request|https\.request/, desc: "Tool makes outbound HTTP requests", severity: "warning" },
|
|
21
|
+
{ pattern: /eval\s*\(|new\s+Function\s*\(/, desc: "Tool uses dynamic code execution", severity: "critical" },
|
|
22
|
+
{ pattern: /\.ssh|\.aws|\.env\b|credentials|secret/i, desc: "Tool accesses sensitive paths/credentials", severity: "critical" },
|
|
23
|
+
];
|
|
24
|
+
// Suspicious tool name/description patterns
|
|
25
|
+
const SUSPICIOUS_TOOL_DESC = [
|
|
26
|
+
{ pattern: /run.*any.*command|execute.*arbitrary|shell.*access/i, desc: "Tool claims unrestricted command execution" },
|
|
27
|
+
{ pattern: /access.*all.*files|read.*entire.*filesystem/i, desc: "Tool claims full filesystem access" },
|
|
28
|
+
{ pattern: /send.*data.*to|upload.*to|transmit.*to/i, desc: "Tool description mentions data transmission" },
|
|
29
|
+
{ pattern: /modify.*system|change.*config/i, desc: "Tool claims system modification capability" },
|
|
30
|
+
];
|
|
31
|
+
// Dynamic tool loading patterns (runtime tool registration from external sources)
|
|
32
|
+
const DYNAMIC_TOOL_PATTERNS = [
|
|
33
|
+
{ pattern: /(?:fetch|axios|got|request)\s*\([^)]*(?:tools|schema|manifest|definition)/i, desc: "Fetches tool definitions from external URL" },
|
|
34
|
+
{ pattern: /(?:import|require|load)\s*\([^)]*(?:tool|plugin|extension)\s*(?:url|endpoint|remote)/i, desc: "Dynamically imports tools from remote source" },
|
|
35
|
+
{ pattern: /(?:register|add)Tool\s*\(\s*(?:await\s+)?(?:fetch|get|load)/i, desc: "Registers tools loaded from external source" },
|
|
36
|
+
{ pattern: /tools\s*=\s*(?:await\s+)?(?:fetch|axios|got)\s*\(/i, desc: "Tool list fetched from remote URL" },
|
|
37
|
+
];
|
|
38
|
+
export const mcpManifestRule = {
|
|
39
|
+
id: "mcp-manifest",
|
|
40
|
+
name: "MCP Server Validation",
|
|
41
|
+
description: "Validates MCP server tool/resource declarations against actual code behavior",
|
|
42
|
+
run(files) {
|
|
43
|
+
const findings = [];
|
|
44
|
+
// Entity count and config checks work on any JSON with mcpServers
|
|
45
|
+
checkEntityCount(files, findings);
|
|
46
|
+
// Detect if this is an MCP server project for deeper checks
|
|
47
|
+
const isMcpServer = detectMcpServer(files);
|
|
48
|
+
if (!isMcpServer)
|
|
49
|
+
return findings;
|
|
50
|
+
// Check for MCP manifest/config files
|
|
51
|
+
checkMcpConfig(files, findings);
|
|
52
|
+
// Analyze tool implementations for dangerous patterns
|
|
53
|
+
checkToolImplementations(files, findings);
|
|
54
|
+
// Check tool descriptions for suspicious claims
|
|
55
|
+
checkToolDescriptions(files, findings);
|
|
56
|
+
// Check for dynamic tool loading from external sources
|
|
57
|
+
checkDynamicToolLoading(files, findings);
|
|
58
|
+
// Check if tools are registered but have no input validation
|
|
59
|
+
checkInputValidation(files, findings);
|
|
60
|
+
return findings;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
function detectMcpServer(files) {
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
// Check package.json for MCP-related deps
|
|
66
|
+
if (file.relativePath === "package.json") {
|
|
67
|
+
try {
|
|
68
|
+
const pkg = JSON.parse(file.content);
|
|
69
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
70
|
+
if (allDeps["@modelcontextprotocol/sdk"] ||
|
|
71
|
+
allDeps["@anthropic-ai/sdk"] ||
|
|
72
|
+
pkg.mcp) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// ignore parse errors
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check for MCP imports in code
|
|
81
|
+
if (file.ext === ".ts" || file.ext === ".js" || file.ext === ".mjs") {
|
|
82
|
+
if (file.content.includes("@modelcontextprotocol/sdk") ||
|
|
83
|
+
file.content.includes("McpServer") ||
|
|
84
|
+
file.content.includes("createMcpServer") ||
|
|
85
|
+
TOOL_REGISTER_RE.test(file.content)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Check for mcp.json or similar config
|
|
90
|
+
if (file.relativePath === "mcp.json" ||
|
|
91
|
+
file.relativePath.endsWith("/mcp.json")) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
function checkMcpConfig(files, findings) {
|
|
98
|
+
const mcpConfig = files.find((f) => f.relativePath === "mcp.json" || f.relativePath.endsWith("/mcp.json"));
|
|
99
|
+
if (mcpConfig) {
|
|
100
|
+
try {
|
|
101
|
+
const config = JSON.parse(mcpConfig.content);
|
|
102
|
+
// Check for overly broad permissions
|
|
103
|
+
if (config.permissions) {
|
|
104
|
+
const perms = Array.isArray(config.permissions)
|
|
105
|
+
? config.permissions
|
|
106
|
+
: Object.keys(config.permissions);
|
|
107
|
+
if (perms.length > 5) {
|
|
108
|
+
findings.push({
|
|
109
|
+
rule: "mcp-manifest",
|
|
110
|
+
severity: "warning",
|
|
111
|
+
file: mcpConfig.relativePath,
|
|
112
|
+
message: `MCP config declares ${perms.length} permissions — consider reducing scope`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check for wildcard or dangerous permissions
|
|
117
|
+
const configStr = JSON.stringify(config);
|
|
118
|
+
if (configStr.includes('"*"') || configStr.includes('"all"')) {
|
|
119
|
+
findings.push({
|
|
120
|
+
rule: "mcp-manifest",
|
|
121
|
+
severity: "critical",
|
|
122
|
+
file: mcpConfig.relativePath,
|
|
123
|
+
message: "MCP config uses wildcard/all permissions",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
findings.push({
|
|
129
|
+
rule: "mcp-manifest",
|
|
130
|
+
severity: "warning",
|
|
131
|
+
file: mcpConfig.relativePath,
|
|
132
|
+
message: "Invalid JSON in MCP config file",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkToolImplementations(files, findings) {
|
|
138
|
+
const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs" || f.ext === ".cjs");
|
|
139
|
+
for (const file of codeFiles) {
|
|
140
|
+
// Only check files that register tools
|
|
141
|
+
if (!TOOL_REGISTER_RE.test(file.content) && !RESOURCE_REGISTER_RE.test(file.content)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
145
|
+
const line = file.lines[i];
|
|
146
|
+
const trimmed = line.trimStart();
|
|
147
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*"))
|
|
148
|
+
continue;
|
|
149
|
+
for (const { pattern, desc, severity } of DANGEROUS_TOOL_PATTERNS) {
|
|
150
|
+
if (pattern.test(line)) {
|
|
151
|
+
findings.push({
|
|
152
|
+
rule: "mcp-manifest",
|
|
153
|
+
severity,
|
|
154
|
+
file: file.relativePath,
|
|
155
|
+
line: i + 1,
|
|
156
|
+
message: `MCP tool: ${desc}`,
|
|
157
|
+
evidence: line.trim().slice(0, 120),
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function checkToolDescriptions(files, findings) {
|
|
166
|
+
const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs");
|
|
167
|
+
for (const file of codeFiles) {
|
|
168
|
+
// Look for tool description strings
|
|
169
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
170
|
+
const line = file.lines[i];
|
|
171
|
+
for (const { pattern, desc } of SUSPICIOUS_TOOL_DESC) {
|
|
172
|
+
if (pattern.test(line)) {
|
|
173
|
+
findings.push({
|
|
174
|
+
rule: "mcp-manifest",
|
|
175
|
+
severity: "warning",
|
|
176
|
+
file: file.relativePath,
|
|
177
|
+
line: i + 1,
|
|
178
|
+
message: `Suspicious MCP tool description: ${desc}`,
|
|
179
|
+
evidence: line.trim().slice(0, 120),
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function checkInputValidation(files, findings) {
|
|
188
|
+
const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs");
|
|
189
|
+
for (const file of codeFiles) {
|
|
190
|
+
if (!TOOL_REGISTER_RE.test(file.content))
|
|
191
|
+
continue;
|
|
192
|
+
// Check for tools that accept path inputs without validation
|
|
193
|
+
const hasPathInput = /path|file|dir|folder/i.test(file.content);
|
|
194
|
+
const hasPathValidation = /sanitize|validate|allowlist|whitelist|isAbsolute|normalize|resolve/i.test(file.content);
|
|
195
|
+
const hasTraversalCheck = /\.\.\//i.test(file.content) || /path.*traversal/i.test(file.content);
|
|
196
|
+
if (hasPathInput && !hasPathValidation && !hasTraversalCheck) {
|
|
197
|
+
findings.push({
|
|
198
|
+
rule: "mcp-manifest",
|
|
199
|
+
severity: "warning",
|
|
200
|
+
file: file.relativePath,
|
|
201
|
+
message: "MCP tool accepts path inputs but has no visible path validation/sanitization",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Check for dynamic tool loading from external sources */
|
|
207
|
+
function checkDynamicToolLoading(files, findings) {
|
|
208
|
+
const codeFiles = files.filter((f) => f.ext === ".ts" || f.ext === ".js" || f.ext === ".mjs" || f.ext === ".py");
|
|
209
|
+
for (const file of codeFiles) {
|
|
210
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
211
|
+
const line = file.lines[i];
|
|
212
|
+
const trimmed = line.trimStart();
|
|
213
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*"))
|
|
214
|
+
continue;
|
|
215
|
+
for (const { pattern, desc } of DYNAMIC_TOOL_PATTERNS) {
|
|
216
|
+
if (pattern.test(line)) {
|
|
217
|
+
findings.push({
|
|
218
|
+
rule: "mcp-manifest",
|
|
219
|
+
severity: "critical",
|
|
220
|
+
file: file.relativePath,
|
|
221
|
+
line: i + 1,
|
|
222
|
+
message: `Dynamic tool loading: ${desc}`,
|
|
223
|
+
evidence: line.trim().slice(0, 120),
|
|
224
|
+
});
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** Check entity count (Snyk W002: too many tools/resources) */
|
|
232
|
+
function checkEntityCount(files, findings) {
|
|
233
|
+
for (const file of files) {
|
|
234
|
+
if (file.ext !== ".json")
|
|
235
|
+
continue;
|
|
236
|
+
try {
|
|
237
|
+
const config = JSON.parse(file.content);
|
|
238
|
+
const servers = config.mcpServers || config.servers || {};
|
|
239
|
+
for (const [serverName, serverConfig] of Object.entries(servers)) {
|
|
240
|
+
if (!serverConfig || typeof serverConfig !== "object")
|
|
241
|
+
continue;
|
|
242
|
+
const sc = serverConfig;
|
|
243
|
+
const tools = Array.isArray(sc.tools) ? sc.tools.length : 0;
|
|
244
|
+
const resources = Array.isArray(sc.resources) ? sc.resources.length : 0;
|
|
245
|
+
const prompts = Array.isArray(sc.prompts) ? sc.prompts.length : 0;
|
|
246
|
+
const total = tools + resources + prompts;
|
|
247
|
+
if (total > 100) {
|
|
248
|
+
findings.push({
|
|
249
|
+
rule: "mcp-manifest",
|
|
250
|
+
severity: "warning",
|
|
251
|
+
file: file.relativePath,
|
|
252
|
+
message: `W002: Server "${serverName}" exposes ${total} entities (${tools} tools, ${resources} resources, ${prompts} prompts) — agent performance may degrade above 100`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else if (total > 50) {
|
|
256
|
+
findings.push({
|
|
257
|
+
rule: "mcp-manifest",
|
|
258
|
+
severity: "info",
|
|
259
|
+
file: file.relativePath,
|
|
260
|
+
message: `Server "${serverName}" exposes ${total} entities — consider reducing for optimal agent performance`,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// Not valid JSON
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=mcp-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-manifest.js","sourceRoot":"","sources":["../../src/rules/mcp-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: network-ssrf
|
|
3
|
+
* Detects Server-Side Request Forgery patterns — user-controlled URLs in HTTP requests.
|
|
4
|
+
*/
|
|
5
|
+
const SSRF_PATTERNS = [
|
|
6
|
+
// Template literals in fetch/request
|
|
7
|
+
{ pattern: /fetch\s*\(\s*`[^`]*\$\{/, desc: "fetch() with template literal URL — potential SSRF", severity: "warning" },
|
|
8
|
+
{ pattern: /axios\.\w+\s*\(\s*`[^`]*\$\{/, desc: "axios with template literal URL — potential SSRF", severity: "warning" },
|
|
9
|
+
{ pattern: /http\.request\s*\(\s*`[^`]*\$\{/, desc: "http.request with template literal URL — potential SSRF", severity: "warning" },
|
|
10
|
+
// URL constructed from user input
|
|
11
|
+
{ pattern: /new\s+URL\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, desc: "URL from request parameters — potential SSRF", severity: "critical" },
|
|
12
|
+
{ pattern: /fetch\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, desc: "fetch() with request parameter — potential SSRF", severity: "critical" },
|
|
13
|
+
// Redirect to user-controlled URL
|
|
14
|
+
{ pattern: /redirect\s*\(\s*(?:req\.|request\.|params\.|query\.)/, desc: "Open redirect — user-controlled redirect URL", severity: "warning" },
|
|
15
|
+
// Internal network access patterns
|
|
16
|
+
{ pattern: /127\.0\.0\.1|0\.0\.0\.0|localhost.*fetch|fetch.*localhost/i, desc: "Request to localhost — verify if intentional", severity: "warning" },
|
|
17
|
+
{ pattern: /169\.254\.169\.254/, desc: "AWS metadata endpoint access — potential SSRF", severity: "critical" },
|
|
18
|
+
];
|
|
19
|
+
export const networkSsrfRule = {
|
|
20
|
+
id: "network-ssrf",
|
|
21
|
+
name: "Server-Side Request Forgery",
|
|
22
|
+
description: "Detects user-controlled URLs in HTTP requests and internal network access",
|
|
23
|
+
run(files) {
|
|
24
|
+
const findings = [];
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
if (file.ext === ".json" || file.ext === ".yaml" || file.ext === ".yml" || file.ext === ".md")
|
|
27
|
+
continue;
|
|
28
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
29
|
+
const line = file.lines[i];
|
|
30
|
+
const trimmed = line.trimStart();
|
|
31
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#"))
|
|
32
|
+
continue;
|
|
33
|
+
for (const { pattern, desc, severity } of SSRF_PATTERNS) {
|
|
34
|
+
if (pattern.test(line)) {
|
|
35
|
+
findings.push({
|
|
36
|
+
rule: "network-ssrf",
|
|
37
|
+
severity,
|
|
38
|
+
file: file.relativePath,
|
|
39
|
+
line: i + 1,
|
|
40
|
+
message: desc,
|
|
41
|
+
evidence: line.trim().slice(0, 120),
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return findings;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=network-ssrf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-ssrf.js","sourceRoot":"","sources":["../../src/rules/network-ssrf.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,aAAa,GAA+E;IAChG,qCAAqC;IACrC,EAAE,OAAO,EAAE,yBAAyB,EAAE,IAAI,EAAE,oDAAoD,EAAE,QAAQ,EAAE,SAAS,EAAE;IACvH,EAAE,OAAO,EAAE,8BAA8B,EAAE,IAAI,EAAE,kDAAkD,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC1H,EAAE,OAAO,EAAE,iCAAiC,EAAE,IAAI,EAAE,yDAAyD,EAAE,QAAQ,EAAE,SAAS,EAAE;IACpI,kCAAkC;IAClC,EAAE,OAAO,EAAE,8DAA8D,EAAE,IAAI,EAAE,8CAA8C,EAAE,QAAQ,EAAE,UAAU,EAAE;IACvJ,EAAE,OAAO,EAAE,0DAA0D,EAAE,IAAI,EAAE,iDAAiD,EAAE,QAAQ,EAAE,UAAU,EAAE;IACtJ,kCAAkC;IAClC,EAAE,OAAO,EAAE,sDAAsD,EAAE,IAAI,EAAE,8CAA8C,EAAE,QAAQ,EAAE,SAAS,EAAE;IAC9I,mCAAmC;IACnC,EAAE,OAAO,EAAE,4DAA4D,EAAE,IAAI,EAAE,8CAA8C,EAAE,QAAQ,EAAE,SAAS,EAAE;IACpJ,EAAE,OAAO,EAAE,oBAAoB,EAAE,IAAI,EAAE,+CAA+C,EAAE,QAAQ,EAAE,UAAU,EAAE;CAC/G,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAS;IACnC,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,6BAA6B;IACnC,WAAW,EAAE,2EAA2E;IAExF,GAAG,CAAC,KAAoB;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK;gBAAE,SAAS;YAExG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAElE,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,aAAa,EAAE,CAAC;oBACxD,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;wBACvB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,cAAc;4BACpB,QAAQ;4BACR,IAAI,EAAE,IAAI,CAAC,YAAY;4BACvB,IAAI,EAAE,CAAC,GAAG,CAAC;4BACX,OAAO,EAAE,IAAI;4BACb,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;yBACpC,CAAC,CAAC;wBACH,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: obfuscation
|
|
3
|
+
* Detects base64 decoding + eval/exec combos and other obfuscation patterns.
|
|
4
|
+
*/
|
|
5
|
+
const OBFUSCATION_PATTERNS = [
|
|
6
|
+
{ pattern: /atob\s*\(.*\beval\b|eval\s*\(.*\batob\b/, desc: "atob() + eval() combo", severity: "critical" },
|
|
7
|
+
{ pattern: /Buffer\.from\s*\([^)]*,\s*["']base64["']\).*\beval\b/, desc: "Base64 decode + eval()", severity: "critical" },
|
|
8
|
+
{ pattern: /Buffer\.from\s*\([^)]*,\s*["']base64["']\).*\bexec\b/, desc: "Base64 decode + exec()", severity: "critical" },
|
|
9
|
+
{ pattern: /\bString\.fromCharCode\s*\(/, desc: "String.fromCharCode() — potential obfuscation", severity: "warning" },
|
|
10
|
+
{ pattern: /\\x[0-9a-f]{2}\\x[0-9a-f]{2}\\x[0-9a-f]{2}/, desc: "Hex-encoded string sequence", severity: "warning" },
|
|
11
|
+
{ pattern: /\\u00[0-9a-f]{2}\\u00[0-9a-f]{2}/, desc: "Unicode-escaped string sequence", severity: "warning" },
|
|
12
|
+
];
|
|
13
|
+
export const obfuscationRule = {
|
|
14
|
+
id: "obfuscation",
|
|
15
|
+
name: "Code Obfuscation",
|
|
16
|
+
description: "Detects base64+eval combos, hex encoding, and other obfuscation techniques",
|
|
17
|
+
run(files) {
|
|
18
|
+
const findings = [];
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
if (file.ext === ".json" || file.ext === ".yaml" || file.ext === ".yml" || file.ext === ".md")
|
|
21
|
+
continue;
|
|
22
|
+
// Check multi-line patterns across entire content
|
|
23
|
+
for (const { pattern, desc, severity } of OBFUSCATION_PATTERNS.slice(0, 3)) {
|
|
24
|
+
if (pattern.test(file.content)) {
|
|
25
|
+
// Find the line with eval/exec
|
|
26
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
27
|
+
if (/\beval\b|\bexec\b/.test(file.lines[i])) {
|
|
28
|
+
findings.push({ rule: "obfuscation", severity, file: file.relativePath, line: i + 1, message: desc, evidence: file.lines[i].trim().slice(0, 120) });
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Per-line patterns
|
|
35
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
36
|
+
const line = file.lines[i];
|
|
37
|
+
const trimmed = line.trimStart();
|
|
38
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#"))
|
|
39
|
+
continue;
|
|
40
|
+
for (const { pattern, desc, severity } of OBFUSCATION_PATTERNS.slice(3)) {
|
|
41
|
+
if (pattern.test(line)) {
|
|
42
|
+
findings.push({ rule: "obfuscation", severity, file: file.relativePath, line: i + 1, message: desc, evidence: line.trim().slice(0, 120) });
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return findings;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=obfuscation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obfuscation.js","sourceRoot":"","sources":["../../src/rules/obfuscation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: phone-home
|
|
3
|
+
* Detects periodic timers combined with HTTP requests — "calling home" patterns.
|
|
4
|
+
*/
|
|
5
|
+
const TIMER_RE = /setInterval\s*\(|cron\s*\(|schedule\s*\(|setTimeout.*setInterval|recurring|periodic/i;
|
|
6
|
+
const HTTP_RE = /fetch\s*\(|axios\.|http\.request|https\.request|\.post\s*\(|\.get\s*\(/i;
|
|
7
|
+
export const phoneHomeRule = {
|
|
8
|
+
id: "phone-home",
|
|
9
|
+
name: "Phone Home / Beacon",
|
|
10
|
+
description: "Detects periodic timers combined with HTTP requests (heartbeat/beacon pattern)",
|
|
11
|
+
run(files) {
|
|
12
|
+
const findings = [];
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
if (file.ext === ".json" || file.ext === ".yaml" || file.ext === ".yml" || file.ext === ".md")
|
|
15
|
+
continue;
|
|
16
|
+
const hasTimer = TIMER_RE.test(file.content);
|
|
17
|
+
const hasHttp = HTTP_RE.test(file.content);
|
|
18
|
+
if (hasTimer && hasHttp) {
|
|
19
|
+
// Find the timer line
|
|
20
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
21
|
+
if (TIMER_RE.test(file.lines[i])) {
|
|
22
|
+
findings.push({
|
|
23
|
+
rule: "phone-home",
|
|
24
|
+
severity: "warning",
|
|
25
|
+
file: file.relativePath,
|
|
26
|
+
line: i + 1,
|
|
27
|
+
message: "Periodic timer + HTTP request — possible beacon/phone-home pattern",
|
|
28
|
+
evidence: file.lines[i].trim().slice(0, 120),
|
|
29
|
+
});
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return findings;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=phone-home.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phone-home.js","sourceRoot":"","sources":["../../src/rules/phone-home.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,QAAQ,GAAG,sFAAsF,CAAC;AACxG,MAAM,OAAO,GAAG,yEAAyE,CAAC;AAE1F,MAAM,CAAC,MAAM,aAAa,GAAS;IACjC,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,qBAAqB;IAC3B,WAAW,EAAE,gFAAgF;IAE7F,GAAG,CAAC,KAAoB;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK;gBAAE,SAAS;YAExG,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE3C,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;gBACxB,sBAAsB;gBACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3C,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;wBAClC,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,YAAY;4BAClB,QAAQ,EAAE,SAAS;4BACnB,IAAI,EAAE,IAAI,CAAC,YAAY;4BACvB,IAAI,EAAE,CAAC,GAAG,CAAC;4BACX,OAAO,EAAE,oEAAoE;4BAC7E,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;yBAC9C,CAAC,CAAC;wBACH,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import matter from "gray-matter";
|
|
2
|
+
/**
|
|
3
|
+
* Rule: privilege
|
|
4
|
+
* Compares declared permissions in SKILL.md vs actual API usage in code.
|
|
5
|
+
*/
|
|
6
|
+
// Map of capabilities to code patterns that indicate their use
|
|
7
|
+
const CAPABILITY_PATTERNS = {
|
|
8
|
+
exec: /child_process|execSync|exec\(|spawn\(|os\.system|subprocess|ShellAction/i,
|
|
9
|
+
read: /readFile|readFileSync|fs\.read|open\(.*["']r/i,
|
|
10
|
+
write: /writeFile|writeFileSync|fs\.write|appendFile|open\(.*["']w/i,
|
|
11
|
+
web_fetch: /fetch\s*\(|axios|http\.request|https\.request|requests\.(get|post)/i,
|
|
12
|
+
browser: /puppeteer|playwright|selenium|webdriver|BrowserAction/i,
|
|
13
|
+
network: /net\.connect|dgram|WebSocket|Socket/i,
|
|
14
|
+
};
|
|
15
|
+
export const privilegeRule = {
|
|
16
|
+
id: "privilege",
|
|
17
|
+
name: "Privilege Mismatch",
|
|
18
|
+
description: "Compares declared permissions in SKILL.md against actual code behavior",
|
|
19
|
+
run(files) {
|
|
20
|
+
const findings = [];
|
|
21
|
+
// Find SKILL.md
|
|
22
|
+
const skillMd = files.find((f) => f.relativePath === "SKILL.md" || f.relativePath.endsWith("/SKILL.md"));
|
|
23
|
+
if (!skillMd) {
|
|
24
|
+
// No SKILL.md — can't check permissions
|
|
25
|
+
findings.push({
|
|
26
|
+
rule: "privilege",
|
|
27
|
+
severity: "info",
|
|
28
|
+
file: ".",
|
|
29
|
+
message: "No SKILL.md found — permission analysis skipped",
|
|
30
|
+
});
|
|
31
|
+
return findings;
|
|
32
|
+
}
|
|
33
|
+
// Parse frontmatter
|
|
34
|
+
let meta = {};
|
|
35
|
+
try {
|
|
36
|
+
const { data } = matter(skillMd.content);
|
|
37
|
+
meta = data;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// not valid frontmatter
|
|
41
|
+
}
|
|
42
|
+
// Extract declared permissions (from frontmatter or body)
|
|
43
|
+
const declaredPerms = new Set();
|
|
44
|
+
if (Array.isArray(meta.permissions)) {
|
|
45
|
+
for (const p of meta.permissions) {
|
|
46
|
+
if (typeof p === "string")
|
|
47
|
+
declaredPerms.add(p.toLowerCase());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Also scan SKILL.md body for permission keywords
|
|
51
|
+
const bodyPermsMatch = skillMd.content.match(/permissions?:\s*([\w,\s]+)/i);
|
|
52
|
+
if (bodyPermsMatch) {
|
|
53
|
+
for (const p of bodyPermsMatch[1].split(/[,\s]+/)) {
|
|
54
|
+
if (p.trim())
|
|
55
|
+
declaredPerms.add(p.trim().toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Scan code files for actual capability usage
|
|
59
|
+
const codeFiles = files.filter((f) => f.ext !== ".md" && f.ext !== ".json" && f.ext !== ".yaml" && f.ext !== ".yml");
|
|
60
|
+
const usedCapabilities = new Set();
|
|
61
|
+
const capabilityLocations = {};
|
|
62
|
+
for (const file of codeFiles) {
|
|
63
|
+
for (const [cap, pattern] of Object.entries(CAPABILITY_PATTERNS)) {
|
|
64
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
65
|
+
if (pattern.test(file.lines[i])) {
|
|
66
|
+
usedCapabilities.add(cap);
|
|
67
|
+
if (!capabilityLocations[cap])
|
|
68
|
+
capabilityLocations[cap] = [];
|
|
69
|
+
capabilityLocations[cap].push({ file: file.relativePath, line: i + 1 });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Report undeclared capabilities
|
|
75
|
+
for (const cap of usedCapabilities) {
|
|
76
|
+
if (declaredPerms.size > 0 && !declaredPerms.has(cap)) {
|
|
77
|
+
const locations = capabilityLocations[cap] || [];
|
|
78
|
+
const first = locations[0];
|
|
79
|
+
findings.push({
|
|
80
|
+
rule: "privilege",
|
|
81
|
+
severity: "warning",
|
|
82
|
+
file: first?.file || skillMd.relativePath,
|
|
83
|
+
line: first?.line,
|
|
84
|
+
message: `Code uses '${cap}' capability but SKILL.md doesn't declare it (found in ${locations.length} location${locations.length > 1 ? "s" : ""})`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Report declared but unused permissions
|
|
89
|
+
for (const perm of declaredPerms) {
|
|
90
|
+
if (!usedCapabilities.has(perm) && CAPABILITY_PATTERNS[perm]) {
|
|
91
|
+
findings.push({
|
|
92
|
+
rule: "privilege",
|
|
93
|
+
severity: "info",
|
|
94
|
+
file: skillMd.relativePath,
|
|
95
|
+
message: `SKILL.md declares '${perm}' permission but code doesn't appear to use it`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Report used capabilities as info
|
|
100
|
+
if (usedCapabilities.size > 0) {
|
|
101
|
+
findings.push({
|
|
102
|
+
rule: "privilege",
|
|
103
|
+
severity: "info",
|
|
104
|
+
file: skillMd.relativePath,
|
|
105
|
+
message: `Detected capabilities: ${[...usedCapabilities].join(", ")}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return findings;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=privilege.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privilege.js","sourceRoot":"","sources":["../../src/rules/privilege.ts"],"names":[],"mappings":"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"}
|