@mcp-shark/mcp-shark 1.5.13 → 1.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.
Files changed (100) hide show
  1. package/README.md +396 -59
  2. package/bin/mcp-shark.js +146 -52
  3. package/core/cli/AutoFixEngine.js +93 -0
  4. package/core/cli/ConfigScanner.js +193 -0
  5. package/core/cli/DataLoader.js +200 -0
  6. package/core/cli/DeclarativeRuleEngine.js +363 -0
  7. package/core/cli/DoctorCommand.js +218 -0
  8. package/core/cli/FixHandlers.js +222 -0
  9. package/core/cli/HtmlReportGenerator.js +203 -0
  10. package/core/cli/IdeConfigPaths.js +175 -0
  11. package/core/cli/ListCommand.js +224 -0
  12. package/core/cli/LockCommand.js +164 -0
  13. package/core/cli/LockDiffEngine.js +152 -0
  14. package/core/cli/RuleRegistryConfig.js +131 -0
  15. package/core/cli/ScanCommand.js +244 -0
  16. package/core/cli/ScanService.js +200 -0
  17. package/core/cli/SecretDetector.js +92 -0
  18. package/core/cli/SharkScoreCalculator.js +109 -0
  19. package/core/cli/ToolClassifications.js +51 -0
  20. package/core/cli/ToxicFlowAnalyzer.js +212 -0
  21. package/core/cli/UpdateCommand.js +188 -0
  22. package/core/cli/WalkthroughGenerator.js +195 -0
  23. package/core/cli/WatchCommand.js +129 -0
  24. package/core/cli/YamlRuleEngine.js +197 -0
  25. package/core/cli/data/rule-packs/agentic-security-2026.json +180 -0
  26. package/core/cli/data/rule-packs/general-security.json +173 -0
  27. package/core/cli/data/rule-packs/owasp-mcp-2026.json +244 -0
  28. package/core/cli/data/rule-packs/toxic-flow-heuristics.json +21 -0
  29. package/core/cli/data/rule-sources.json +5 -0
  30. package/core/cli/data/secret-patterns.json +18 -0
  31. package/core/cli/data/tool-classifications.json +111 -0
  32. package/core/cli/data/toxic-flow-rules.json +47 -0
  33. package/core/cli/index.js +23 -0
  34. package/core/cli/output/Banner.js +52 -0
  35. package/core/cli/output/Formatter.js +183 -0
  36. package/core/cli/output/JsonFormatter.js +106 -0
  37. package/core/cli/output/index.js +16 -0
  38. package/core/cli/secureRegistryFetch.js +157 -0
  39. package/core/cli/symbols.js +16 -0
  40. package/core/container/DependencyContainer.js +4 -1
  41. package/core/repositories/PacketRepository.js +16 -0
  42. package/core/services/AuditService.js +2 -0
  43. package/core/services/security/StaticRulesService.js +69 -13
  44. package/core/services/security/TrafficAnalysisService.js +19 -1
  45. package/core/services/security/TrafficToxicFlowService.js +154 -0
  46. package/core/services/security/index.js +2 -1
  47. package/core/services/security/rules/index.js +25 -59
  48. package/core/services/security/rules/scans/configPermissions.js +91 -0
  49. package/core/services/security/rules/scans/duplicateToolNames.js +85 -0
  50. package/core/services/security/rules/scans/insecureTransport.js +148 -0
  51. package/core/services/security/rules/scans/missingContainment.js +123 -0
  52. package/core/services/security/rules/scans/shellEnvInjection.js +101 -0
  53. package/core/services/security/rules/scans/unsafeDefaults.js +99 -0
  54. package/core/services/security/toolsListFromTrafficParser.js +70 -0
  55. package/core/tui/App.js +144 -0
  56. package/core/tui/FindingsPanel.js +115 -0
  57. package/core/tui/FixPanel.js +132 -0
  58. package/core/tui/Header.js +51 -0
  59. package/core/tui/HelpBar.js +42 -0
  60. package/core/tui/ServersPanel.js +109 -0
  61. package/core/tui/ToxicFlowsPanel.js +100 -0
  62. package/core/tui/h.js +8 -0
  63. package/core/tui/index.js +11 -0
  64. package/core/tui/render.js +22 -0
  65. package/package.json +24 -16
  66. package/ui/dist/assets/{index-CiCSDYf-.js → index-Buah4fNS.js} +32 -32
  67. package/ui/dist/index.html +1 -1
  68. package/ui/server/controllers/RequestController.js +9 -1
  69. package/ui/server/controllers/SecurityFindingsController.js +46 -1
  70. package/ui/server/routes/requests.js +8 -1
  71. package/ui/server/routes/security.js +5 -1
  72. package/ui/server/setup.js +2 -0
  73. package/ui/server/swagger/paths/components.js +55 -0
  74. package/ui/server/swagger/paths/securityTrafficFlows.js +59 -0
  75. package/ui/server/swagger/paths.js +2 -0
  76. package/ui/server/swagger/swagger.js +5 -1
  77. package/ui/src/Security.jsx +10 -0
  78. package/ui/src/components/Security/ScannerContent.jsx +30 -1
  79. package/ui/src/components/Security/TrafficToxicFlowsPanel.jsx +253 -0
  80. package/ui/src/components/Security/securityApi.js +15 -0
  81. package/ui/src/components/Security/useSecurity.js +60 -3
  82. package/core/services/security/rules/scans/agentic01GoalHijack.js +0 -130
  83. package/core/services/security/rules/scans/agentic02ToolMisuse.js +0 -129
  84. package/core/services/security/rules/scans/agentic03IdentityAbuse.js +0 -130
  85. package/core/services/security/rules/scans/agentic04SupplyChain.js +0 -130
  86. package/core/services/security/rules/scans/agentic06MemoryPoisoning.js +0 -130
  87. package/core/services/security/rules/scans/agentic07InsecureCommunication.js +0 -135
  88. package/core/services/security/rules/scans/agentic08CascadingFailures.js +0 -135
  89. package/core/services/security/rules/scans/agentic09TrustExploitation.js +0 -135
  90. package/core/services/security/rules/scans/agentic10RogueAgent.js +0 -130
  91. package/core/services/security/rules/scans/hardcodedSecrets.js +0 -130
  92. package/core/services/security/rules/scans/mcp01TokenMismanagement.js +0 -127
  93. package/core/services/security/rules/scans/mcp02ScopeCreep.js +0 -130
  94. package/core/services/security/rules/scans/mcp03ToolPoisoning.js +0 -132
  95. package/core/services/security/rules/scans/mcp04SupplyChain.js +0 -131
  96. package/core/services/security/rules/scans/mcp06PromptInjection.js +0 -200
  97. package/core/services/security/rules/scans/mcp07InsufficientAuth.js +0 -130
  98. package/core/services/security/rules/scans/mcp08LackAudit.js +0 -129
  99. package/core/services/security/rules/scans/mcp09ShadowServers.js +0 -129
  100. package/core/services/security/rules/scans/mcp10ContextInjection.js +0 -130
