@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.
Files changed (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -0
  3. package/README.zh-CN.md +130 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +265 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +24 -0
  8. package/dist/config.js +91 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/discover.d.ts +9 -0
  11. package/dist/discover.js +143 -0
  12. package/dist/discover.js.map +1 -0
  13. package/dist/llm/anthropic.d.ts +10 -0
  14. package/dist/llm/anthropic.js +67 -0
  15. package/dist/llm/anthropic.js.map +1 -0
  16. package/dist/llm/index.d.ts +10 -0
  17. package/dist/llm/index.js +41 -0
  18. package/dist/llm/index.js.map +1 -0
  19. package/dist/llm/ollama.d.ts +9 -0
  20. package/dist/llm/ollama.js +61 -0
  21. package/dist/llm/ollama.js.map +1 -0
  22. package/dist/llm/openai.d.ts +10 -0
  23. package/dist/llm/openai.js +66 -0
  24. package/dist/llm/openai.js.map +1 -0
  25. package/dist/llm/prompt.d.ts +3 -0
  26. package/dist/llm/prompt.js +31 -0
  27. package/dist/llm/prompt.js.map +1 -0
  28. package/dist/llm/types.d.ts +23 -0
  29. package/dist/llm/types.js +3 -0
  30. package/dist/llm/types.js.map +1 -0
  31. package/dist/llm-analyzer.d.ts +13 -0
  32. package/dist/llm-analyzer.js +169 -0
  33. package/dist/llm-analyzer.js.map +1 -0
  34. package/dist/reporter/badge.d.ts +7 -0
  35. package/dist/reporter/badge.js +50 -0
  36. package/dist/reporter/badge.js.map +1 -0
  37. package/dist/reporter/json.d.ts +3 -0
  38. package/dist/reporter/json.js +5 -0
  39. package/dist/reporter/json.js.map +1 -0
  40. package/dist/reporter/terminal.d.ts +2 -0
  41. package/dist/reporter/terminal.js +64 -0
  42. package/dist/reporter/terminal.js.map +1 -0
  43. package/dist/rules/backdoor.d.ts +2 -0
  44. package/dist/rules/backdoor.js +57 -0
  45. package/dist/rules/backdoor.js.map +1 -0
  46. package/dist/rules/credential-hardcode.d.ts +2 -0
  47. package/dist/rules/credential-hardcode.js +57 -0
  48. package/dist/rules/credential-hardcode.js.map +1 -0
  49. package/dist/rules/crypto-mining.d.ts +2 -0
  50. package/dist/rules/crypto-mining.js +41 -0
  51. package/dist/rules/crypto-mining.js.map +1 -0
  52. package/dist/rules/data-exfil.d.ts +2 -0
  53. package/dist/rules/data-exfil.js +61 -0
  54. package/dist/rules/data-exfil.js.map +1 -0
  55. package/dist/rules/env-leak.d.ts +2 -0
  56. package/dist/rules/env-leak.js +43 -0
  57. package/dist/rules/env-leak.js.map +1 -0
  58. package/dist/rules/excessive-perms.d.ts +2 -0
  59. package/dist/rules/excessive-perms.js +50 -0
  60. package/dist/rules/excessive-perms.js.map +1 -0
  61. package/dist/rules/hidden-files.d.ts +2 -0
  62. package/dist/rules/hidden-files.js +52 -0
  63. package/dist/rules/hidden-files.js.map +1 -0
  64. package/dist/rules/index.d.ts +5 -0
  65. package/dist/rules/index.js +53 -0
  66. package/dist/rules/index.js.map +1 -0
  67. package/dist/rules/mcp-manifest.d.ts +2 -0
  68. package/dist/rules/mcp-manifest.js +270 -0
  69. package/dist/rules/mcp-manifest.js.map +1 -0
  70. package/dist/rules/network-ssrf.d.ts +2 -0
  71. package/dist/rules/network-ssrf.js +51 -0
  72. package/dist/rules/network-ssrf.js.map +1 -0
  73. package/dist/rules/obfuscation.d.ts +2 -0
  74. package/dist/rules/obfuscation.js +51 -0
  75. package/dist/rules/obfuscation.js.map +1 -0
  76. package/dist/rules/phone-home.d.ts +2 -0
  77. package/dist/rules/phone-home.js +38 -0
  78. package/dist/rules/phone-home.js.map +1 -0
  79. package/dist/rules/privilege.d.ts +2 -0
  80. package/dist/rules/privilege.js +111 -0
  81. package/dist/rules/privilege.js.map +1 -0
  82. package/dist/rules/prompt-injection.d.ts +2 -0
  83. package/dist/rules/prompt-injection.js +323 -0
  84. package/dist/rules/prompt-injection.js.map +1 -0
  85. package/dist/rules/reverse-shell.d.ts +2 -0
  86. package/dist/rules/reverse-shell.js +53 -0
  87. package/dist/rules/reverse-shell.js.map +1 -0
  88. package/dist/rules/sensitive-read.d.ts +2 -0
  89. package/dist/rules/sensitive-read.js +53 -0
  90. package/dist/rules/sensitive-read.js.map +1 -0
  91. package/dist/rules/skill-risks.d.ts +2 -0
  92. package/dist/rules/skill-risks.js +148 -0
  93. package/dist/rules/skill-risks.js.map +1 -0
  94. package/dist/rules/supply-chain.d.ts +6 -0
  95. package/dist/rules/supply-chain.js +105 -0
  96. package/dist/rules/supply-chain.js.map +1 -0
  97. package/dist/rules/tool-shadowing.d.ts +2 -0
  98. package/dist/rules/tool-shadowing.js +129 -0
  99. package/dist/rules/tool-shadowing.js.map +1 -0
  100. package/dist/rules/toxic-flow.d.ts +2 -0
  101. package/dist/rules/toxic-flow.js +160 -0
  102. package/dist/rules/toxic-flow.js.map +1 -0
  103. package/dist/rules/typosquatting.d.ts +2 -0
  104. package/dist/rules/typosquatting.js +56 -0
  105. package/dist/rules/typosquatting.js.map +1 -0
  106. package/dist/scanner/files.d.ts +5 -0
  107. package/dist/scanner/files.js +105 -0
  108. package/dist/scanner/files.js.map +1 -0
  109. package/dist/scanner/index.d.ts +6 -0
  110. package/dist/scanner/index.js +198 -0
  111. package/dist/scanner/index.js.map +1 -0
  112. package/dist/score.d.ts +14 -0
  113. package/dist/score.js +35 -0
  114. package/dist/score.js.map +1 -0
  115. package/dist/types.d.ts +60 -0
  116. package/dist/types.js +2 -0
  117. package/dist/types.js.map +1 -0
  118. package/dist/yaml-simple.d.ts +6 -0
  119. package/dist/yaml-simple.js +98 -0
  120. package/dist/yaml-simple.js.map +1 -0
  121. package/package.json +72 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Rule: skill-risks
3
+ * Detects high-risk patterns in agent skills that may indicate
4
+ * dangerous capabilities or insecure practices.
5
+ *
6
+ * Based on Snyk Agent Scan issue codes:
7
+ * - W007: Insecure credential handling
8
+ * - W009: Direct financial execution
9
+ * - W011: Exposure to untrusted third-party content
10
+ * - W012: Unverifiable external dependency (runtime fetch of instructions)
11
+ * - W013: System service modification
12
+ */
13
+ // W007: Insecure credential handling — skill asks agent to output secrets verbatim
14
+ const INSECURE_CRED_PATTERNS = [
15
+ { pattern: /(?:paste|type|enter|input|write|include|put)\s+(?:your\s+)?(?:api\s+key|token|password|secret|credential|access\s+key)\s+(?:here|below|in\s+the|into|directly)/i, description: "Asks user to paste credentials directly" },
16
+ { pattern: /(?:hardcode|embed|include|put)\s+(?:the\s+)?(?:api\s+key|token|password|secret)\s+(?:in|into|inside)\s+(?:the\s+)?(?:code|file|config|script)/i, description: "Instructs embedding secrets in code" },
17
+ { pattern: /(?:api_key|token|password|secret)\s*[:=]\s*["'][A-Za-z0-9_\-]{20,}["']/i, description: "Appears to contain a hardcoded credential in instructions" },
18
+ ];
19
+ // W009: Financial execution capabilities
20
+ const FINANCIAL_PATTERNS = [
21
+ { pattern: /(?:send|transfer|move|wire)\s+(?:money|funds|payment|bitcoin|crypto|eth|btc|usdt)/i, description: "Direct money transfer capability" },
22
+ { pattern: /(?:execute|place|submit)\s+(?:a\s+)?(?:trade|order|transaction|purchase|buy\s+order|sell\s+order)/i, description: "Financial trade/order execution" },
23
+ { pattern: /(?:stripe|paypal|braintree|square)\.(?:charges?|payments?)\.create/i, description: "Payment processing API call" },
24
+ { pattern: /(?:web3|ethers|solana).*(?:sendTransaction|transfer|sign)/i, description: "Blockchain transaction execution" },
25
+ ];
26
+ // W011: Untrusted content exposure
27
+ const UNTRUSTED_CONTENT_PATTERNS = [
28
+ { pattern: /(?:browse|visit|open|navigate|fetch|scrape|crawl)\s+(?:any\s+)?(?:arbitrary|user[- ]?provided|unknown|untrusted)\s+(?:url|website|webpage|link|page)/i, description: "Browses arbitrary user-provided URLs" },
29
+ { pattern: /(?:read|parse|analyze|process)\s+(?:social\s+media|forum|reddit|twitter|comments?|reviews?|posts?)\s+(?:from|on|at)/i, description: "Processes untrusted social media content" },
30
+ { pattern: /(?:download|fetch|get|retrieve)\s+(?:content|data|text|html)\s+from\s+(?:any|arbitrary|user)/i, description: "Downloads content from arbitrary sources" },
31
+ ];
32
+ // W012: Unverifiable external dependency — runtime code/instruction fetch
33
+ const EXTERNAL_DEP_PATTERNS = [
34
+ { pattern: /(?:fetch|download|pull|get)\s+(?:the\s+)?(?:latest|updated?|current)\s+(?:instructions?|prompts?|config|rules?|script)\s+from\s+/i, description: "Fetches instructions from external URL at runtime" },
35
+ { pattern: /(?:auto[- ]?update|self[- ]?update|live[- ]?reload)\s+(?:from|via|using)\s+/i, description: "Auto-update mechanism from external source" },
36
+ { pattern: /(?:eval|exec|execute|run)\s*\(\s*(?:await\s+)?(?:fetch|axios|got|request)\s*\(/i, description: "Fetches and executes remote code" },
37
+ { pattern: /(?:import|require|load)\s*\(\s*(?:['"`]https?:\/\/|url|endpoint)/i, description: "Dynamic import from remote URL" },
38
+ ];
39
+ // W013: System service modification
40
+ const SYSTEM_MOD_PATTERNS = [
41
+ { pattern: /(?:sudo|doas|runas)\s+/i, description: "Elevated privilege execution" },
42
+ { pattern: /(?:systemctl|service|launchctl|sc\.exe)\s+(?:start|stop|enable|disable|restart|create)/i, description: "System service management" },
43
+ { pattern: /(?:crontab|schtasks|at\s+\d|launchd)\s+/i, description: "Scheduled task creation" },
44
+ { pattern: /(?:chmod\s+[0-7]*[1-7][0-7]*|chown\s+root|setuid|setgid)/i, description: "Permission/ownership modification" },
45
+ { pattern: /(?:\/etc\/(?:passwd|shadow|sudoers|crontab|hosts|resolv|fstab|profile|rc\.local))/i, description: "Modifies critical system configuration files" },
46
+ { pattern: /(?:iptables|ufw|firewalld|netsh\s+firewall)\s+/i, description: "Firewall rule modification" },
47
+ { pattern: /(?:~\/\.bashrc|~\/\.zshrc|~\/\.profile|~\/\.bash_profile|\/etc\/environment)\b/i, description: "Shell profile/environment modification" },
48
+ ];
49
+ export const skillRisks = {
50
+ id: "skill-risks",
51
+ name: "Skill Risk Assessment",
52
+ description: "Detects high-risk capabilities in skills: financial ops, untrusted content, external deps, system modification",
53
+ run(files) {
54
+ const findings = [];
55
+ for (const file of files) {
56
+ const isSkillMd = file.relativePath.toLowerCase().includes("skill.md");
57
+ const isMarkdown = file.ext === ".md";
58
+ const isCode = [".ts", ".js", ".py", ".sh", ".bash"].includes(file.ext);
59
+ const isConfig = [".json", ".yaml", ".yml"].includes(file.ext);
60
+ if (!isMarkdown && !isCode && !isConfig)
61
+ continue;
62
+ for (let i = 0; i < file.lines.length; i++) {
63
+ const line = file.lines[i];
64
+ const trimmed = line.trimStart();
65
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!"))
66
+ continue;
67
+ // W007: Insecure credential handling (in markdown/docs)
68
+ if (isMarkdown) {
69
+ for (const { pattern, description } of INSECURE_CRED_PATTERNS) {
70
+ if (pattern.test(line)) {
71
+ findings.push({
72
+ rule: "skill-risks",
73
+ severity: "warning",
74
+ file: file.relativePath,
75
+ line: i + 1,
76
+ message: `Insecure credential handling: ${description}`,
77
+ evidence: line.trim().substring(0, 120),
78
+ });
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ // W009: Financial execution
84
+ for (const { pattern, description } of FINANCIAL_PATTERNS) {
85
+ if (pattern.test(line)) {
86
+ findings.push({
87
+ rule: "skill-risks",
88
+ severity: "warning",
89
+ file: file.relativePath,
90
+ line: i + 1,
91
+ message: `Financial execution: ${description}`,
92
+ evidence: line.trim().substring(0, 120),
93
+ });
94
+ break;
95
+ }
96
+ }
97
+ // W011: Untrusted content (primarily in SKILL.md)
98
+ if (isSkillMd || isMarkdown) {
99
+ for (const { pattern, description } of UNTRUSTED_CONTENT_PATTERNS) {
100
+ if (pattern.test(line)) {
101
+ findings.push({
102
+ rule: "skill-risks",
103
+ severity: "warning",
104
+ file: file.relativePath,
105
+ line: i + 1,
106
+ message: `Untrusted content exposure: ${description}`,
107
+ evidence: line.trim().substring(0, 120),
108
+ });
109
+ break;
110
+ }
111
+ }
112
+ }
113
+ // W012: External dependency
114
+ if (isCode || isMarkdown) {
115
+ for (const { pattern, description } of EXTERNAL_DEP_PATTERNS) {
116
+ if (pattern.test(line)) {
117
+ findings.push({
118
+ rule: "skill-risks",
119
+ severity: isCode ? "critical" : "warning",
120
+ file: file.relativePath,
121
+ line: i + 1,
122
+ message: `Unverifiable external dependency: ${description}`,
123
+ evidence: line.trim().substring(0, 120),
124
+ });
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ // W013: System modification
130
+ for (const { pattern, description } of SYSTEM_MOD_PATTERNS) {
131
+ if (pattern.test(line)) {
132
+ findings.push({
133
+ rule: "skill-risks",
134
+ severity: isSkillMd ? "warning" : "info",
135
+ file: file.relativePath,
136
+ line: i + 1,
137
+ message: `System modification: ${description}`,
138
+ evidence: line.trim().substring(0, 120),
139
+ });
140
+ break;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ return findings;
146
+ },
147
+ };
148
+ //# sourceMappingURL=skill-risks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-risks.js","sourceRoot":"","sources":["../../src/rules/skill-risks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Rule } from "../types.js";
2
+ /**
3
+ * Rule: supply-chain
4
+ * Runs npm audit to detect known CVEs in dependencies.
5
+ */
6
+ export declare const supplyChainRule: Rule;
@@ -0,0 +1,105 @@
1
+ import { execSync } from "child_process";
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+ /**
5
+ * Rule: supply-chain
6
+ * Runs npm audit to detect known CVEs in dependencies.
7
+ */
8
+ export const supplyChainRule = {
9
+ id: "supply-chain",
10
+ name: "Supply Chain Audit",
11
+ description: "Checks for known CVEs in npm/pip dependencies",
12
+ run(files) {
13
+ const findings = [];
14
+ // Determine the target directory from the first file
15
+ if (files.length === 0)
16
+ return findings;
17
+ const targetDir = files[0].path.replace(files[0].relativePath, "").replace(/\/$/, "");
18
+ // Check for package.json
19
+ const pkgJsonPath = join(targetDir, "package.json");
20
+ const hasNodeModules = existsSync(join(targetDir, "node_modules"));
21
+ const hasPkgJson = existsSync(pkgJsonPath);
22
+ const hasLockFile = existsSync(join(targetDir, "package-lock.json")) ||
23
+ existsSync(join(targetDir, "yarn.lock")) ||
24
+ existsSync(join(targetDir, "pnpm-lock.yaml"));
25
+ if (hasPkgJson && (hasNodeModules || hasLockFile)) {
26
+ try {
27
+ // npm audit returns exit code > 0 when vulnerabilities are found
28
+ const result = execSync("npm audit --json 2>/dev/null", {
29
+ cwd: targetDir,
30
+ encoding: "utf-8",
31
+ timeout: 30000,
32
+ stdio: ["pipe", "pipe", "pipe"],
33
+ });
34
+ parseNpmAudit(result, findings);
35
+ }
36
+ catch (err) {
37
+ // npm audit exits non-zero when vulns are found
38
+ if (err && typeof err === "object" && "stdout" in err) {
39
+ parseNpmAudit(err.stdout, findings);
40
+ }
41
+ }
42
+ }
43
+ else if (hasPkgJson) {
44
+ // Has package.json but no lock file — check deps manually
45
+ try {
46
+ const pkgContent = files.find((f) => f.relativePath === "package.json");
47
+ if (pkgContent) {
48
+ const pkg = JSON.parse(pkgContent.content);
49
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50
+ const depCount = Object.keys(deps).length;
51
+ if (depCount > 0) {
52
+ findings.push({
53
+ rule: "supply-chain",
54
+ severity: "info",
55
+ file: "package.json",
56
+ message: `${depCount} dependencies declared — run 'npm install && npm audit' for full CVE check`,
57
+ });
58
+ }
59
+ }
60
+ }
61
+ catch {
62
+ // ignore parse errors
63
+ }
64
+ }
65
+ // Check for requirements.txt (Python)
66
+ const reqTxt = files.find((f) => f.relativePath === "requirements.txt" || f.relativePath.endsWith("/requirements.txt"));
67
+ if (reqTxt) {
68
+ findings.push({
69
+ rule: "supply-chain",
70
+ severity: "info",
71
+ file: reqTxt.relativePath,
72
+ message: "Python requirements.txt found — run 'pip-audit' for CVE check",
73
+ });
74
+ }
75
+ return findings;
76
+ },
77
+ };
78
+ function parseNpmAudit(output, findings) {
79
+ try {
80
+ const audit = JSON.parse(output);
81
+ if (audit.vulnerabilities) {
82
+ for (const [name, vuln] of Object.entries(audit.vulnerabilities)) {
83
+ const severity = vuln.severity || "moderate";
84
+ const via = Array.isArray(vuln.via) ? vuln.via : [];
85
+ const cves = via
86
+ .filter((v) => typeof v === "object" && v !== null && "url" in v)
87
+ .map((v) => String(v.url))
88
+ .join(", ");
89
+ const mappedSeverity = severity === "critical" || severity === "high" ? "critical" :
90
+ severity === "moderate" ? "warning" : "info";
91
+ findings.push({
92
+ rule: "supply-chain",
93
+ severity: mappedSeverity,
94
+ file: "package.json",
95
+ message: `${name} — ${severity} severity${cves ? ` (${cves})` : ""}`,
96
+ evidence: typeof vuln.range === "string" ? `affected: ${vuln.range}` : undefined,
97
+ });
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ // unparseable output
103
+ }
104
+ }
105
+ //# sourceMappingURL=supply-chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supply-chain.js","sourceRoot":"","sources":["../../src/rules/supply-chain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const toolShadowing: Rule;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Rule: tool-shadowing
3
+ * Detects when a tool description references tools from other MCP servers,
4
+ * potentially hijacking or overriding their behavior.
5
+ * Also detects cross-server tool name conflicts from MCP config files.
6
+ */
7
+ // Common MCP tool names that could be shadowed
8
+ const WELL_KNOWN_TOOLS = [
9
+ "read_file", "write_file", "list_files", "search_files",
10
+ "execute_command", "run_terminal_command",
11
+ "browser_action", "web_search",
12
+ "create_file", "edit_file", "delete_file",
13
+ "git_commit", "git_push", "git_pull",
14
+ "send_message", "read_email",
15
+ ];
16
+ // Patterns indicating cross-tool reference in descriptions
17
+ const CROSS_TOOL_PATTERNS = [
18
+ { pattern: /(?:instead\s+of|replace|override|intercept|wrap)\s+(?:the\s+)?(?:original|default|built-in|other)\s+(?:tool|function|command)/i, description: "Claims to replace another tool" },
19
+ { pattern: /(?:call|use|invoke)\s+(?:this\s+tool\s+)?(?:instead\s+of|before|after)\s+["']?\w+["']?/i, description: "Redirects from another tool to this one" },
20
+ { pattern: /(?:this|my)\s+(?:version|implementation)\s+of\s+["']?\w+["']?\s+(?:is|should\s+be)/i, description: "Claims to be a version of another tool" },
21
+ { pattern: /(?:enhanced|improved|better|secure)\s+(?:version|replacement)\s+(?:of|for)\s+/i, description: "Claims to be an enhanced version" },
22
+ { pattern: /(?:whenever|every\s+time)\s+(?:you|the\s+agent)\s+(?:would\s+)?(?:use|call|invoke)\s+["']?\w+["']?,?\s+(?:use|call)\s+(?:this|me)/i, description: "Hijacks calls to another tool" },
23
+ ];
24
+ export const toolShadowing = {
25
+ id: "tool-shadowing",
26
+ name: "Tool Shadowing Detection",
27
+ description: "Detects tools that attempt to shadow, override, or hijack other tools",
28
+ run(files) {
29
+ const findings = [];
30
+ // Collect tool names from MCP config files
31
+ const toolsByServer = new Map();
32
+ for (const file of files) {
33
+ // Check JSON config files for MCP tool definitions
34
+ if (file.ext === ".json") {
35
+ try {
36
+ const config = JSON.parse(file.content);
37
+ extractMcpTools(config, file.relativePath, toolsByServer);
38
+ }
39
+ catch {
40
+ // Not valid JSON, skip
41
+ }
42
+ }
43
+ // Check tool descriptions for cross-tool reference patterns
44
+ if ([".json", ".md", ".yaml", ".yml", ".ts", ".js"].includes(file.ext)) {
45
+ for (let i = 0; i < file.lines.length; i++) {
46
+ const line = file.lines[i];
47
+ for (const { pattern, description } of CROSS_TOOL_PATTERNS) {
48
+ pattern.lastIndex = 0;
49
+ if (pattern.test(line)) {
50
+ findings.push({
51
+ rule: "tool-shadowing",
52
+ severity: "critical",
53
+ file: file.relativePath,
54
+ line: i + 1,
55
+ message: `Tool shadowing: ${description}`,
56
+ evidence: line.trim().substring(0, 120),
57
+ });
58
+ break;
59
+ }
60
+ }
61
+ // Check if description references well-known tool names
62
+ const lineLower = line.toLowerCase();
63
+ for (const toolName of WELL_KNOWN_TOOLS) {
64
+ if (lineLower.includes(toolName) &&
65
+ /(?:instead|replace|override|intercept|before|after|wrap)/i.test(line)) {
66
+ findings.push({
67
+ rule: "tool-shadowing",
68
+ severity: "critical",
69
+ file: file.relativePath,
70
+ line: i + 1,
71
+ message: `References well-known tool "${toolName}" with override intent`,
72
+ evidence: line.trim().substring(0, 120),
73
+ });
74
+ break;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ // Check for duplicate tool names across servers
81
+ const allToolNames = new Map(); // toolName -> [server1, server2]
82
+ for (const [serverName, { tools }] of toolsByServer) {
83
+ for (const tool of tools) {
84
+ if (!allToolNames.has(tool))
85
+ allToolNames.set(tool, []);
86
+ allToolNames.get(tool).push(serverName);
87
+ }
88
+ }
89
+ for (const [toolName, servers] of allToolNames) {
90
+ if (servers.length > 1) {
91
+ findings.push({
92
+ rule: "tool-shadowing",
93
+ severity: "critical",
94
+ file: servers[0],
95
+ message: `Tool name conflict: "${toolName}" defined in ${servers.length} servers (${servers.join(", ")}) — potential tool shadowing attack`,
96
+ });
97
+ }
98
+ }
99
+ return findings;
100
+ },
101
+ };
102
+ function extractMcpTools(obj, filePath, result, serverName) {
103
+ if (!obj || typeof obj !== "object")
104
+ return;
105
+ const record = obj;
106
+ // Look for MCP server definitions with tools
107
+ if (record.tools && Array.isArray(record.tools)) {
108
+ const tools = record.tools
109
+ .map((t) => String(t.name || t.id || ""))
110
+ .filter(Boolean);
111
+ if (tools.length > 0) {
112
+ const name = serverName || filePath;
113
+ result.set(name, { tools, file: filePath });
114
+ }
115
+ }
116
+ // Recurse into mcpServers config
117
+ if (record.mcpServers && typeof record.mcpServers === "object") {
118
+ for (const [name, config] of Object.entries(record.mcpServers)) {
119
+ extractMcpTools(config, filePath, result, name);
120
+ }
121
+ }
122
+ // Recurse into servers
123
+ if (record.servers && typeof record.servers === "object") {
124
+ for (const [name, config] of Object.entries(record.servers)) {
125
+ extractMcpTools(config, filePath, result, name);
126
+ }
127
+ }
128
+ }
129
+ //# sourceMappingURL=tool-shadowing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-shadowing.js","sourceRoot":"","sources":["../../src/rules/tool-shadowing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const toxicFlow: Rule;
@@ -0,0 +1,160 @@
1
+ // Patterns for classifying tool capabilities
2
+ const UNTRUSTED_INDICATORS = [
3
+ /(?:fetch|browse|scrape|crawl|read)[_\s]*(?:url|web|page|site|link)/i,
4
+ /(?:user[- ]?(?:provided|input|supplied)|external|third[- ]?party)/i,
5
+ /(?:search|query)\s+(?:the\s+)?(?:web|internet|google|bing)/i,
6
+ /(?:download|get)\s+(?:from|content)/i,
7
+ /(?:parse|read)\s+(?:email|message|comment|post|tweet|rss)/i,
8
+ /browse\s+(?:any|arbitrary)\s+(?:web|url|page|site)/i,
9
+ ];
10
+ const PRIVATE_DATA_INDICATORS = [
11
+ /(?:read|access|get|list)\s*(?:file|directory|folder|document)/i,
12
+ /(?:database|db|sql|query|select)\s/i,
13
+ /(?:user\s+data|personal|private|confidential|sensitive)/i,
14
+ /(?:credential|password|secret|token|key)\s/i,
15
+ /(?:history|log|conversation|chat|message)/i,
16
+ /(?:config|setting|preference|profile)/i,
17
+ ];
18
+ const PUBLIC_SINK_INDICATORS = [
19
+ /(?:send|post|publish|share|upload|transmit|forward)\s/i,
20
+ /(?:email|message|notification|webhook|api\s+call)/i,
21
+ /(?:slack|discord|telegram|whatsapp|twitter|github)/i,
22
+ /(?:http|https|request|fetch)\s*\(/i,
23
+ /(?:write|save|export)\s+(?:to\s+)?(?:external|remote|cloud)/i,
24
+ ];
25
+ const DESTRUCTIVE_INDICATORS = [
26
+ /(?:delete|remove|drop|truncate|destroy|purge|wipe|erase)/i,
27
+ /(?:overwrite|replace|modify|update|alter)\s+(?:file|data|record|config)/i,
28
+ /(?:execute|run|exec|spawn|shell)\s+(?:command|script|process)/i,
29
+ /(?:format|reset|factory\s+reset|reinstall)/i,
30
+ /(?:revoke|disable|block|ban|suspend)/i,
31
+ ];
32
+ function classifyTool(name, description, serverName) {
33
+ const cap = {
34
+ name,
35
+ server: serverName,
36
+ untrustedContent: false,
37
+ privateData: false,
38
+ publicSink: false,
39
+ destructive: false,
40
+ reasons: [],
41
+ };
42
+ const text = `${name} ${description}`.toLowerCase();
43
+ for (const re of UNTRUSTED_INDICATORS) {
44
+ if (re.test(text)) {
45
+ cap.untrustedContent = true;
46
+ cap.reasons.push("untrusted-content");
47
+ break;
48
+ }
49
+ }
50
+ for (const re of PRIVATE_DATA_INDICATORS) {
51
+ if (re.test(text)) {
52
+ cap.privateData = true;
53
+ cap.reasons.push("private-data");
54
+ break;
55
+ }
56
+ }
57
+ for (const re of PUBLIC_SINK_INDICATORS) {
58
+ if (re.test(text)) {
59
+ cap.publicSink = true;
60
+ cap.reasons.push("public-sink");
61
+ break;
62
+ }
63
+ }
64
+ for (const re of DESTRUCTIVE_INDICATORS) {
65
+ if (re.test(text)) {
66
+ cap.destructive = true;
67
+ cap.reasons.push("destructive");
68
+ break;
69
+ }
70
+ }
71
+ return cap;
72
+ }
73
+ /** Extract tools from MCP config JSON */
74
+ function extractTools(config, filePath) {
75
+ const tools = [];
76
+ const servers = (config.mcpServers || config.servers || {});
77
+ for (const [serverName, serverConfig] of Object.entries(servers)) {
78
+ if (!serverConfig || typeof serverConfig !== "object")
79
+ continue;
80
+ const sc = serverConfig;
81
+ // Tools array
82
+ if (Array.isArray(sc.tools)) {
83
+ for (const tool of sc.tools) {
84
+ const name = String(tool.name || tool.id || "unknown");
85
+ const desc = String(tool.description || "");
86
+ tools.push(classifyTool(name, desc, serverName));
87
+ }
88
+ }
89
+ // If no tools array, classify the server itself by name/description
90
+ if (!sc.tools) {
91
+ const desc = String(sc.description || sc.command || "");
92
+ tools.push(classifyTool(serverName, desc, serverName));
93
+ }
94
+ }
95
+ return tools;
96
+ }
97
+ export const toxicFlow = {
98
+ id: "toxic-flow",
99
+ name: "Toxic Flow Detection",
100
+ description: "Detects dangerous tool combinations that enable cross-tool data exfiltration or destructive attacks",
101
+ run(files) {
102
+ const findings = [];
103
+ const allTools = [];
104
+ // Collect tools from all JSON config files
105
+ for (const file of files) {
106
+ if (file.ext !== ".json")
107
+ continue;
108
+ try {
109
+ const config = JSON.parse(file.content);
110
+ const tools = extractTools(config, file.relativePath);
111
+ allTools.push(...tools);
112
+ }
113
+ catch {
114
+ // Not valid JSON
115
+ }
116
+ }
117
+ if (allTools.length === 0)
118
+ return findings;
119
+ // TF001: Data Leak — untrusted content + private data + public sink
120
+ const hasUntrusted = allTools.filter(t => t.untrustedContent);
121
+ const hasPrivate = allTools.filter(t => t.privateData);
122
+ const hasSink = allTools.filter(t => t.publicSink);
123
+ if (hasUntrusted.length > 0 && hasPrivate.length > 0 && hasSink.length > 0) {
124
+ findings.push({
125
+ rule: "toxic-flow",
126
+ severity: "critical",
127
+ file: "MCP configuration",
128
+ message: `TF001 Data Leak: Tool combination enables data exfiltration — ` +
129
+ `untrusted input (${hasUntrusted.map(t => t.name).join(", ")}) → ` +
130
+ `private data (${hasPrivate.map(t => t.name).join(", ")}) → ` +
131
+ `public sink (${hasSink.map(t => t.name).join(", ")})`,
132
+ });
133
+ }
134
+ // TF002: Destructive — untrusted content + destructive action
135
+ const hasDestructive = allTools.filter(t => t.destructive);
136
+ if (hasUntrusted.length > 0 && hasDestructive.length > 0) {
137
+ findings.push({
138
+ rule: "toxic-flow",
139
+ severity: "critical",
140
+ file: "MCP configuration",
141
+ message: `TF002 Destructive Flow: Untrusted content could trigger destructive actions — ` +
142
+ `untrusted (${hasUntrusted.map(t => t.name).join(", ")}) → ` +
143
+ `destructive (${hasDestructive.map(t => t.name).join(", ")})`,
144
+ });
145
+ }
146
+ // Single tool that combines multiple risky capabilities
147
+ for (const tool of allTools) {
148
+ if (tool.untrustedContent && tool.privateData && tool.publicSink) {
149
+ findings.push({
150
+ rule: "toxic-flow",
151
+ severity: "critical",
152
+ file: "MCP configuration",
153
+ message: `Single tool "${tool.name}" (${tool.server}) combines untrusted content, private data access, and public sink — high exfiltration risk`,
154
+ });
155
+ }
156
+ }
157
+ return findings;
158
+ },
159
+ };
160
+ //# sourceMappingURL=toxic-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toxic-flow.js","sourceRoot":"","sources":["../../src/rules/toxic-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const typosquattingRule: Rule;