@@ -1,130 +0,0 @@
1
- import { SECRET_PATTERNS } from '../constants.js';
2
- import { convertPacketFinding, createRuleAdapter } from '../utils/adapter.js';
3
- import { packetToText, promptToText, resourceToText, toolToText } from '../utils/text.js';
4
-
5
- const RULE_ID = 'hardcoded-secrets';
6
- const OWASP_ID = 'SECRET';
7
- const RECOMMENDATION =
8
- 'Move secrets or API tokens to secure storage; never embed them directly in tool or resource metadata.';
9
-
10
- function redacted(value) {
11
- if (!value) {
12
- return '';
13
- }
14
- if (value.length <= 8) {
15
- return '***';
16
- }
17
- return `${value.slice(0, 4)}...${value.slice(-4)}`;
18
- }
19
-
20
- function scanText(text) {
21
- if (!text) {
22
- return null;
23
- }
24
- for (const pattern of SECRET_PATTERNS) {
25
- pattern.regex.lastIndex = 0;
26
- const match = text.match(pattern.regex);
27
- if (match) {
28
- return {
29
- type: pattern.type,
30
- sample: redacted(match[0]),
31
- };
32
- }
33
- }
34
- return null;
35
- }
36
-
37
- function buildReason(entity, secretInfo) {
38
- return `Potential ${secretInfo.type} secret leaked inside ${entity} (sample ${secretInfo.sample}).`;
39
- }
40
-
41
- export function scanHardcodedSecrets(mcpData = {}) {
42
- const results = {
43
- toolFindings: [],
44
- resourceFindings: [],
45
- promptFindings: [],
46
- notablePatterns: [],
47
- recommendations: [RECOMMENDATION],
48
- };
49
-
50
- for (const tool of mcpData.tools || []) {
51
- const info = scanText(toolToText(tool));
52
- if (info) {
53
- results.toolFindings.push({
54
- issueType: 'Hardcoded Secret',
55
- name: tool?.name || 'tool',
56
- severity: 'high',
57
- reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, info)],
58
- tags: ['secret'],
59
- safeUseNotes: 'Remove the leaked credential and rotate it immediately.',
60
- });
61
- }
62
- }
63
-
64
- for (const resource of mcpData.resources || []) {
65
- const info = scanText(resourceToText(resource));
66
- if (info) {
67
- results.resourceFindings.push({
68
- issueType: 'Hardcoded Secret',
69
- uri: resource?.uri || resource?.name || 'resource',
70
- severity: 'high',
71
- reasons: [buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, info)],
72
- tags: ['secret'],
73
- });
74
- }
75
- }
76
-
77
- for (const prompt of mcpData.prompts || []) {
78
- const info = scanText(promptToText(prompt));
79
- if (info) {
80
- results.promptFindings.push({
81
- issueType: 'Hardcoded Secret',
82
- name: prompt?.name || 'prompt',
83
- severity: 'medium',
84
- reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, info)],
85
- tags: ['secret'],
86
- });
87
- }
88
- }
89
-
90
- return results;
91
- }
92
-
93
- const adapter = createRuleAdapter(scanHardcodedSecrets, RULE_ID, OWASP_ID, RECOMMENDATION);
94
-
95
- export const analyzeTool = adapter.analyzeTool;
96
- export const analyzePrompt = adapter.analyzePrompt;
97
- export const analyzeResource = adapter.analyzeResource;
98
-
99
- export function analyzePacket(packet) {
100
- const text = packetToText(packet);
101
- const info = scanText(text);
102
- if (!info) {
103
- return [];
104
- }
105
- return [
106
- convertPacketFinding(
107
- {
108
- issueType: 'Hardcoded Secret',
109
- severity: 'high',
110
- title: 'Secret Detected in Traffic',
111
- description: `Potential ${info.type} secret in packet (sample ${info.sample}).`,
112
- evidence: info.sample,
113
- },
114
- RULE_ID,
115
- OWASP_ID,
116
- RECOMMENDATION,
117
- packet
118
- ),
119
- ];
120
- }
121
-
122
- export const ruleMetadata = {
123
- id: RULE_ID,
124
- name: 'Hardcoded Secrets Detection',
125
- owasp_id: OWASP_ID,
126
- severity: 'high',
127
- description: 'Detects hardcoded secrets and API tokens in metadata.',
128
- source: 'static',
129
- type: 'general-security',
130
- };
@@ -1,127 +0,0 @@
1
- import { SECRET_PATTERNS } from '../constants.js';
2
- import { convertPacketFinding, createRuleAdapter } from '../utils/adapter.js';
3
- import { packetToText, promptToText, resourceToText, toolToText } from '../utils/text.js';
4
-
5
- const RULE_ID = 'mcp01-token-mismanagement';
6
- const OWASP_ID = 'MCP01';
7
- const RECOMMENDATION =
8
- 'Move all tokens and secrets to secure storage (environment variables, secret managers). Never embed credentials in MCP server configurations.';
9
-
10
- function scanText(text) {
11
- if (!text) {
12
- return null;
13
- }
14
- const matches = [];
15
- for (const pattern of SECRET_PATTERNS) {
16
- pattern.regex.lastIndex = 0;
17
- const match = text.match(pattern.regex);
18
- if (match) {
19
- matches.push({ type: pattern.type, sample: match[0] });
20
- }
21
- }
22
- return matches.length > 0 ? matches : null;
23
- }
24
-
25
- function buildReason(entity, matches) {
26
- const types = matches.map((m) => m.type).join(', ');
27
- return `Token or secret exposure detected in ${entity}: ${types}`;
28
- }
29
-
30
- export function scanMCP01TokenMismanagement(mcpData = {}) {
31
- const results = {
32
- toolFindings: [],
33
- resourceFindings: [],
34
- promptFindings: [],
35
- notablePatterns: [],
36
- recommendations: [RECOMMENDATION],
37
- };
38
-
39
- for (const tool of mcpData.tools || []) {
40
- const matches = scanText(toolToText(tool));
41
- if (matches) {
42
- results.toolFindings.push({
43
- issueType: 'Token Mismanagement',
44
- name: tool?.name || 'tool',
45
- severity: 'high',
46
- reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, matches)],
47
- tags: ['token-mismanagement', 'secret-exposure'],
48
- mcpCategory: OWASP_ID,
49
- safeUseNotes:
50
- 'Remove all tokens and secrets from tool metadata. Use secure storage mechanisms.',
51
- });
52
- }
53
- }
54
-
55
- for (const resource of mcpData.resources || []) {
56
- const matches = scanText(resourceToText(resource));
57
- if (matches) {
58
- results.resourceFindings.push({
59
- issueType: 'Token Mismanagement',
60
- uri: resource?.uri || resource?.name || 'resource',
61
- severity: 'high',
62
- reasons: [
63
- buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, matches),
64
- ],
65
- tags: ['token-mismanagement', 'secret-exposure'],
66
- mcpCategory: OWASP_ID,
67
- });
68
- }
69
- }
70
-
71
- for (const prompt of mcpData.prompts || []) {
72
- const matches = scanText(promptToText(prompt));
73
- if (matches) {
74
- results.promptFindings.push({
75
- issueType: 'Token Mismanagement',
76
- name: prompt?.name || 'prompt',
77
- severity: 'medium',
78
- reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, matches)],
79
- tags: ['token-mismanagement', 'secret-exposure'],
80
- mcpCategory: OWASP_ID,
81
- });
82
- }
83
- }
84
-
85
- return results;
86
- }
87
-
88
- const adapter = createRuleAdapter(scanMCP01TokenMismanagement, RULE_ID, OWASP_ID, RECOMMENDATION);
89
-
90
- export const analyzeTool = adapter.analyzeTool;
91
- export const analyzePrompt = adapter.analyzePrompt;
92
- export const analyzeResource = adapter.analyzeResource;
93
-
94
- export function analyzePacket(packet) {
95
- const text = packetToText(packet);
96
- const matches = scanText(text);
97
- if (!matches) {
98
- return [];
99
- }
100
- const types = matches.map((m) => m.type).join(', ');
101
- return [
102
- convertPacketFinding(
103
- {
104
- issueType: 'Token Mismanagement',
105
- severity: 'high',
106
- title: 'Token/Secret Detected in Traffic',
107
- description: `Potential secret exposure in packet: ${types}`,
108
- evidence: matches[0]?.sample?.substring(0, 50) || '',
109
- },
110
- RULE_ID,
111
- OWASP_ID,
112
- RECOMMENDATION,
113
- packet
114
- ),
115
- ];
116
- }
117
-
118
- export const ruleMetadata = {
119
- id: RULE_ID,
120
- name: 'Token Mismanagement & Secret Exposure',
121
- owasp_id: OWASP_ID,
122
- severity: 'high',
123
- description:
124
- 'Detects hard-coded credentials, API keys, and secrets that could be exposed through prompt injection or compromised context.',
125
- source: 'static',
126
- type: 'owasp-mcp',
127
- };
@@ -1,130 +0,0 @@
1
- import { convertPacketFinding, createRuleAdapter } from '../utils/adapter.js';
2
- import { packetToText, promptToText, resourceToText, toolToText } from '../utils/text.js';
3
-
4
- const RULE_ID = 'mcp02-scope-creep';
5
- const OWASP_ID = 'MCP02';
6
- const RECOMMENDATION =
7
- 'Implement strict scope boundaries for tools. Monitor for unauthorized scope expansion and enforce least privilege principles.';
8
-
9
- const SCOPE_CREEP_PATTERNS = [
10
- /(?:expand|extend|increase|broaden|widen)\s+(?:scope|permission|access|capability|authority)/i,
11
- /(?:additional|extra|more|further)\s+(?:privilege|permission|access|right)/i,
12
- /(?:escalate|elevate|raise|upgrade)\s+(?:privilege|permission|access|level)/i,
13
- /(?:unrestricted|unlimited|full|complete|total)\s+(?:access|permission|privilege|control)/i,
14
- /(?:bypass|override|circumvent)\s+(?:restriction|limit|constraint|control)/i,
15
- ];
16
-
17
- function scanText(text) {
18
- if (!text) {
19
- return null;
20
- }
21
- const matches = [];
22
- for (const pattern of SCOPE_CREEP_PATTERNS) {
23
- const match = text.match(pattern);
24
- if (match) {
25
- matches.push(match[0]);
26
- }
27
- }
28
- return matches.length > 0 ? matches : null;
29
- }
30
-
31
- function buildReason(entity, matches) {
32
- return `Potential scope creep detected in ${entity}: ${matches.join(', ')}`;
33
- }
34
-
35
- export function scanMCP02ScopeCreep(mcpData = {}) {
36
- const results = {
37
- toolFindings: [],
38
- resourceFindings: [],
39
- promptFindings: [],
40
- notablePatterns: [],
41
- recommendations: [RECOMMENDATION],
42
- };
43
-
44
- for (const tool of mcpData.tools || []) {
45
- const matches = scanText(toolToText(tool));
46
- if (matches) {
47
- results.toolFindings.push({
48
- issueType: 'Scope Creep',
49
- name: tool?.name || 'tool',
50
- severity: 'medium',
51
- reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, matches)],
52
- tags: ['scope-creep', 'privilege-escalation'],
53
- mcpCategory: OWASP_ID,
54
- safeUseNotes:
55
- 'Review tool scope boundaries. Ensure tools cannot expand their permissions beyond intended limits.',
56
- });
57
- }
58
- }
59
-
60
- for (const resource of mcpData.resources || []) {
61
- const matches = scanText(resourceToText(resource));
62
- if (matches) {
63
- results.resourceFindings.push({
64
- issueType: 'Scope Creep',
65
- uri: resource?.uri || resource?.name || 'resource',
66
- severity: 'medium',
67
- reasons: [
68
- buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, matches),
69
- ],
70
- tags: ['scope-creep', 'privilege-escalation'],
71
- mcpCategory: OWASP_ID,
72
- });
73
- }
74
- }
75
-
76
- for (const prompt of mcpData.prompts || []) {
77
- const matches = scanText(promptToText(prompt));
78
- if (matches) {
79
- results.promptFindings.push({
80
- issueType: 'Scope Creep',
81
- name: prompt?.name || 'prompt',
82
- severity: 'low',
83
- reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, matches)],
84
- tags: ['scope-creep', 'privilege-escalation'],
85
- mcpCategory: OWASP_ID,
86
- });
87
- }
88
- }
89
-
90
- return results;
91
- }
92
-
93
- const adapter = createRuleAdapter(scanMCP02ScopeCreep, RULE_ID, OWASP_ID, RECOMMENDATION);
94
-
95
- export const analyzeTool = adapter.analyzeTool;
96
- export const analyzePrompt = adapter.analyzePrompt;
97
- export const analyzeResource = adapter.analyzeResource;
98
-
99
- export function analyzePacket(packet) {
100
- const text = packetToText(packet);
101
- const matches = scanText(text);
102
- if (!matches) {
103
- return [];
104
- }
105
- return [
106
- convertPacketFinding(
107
- {
108
- issueType: 'Scope Creep',
109
- severity: 'medium',
110
- title: 'Scope Creep Pattern in Traffic',
111
- description: `Potential scope creep in packet: ${matches.join(', ')}`,
112
- evidence: matches[0]?.substring(0, 50) || '',
113
- },
114
- RULE_ID,
115
- OWASP_ID,
116
- RECOMMENDATION,
117
- packet
118
- ),
119
- ];
120
- }
121
-
122
- export const ruleMetadata = {
123
- id: RULE_ID,
124
- name: 'Scope Creep Detection',
125
- owasp_id: OWASP_ID,
126
- severity: 'medium',
127
- description: 'Detects potential privilege escalation via scope creep in tool definitions.',
128
- source: 'static',
129
- type: 'owasp-mcp',
130
- };
@@ -1,132 +0,0 @@
1
- import { convertPacketFinding, createRuleAdapter } from '../utils/adapter.js';
2
- import { packetToText, promptToText, resourceToText, toolToText } from '../utils/text.js';
3
-
4
- const RULE_ID = 'mcp03-tool-poisoning';
5
- const OWASP_ID = 'MCP03';
6
- const RECOMMENDATION =
7
- 'Verify tool authenticity and integrity. Implement tool signing and verification mechanisms. Monitor for suspicious tool behavior.';
8
-
9
- const TOOL_POISONING_PATTERNS = [
10
- /(?:malicious|harmful|dangerous|exploit|attack|backdoor|trojan|virus)/i,
11
- /(?:unauthorized|illegitimate|fake|spoofed|forged|tampered)/i,
12
- /(?:steal|exfiltrate|leak|extract)\s+(?:data|information|credentials|secrets)/i,
13
- /(?:delete|remove|destroy|wipe|erase)\s+(?:file|data|information)/i,
14
- /(?:override|bypass|disable)\s+(?:security|protection|safeguard|control)/i,
15
- /(?:hidden|concealed|obfuscated|encoded)\s+(?:functionality|behavior|action)/i,
16
- ];
17
-
18
- function scanText(text) {
19
- if (!text) {
20
- return null;
21
- }
22
- const matches = [];
23
- for (const pattern of TOOL_POISONING_PATTERNS) {
24
- const match = text.match(pattern);
25
- if (match) {
26
- matches.push(match[0]);
27
- }
28
- }
29
- return matches.length > 0 ? matches : null;
30
- }
31
-
32
- function buildReason(entity, matches) {
33
- return `Potential tool poisoning indicators in ${entity}: ${matches.join(', ')}`;
34
- }
35
-
36
- export function scanMCP03ToolPoisoning(mcpData = {}) {
37
- const results = {
38
- toolFindings: [],
39
- resourceFindings: [],
40
- promptFindings: [],
41
- notablePatterns: [],
42
- recommendations: [RECOMMENDATION],
43
- };
44
-
45
- for (const tool of mcpData.tools || []) {
46
- const matches = scanText(toolToText(tool));
47
- if (matches) {
48
- results.toolFindings.push({
49
- issueType: 'Tool Poisoning',
50
- name: tool?.name || 'tool',
51
- severity: 'high',
52
- reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, matches)],
53
- tags: ['tool-poisoning', 'malicious'],
54
- mcpCategory: OWASP_ID,
55
- is_potentially_poisoned: true,
56
- safeUseNotes:
57
- 'Review tool description and behavior carefully. Verify tool source and integrity before use.',
58
- });
59
- }
60
- }
61
-
62
- for (const resource of mcpData.resources || []) {
63
- const matches = scanText(resourceToText(resource));
64
- if (matches) {
65
- results.resourceFindings.push({
66
- issueType: 'Tool Poisoning',
67
- uri: resource?.uri || resource?.name || 'resource',
68
- severity: 'high',
69
- reasons: [
70
- buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, matches),
71
- ],
72
- tags: ['tool-poisoning', 'malicious'],
73
- mcpCategory: OWASP_ID,
74
- });
75
- }
76
- }
77
-
78
- for (const prompt of mcpData.prompts || []) {
79
- const matches = scanText(promptToText(prompt));
80
- if (matches) {
81
- results.promptFindings.push({
82
- issueType: 'Tool Poisoning',
83
- name: prompt?.name || 'prompt',
84
- severity: 'medium',
85
- reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, matches)],
86
- tags: ['tool-poisoning', 'malicious'],
87
- mcpCategory: OWASP_ID,
88
- });
89
- }
90
- }
91
-
92
- return results;
93
- }
94
-
95
- const adapter = createRuleAdapter(scanMCP03ToolPoisoning, RULE_ID, OWASP_ID, RECOMMENDATION);
96
-
97
- export const analyzeTool = adapter.analyzeTool;
98
- export const analyzePrompt = adapter.analyzePrompt;
99
- export const analyzeResource = adapter.analyzeResource;
100
-
101
- export function analyzePacket(packet) {
102
- const text = packetToText(packet);
103
- const matches = scanText(text);
104
- if (!matches) {
105
- return [];
106
- }
107
- return [
108
- convertPacketFinding(
109
- {
110
- issueType: 'Tool Poisoning',
111
- severity: 'high',
112
- title: 'Tool Poisoning Pattern in Traffic',
113
- description: `Potential malicious pattern in packet: ${matches.join(', ')}`,
114
- evidence: matches[0]?.substring(0, 50) || '',
115
- },
116
- RULE_ID,
117
- OWASP_ID,
118
- RECOMMENDATION,
119
- packet
120
- ),
121
- ];
122
- }
123
-
124
- export const ruleMetadata = {
125
- id: RULE_ID,
126
- name: 'Tool Poisoning Detection',
127
- owasp_id: OWASP_ID,
128
- severity: 'high',
129
- description: 'Detects malicious patterns that could indicate tool poisoning attacks.',
130
- source: 'static',
131
- type: 'owasp-mcp',
132
- };
@@ -1,131 +0,0 @@
1
- import { convertPacketFinding, createRuleAdapter } from '../utils/adapter.js';
2
- import { packetToText, promptToText, resourceToText, toolToText } from '../utils/text.js';
3
-
4
- const RULE_ID = 'mcp04-supply-chain';
5
- const OWASP_ID = 'MCP04';
6
- const RECOMMENDATION =
7
- 'Pin all dependencies to specific versions. Verify package integrity and signatures. Use trusted repositories and registries.';
8
-
9
- const SUPPLY_CHAIN_PATTERNS = [
10
- /(?:unsigned|unverified|untrusted|unauthenticated)\s+(?:package|dependency|module|library)/i,
11
- /(?:tampered|modified|altered|compromised)\s+(?:package|dependency|module|library)/i,
12
- /(?:dependency|package|module)\s+(?:confusion|substitution|hijacking)/i,
13
- /(?:typosquatting|brandjacking|namespace)\s+(?:package|dependency|module)/i,
14
- /(?:unpinned|floating|wildcard)\s+(?:version|dependency|package)/i,
15
- /(?:from|source)\s+(?:unknown|unverified|suspicious|untrusted)\s+(?:source|repository|registry)/i,
16
- ];
17
-
18
- function scanText(text) {
19
- if (!text) {
20
- return null;
21
- }
22
- const matches = [];
23
- for (const pattern of SUPPLY_CHAIN_PATTERNS) {
24
- const match = text.match(pattern);
25
- if (match) {
26
- matches.push(match[0]);
27
- }
28
- }
29
- return matches.length > 0 ? matches : null;
30
- }
31
-
32
- function buildReason(entity, matches) {
33
- return `Potential supply chain vulnerability in ${entity}: ${matches.join(', ')}`;
34
- }
35
-
36
- export function scanMCP04SupplyChain(mcpData = {}) {
37
- const results = {
38
- toolFindings: [],
39
- resourceFindings: [],
40
- promptFindings: [],
41
- notablePatterns: [],
42
- recommendations: [RECOMMENDATION],
43
- };
44
-
45
- for (const tool of mcpData.tools || []) {
46
- const matches = scanText(toolToText(tool));
47
- if (matches) {
48
- results.toolFindings.push({
49
- issueType: 'Supply Chain',
50
- name: tool?.name || 'tool',
51
- severity: 'high',
52
- reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, matches)],
53
- tags: ['supply-chain', 'dependency-tampering'],
54
- mcpCategory: OWASP_ID,
55
- safeUseNotes:
56
- 'Verify all dependencies and packages. Use signed packages from trusted sources.',
57
- });
58
- }
59
- }
60
-
61
- for (const resource of mcpData.resources || []) {
62
- const matches = scanText(resourceToText(resource));
63
- if (matches) {
64
- results.resourceFindings.push({
65
- issueType: 'Supply Chain',
66
- uri: resource?.uri || resource?.name || 'resource',
67
- severity: 'high',
68
- reasons: [
69
- buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, matches),
70
- ],
71
- tags: ['supply-chain', 'dependency-tampering'],
72
- mcpCategory: OWASP_ID,
73
- });
74
- }
75
- }
76
-
77
- for (const prompt of mcpData.prompts || []) {
78
- const matches = scanText(promptToText(prompt));
79
- if (matches) {
80
- results.promptFindings.push({
81
- issueType: 'Supply Chain',
82
- name: prompt?.name || 'prompt',
83
- severity: 'medium',
84
- reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, matches)],
85
- tags: ['supply-chain', 'dependency-tampering'],
86
- mcpCategory: OWASP_ID,
87
- });
88
- }
89
- }
90
-
91
- return results;
92
- }
93
-
94
- const adapter = createRuleAdapter(scanMCP04SupplyChain, RULE_ID, OWASP_ID, RECOMMENDATION);
95
-
96
- export const analyzeTool = adapter.analyzeTool;
97
- export const analyzePrompt = adapter.analyzePrompt;
98
- export const analyzeResource = adapter.analyzeResource;
99
-
100
- export function analyzePacket(packet) {
101
- const text = packetToText(packet);
102
- const matches = scanText(text);
103
- if (!matches) {
104
- return [];
105
- }
106
- return [
107
- convertPacketFinding(
108
- {
109
- issueType: 'Supply Chain',
110
- severity: 'high',
111
- title: 'Supply Chain Pattern in Traffic',
112
- description: `Potential supply chain vulnerability in packet: ${matches.join(', ')}`,
113
- evidence: matches[0]?.substring(0, 50) || '',
114
- },
115
- RULE_ID,
116
- OWASP_ID,
117
- RECOMMENDATION,
118
- packet
119
- ),
120
- ];
121
- }
122
-
123
- export const ruleMetadata = {
124
- id: RULE_ID,
125
- name: 'Supply Chain Vulnerability Detection',
126
- owasp_id: OWASP_ID,
127
- severity: 'high',
128
- description: 'Detects potential supply chain vulnerabilities in MCP configurations.',
129
- source: 'static',
130
- type: 'owasp-mcp',
131
- };