@mcp-shark/mcp-shark 1.5.13 → 1.7.2
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 +482 -56
- package/bin/mcp-shark.js +146 -52
- package/core/cli/AutoFixEngine.js +93 -0
- package/core/cli/ConfigScanner.js +193 -0
- package/core/cli/DataLoader.js +200 -0
- package/core/cli/DeclarativeRuleEngine.js +363 -0
- package/core/cli/DoctorCommand.js +218 -0
- package/core/cli/FixHandlers.js +222 -0
- package/core/cli/HtmlReportGenerator.js +203 -0
- package/core/cli/IdeConfigPaths.js +175 -0
- package/core/cli/ListCommand.js +255 -0
- package/core/cli/LockCommand.js +164 -0
- package/core/cli/LockDiffEngine.js +152 -0
- package/core/cli/RuleRegistryConfig.js +131 -0
- package/core/cli/ScanCommand.js +244 -0
- package/core/cli/ScanService.js +200 -0
- package/core/cli/SecretDetector.js +92 -0
- package/core/cli/SharkScoreCalculator.js +109 -0
- package/core/cli/ToolClassifications.js +51 -0
- package/core/cli/ToxicFlowAnalyzer.js +212 -0
- package/core/cli/UpdateCommand.js +188 -0
- package/core/cli/WalkthroughGenerator.js +195 -0
- package/core/cli/WatchCommand.js +129 -0
- package/core/cli/YamlRuleEngine.js +197 -0
- package/core/cli/data/rule-packs/aauth-visibility.json +117 -0
- package/core/cli/data/rule-packs/agentic-security-2026.json +180 -0
- package/core/cli/data/rule-packs/general-security.json +173 -0
- package/core/cli/data/rule-packs/owasp-mcp-2026.json +244 -0
- package/core/cli/data/rule-packs/toxic-flow-heuristics.json +21 -0
- package/core/cli/data/rule-sources.json +5 -0
- package/core/cli/data/secret-patterns.json +18 -0
- package/core/cli/data/tool-classifications.json +111 -0
- package/core/cli/data/toxic-flow-rules.json +47 -0
- package/core/cli/index.js +23 -0
- package/core/cli/output/Banner.js +52 -0
- package/core/cli/output/Formatter.js +183 -0
- package/core/cli/output/JsonFormatter.js +106 -0
- package/core/cli/output/index.js +16 -0
- package/core/cli/secureRegistryFetch.js +157 -0
- package/core/cli/symbols.js +16 -0
- package/core/configs/environment.js +3 -1
- package/core/configs/index.js +3 -64
- package/core/container/DependencyContainer.js +4 -1
- package/core/mcp-server/index.js +4 -1
- package/core/mcp-server/server/external/all.js +10 -3
- package/core/mcp-server/server/external/config.js +62 -5
- package/core/models/RequestFilters.js +3 -0
- package/core/repositories/PacketRepository.js +16 -0
- package/core/services/AuditService.js +2 -0
- package/core/services/ConfigService.js +9 -1
- package/core/services/ConfigTransformService.js +34 -2
- package/core/services/RequestService.js +58 -5
- package/core/services/ServerManagementService.js +59 -4
- package/core/services/security/StaticRulesService.js +69 -13
- package/core/services/security/TrafficAnalysisService.js +19 -1
- package/core/services/security/TrafficToxicFlowService.js +154 -0
- package/core/services/security/aauthGraph.js +199 -0
- package/core/services/security/aauthParser.js +274 -0
- package/core/services/security/aauthSelfTest.js +346 -0
- package/core/services/security/index.js +2 -1
- package/core/services/security/rules/index.js +25 -59
- package/core/services/security/rules/scans/configPermissions.js +91 -0
- package/core/services/security/rules/scans/duplicateToolNames.js +85 -0
- package/core/services/security/rules/scans/insecureTransport.js +148 -0
- package/core/services/security/rules/scans/missingContainment.js +123 -0
- package/core/services/security/rules/scans/shellEnvInjection.js +101 -0
- package/core/services/security/rules/scans/unsafeDefaults.js +99 -0
- package/core/services/security/toolsListFromTrafficParser.js +70 -0
- package/core/tui/App.js +144 -0
- package/core/tui/FindingsPanel.js +115 -0
- package/core/tui/FixPanel.js +132 -0
- package/core/tui/Header.js +51 -0
- package/core/tui/HelpBar.js +42 -0
- package/core/tui/ServersPanel.js +109 -0
- package/core/tui/ToxicFlowsPanel.js +100 -0
- package/core/tui/h.js +8 -0
- package/core/tui/index.js +11 -0
- package/core/tui/render.js +22 -0
- package/package.json +24 -16
- package/ui/dist/assets/index-D6zDrtMV.js +81 -0
- package/ui/dist/index.html +1 -1
- package/ui/server/controllers/AauthController.js +279 -0
- package/ui/server/controllers/RequestController.js +12 -1
- package/ui/server/controllers/SecurityFindingsController.js +46 -1
- package/ui/server/routes/aauth.js +18 -0
- package/ui/server/routes/requests.js +8 -1
- package/ui/server/routes/security.js +5 -1
- package/ui/server/setup.js +224 -6
- package/ui/server/swagger/paths/components.js +55 -0
- package/ui/server/swagger/paths/securityTrafficFlows.js +59 -0
- package/ui/server/swagger/paths.js +2 -2
- package/ui/server/swagger/swagger.js +5 -2
- package/ui/server.js +1 -1
- package/ui/src/App.jsx +26 -52
- package/ui/src/PacketFilters.jsx +31 -1
- package/ui/src/PacketList.jsx +2 -2
- package/ui/src/Security.jsx +10 -0
- package/ui/src/TabNavigation.jsx +8 -0
- package/ui/src/components/AAuthBadge.jsx +92 -0
- package/ui/src/components/AauthExplorer/AauthExplorerGraph.jsx +231 -0
- package/ui/src/components/AauthExplorer/AauthExplorerView.jsx +387 -0
- package/ui/src/components/AauthExplorer/NodeDetailPanel.jsx +272 -0
- package/ui/src/components/App/ActionMenu.jsx +4 -31
- package/ui/src/components/App/ApiDocsButton.jsx +0 -1
- package/ui/src/components/App/ShutdownButton.jsx +0 -1
- package/ui/src/components/App/useAppState.js +19 -26
- package/ui/src/components/DetailsTab/AAuthIdentitySection.jsx +119 -0
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +2 -0
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +2 -0
- package/ui/src/components/DetectedPathsList.jsx +1 -5
- package/ui/src/components/FileInput.jsx +0 -1
- package/ui/src/components/PacketFilters/AAuthPostureFilter.jsx +81 -0
- package/ui/src/components/RequestRow/RequestRowMain.jsx +7 -1
- package/ui/src/components/Security/AAuthPosturePanel.jsx +360 -0
- package/ui/src/components/Security/ScannerContent.jsx +33 -1
- package/ui/src/components/Security/TrafficToxicFlowsPanel.jsx +253 -0
- package/ui/src/components/Security/securityApi.js +15 -0
- package/ui/src/components/Security/useSecurity.js +60 -3
- package/ui/src/components/ServerControl.jsx +0 -1
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +0 -11
- package/ui/src/components/TabNavigationIcons.jsx +5 -0
- package/ui/src/components/ViewModeTabs.jsx +0 -1
- package/ui/src/utils/animations.js +26 -9
- package/core/services/security/rules/scans/agentic01GoalHijack.js +0 -130
- package/core/services/security/rules/scans/agentic02ToolMisuse.js +0 -129
- package/core/services/security/rules/scans/agentic03IdentityAbuse.js +0 -130
- package/core/services/security/rules/scans/agentic04SupplyChain.js +0 -130
- package/core/services/security/rules/scans/agentic06MemoryPoisoning.js +0 -130
- package/core/services/security/rules/scans/agentic07InsecureCommunication.js +0 -135
- package/core/services/security/rules/scans/agentic08CascadingFailures.js +0 -135
- package/core/services/security/rules/scans/agentic09TrustExploitation.js +0 -135
- package/core/services/security/rules/scans/agentic10RogueAgent.js +0 -130
- package/core/services/security/rules/scans/hardcodedSecrets.js +0 -130
- package/core/services/security/rules/scans/mcp01TokenMismanagement.js +0 -127
- package/core/services/security/rules/scans/mcp02ScopeCreep.js +0 -130
- package/core/services/security/rules/scans/mcp03ToolPoisoning.js +0 -132
- package/core/services/security/rules/scans/mcp04SupplyChain.js +0 -131
- package/core/services/security/rules/scans/mcp06PromptInjection.js +0 -200
- package/core/services/security/rules/scans/mcp07InsufficientAuth.js +0 -130
- package/core/services/security/rules/scans/mcp08LackAudit.js +0 -129
- package/core/services/security/rules/scans/mcp09ShadowServers.js +0 -129
- package/core/services/security/rules/scans/mcp10ContextInjection.js +0 -130
- package/ui/dist/assets/index-CiCSDYf-.js +0 -97
- package/ui/server/routes/help.js +0 -44
- package/ui/server/swagger/paths/help.js +0 -82
- package/ui/src/HelpGuide/HelpGuideContent.jsx +0 -118
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +0 -59
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +0 -57
- package/ui/src/HelpGuide.jsx +0 -78
- package/ui/src/IntroTour.jsx +0 -154
- package/ui/src/components/App/HelpButton.jsx +0 -90
- package/ui/src/components/TourOverlay.jsx +0 -117
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +0 -120
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +0 -71
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +0 -54
- package/ui/src/components/TourTooltip/useTooltipPosition.js +0 -135
- package/ui/src/components/TourTooltip.jsx +0 -91
- package/ui/src/config/tourSteps.jsx +0 -140
|
@@ -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 = 'asi10-rogue-agent';
|
|
5
|
-
const OWASP_ID = 'ASI10';
|
|
6
|
-
const RECOMMENDATION =
|
|
7
|
-
'Implement agent registration and approval processes. Monitor for unauthorized agent creation. Enforce agent lifecycle management.';
|
|
8
|
-
|
|
9
|
-
const ROGUE_AGENT_PATTERNS = [
|
|
10
|
-
/(?:rogue|unauthorized|unapproved|malicious|harmful)\s+(?:agent|bot|automation|system)/i,
|
|
11
|
-
/(?:unauthorized|unapproved|unmanaged)\s+(?:agent|bot)\s+(?:creation|deployment|execution|activation)/i,
|
|
12
|
-
/(?:bypass|circumvent|avoid)\s+(?:approval|authorization|review|governance)\s+(?:for|to\s+create|to\s+deploy)/i,
|
|
13
|
-
/(?:hidden|concealed|undocumented|unregistered)\s+(?:agent|bot|automation)/i,
|
|
14
|
-
/(?:self-replicating|self-propagating|autonomous)\s+(?:agent|bot)\s+(?:without|lacking)/i,
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
function scanText(text) {
|
|
18
|
-
if (!text) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
const matches = [];
|
|
22
|
-
for (const pattern of ROGUE_AGENT_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 rogue agent indicators in ${entity}: ${matches.join(', ')}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function scanAgentic10RogueAgent(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: 'Rogue Agent',
|
|
49
|
-
name: tool?.name || 'tool',
|
|
50
|
-
severity: 'critical',
|
|
51
|
-
reasons: [buildReason(`tool "${tool?.name || 'unknown'}"`, matches)],
|
|
52
|
-
tags: ['rogue-agent', 'unauthorized'],
|
|
53
|
-
agenticCategory: OWASP_ID,
|
|
54
|
-
safeUseNotes:
|
|
55
|
-
'Verify agent registration and approval status. Ensure all agents are properly managed and monitored.',
|
|
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: 'Rogue Agent',
|
|
65
|
-
uri: resource?.uri || resource?.name || 'resource',
|
|
66
|
-
severity: 'critical',
|
|
67
|
-
reasons: [
|
|
68
|
-
buildReason(`resource "${resource?.name || resource?.uri || 'unknown'}"`, matches),
|
|
69
|
-
],
|
|
70
|
-
tags: ['rogue-agent', 'unauthorized'],
|
|
71
|
-
agenticCategory: 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: 'Rogue Agent',
|
|
81
|
-
name: prompt?.name || 'prompt',
|
|
82
|
-
severity: 'high',
|
|
83
|
-
reasons: [buildReason(`prompt "${prompt?.name || 'unknown'}"`, matches)],
|
|
84
|
-
tags: ['rogue-agent', 'unauthorized'],
|
|
85
|
-
agenticCategory: OWASP_ID,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return results;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const adapter = createRuleAdapter(scanAgentic10RogueAgent, 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: 'Rogue Agent',
|
|
109
|
-
severity: 'critical',
|
|
110
|
-
title: 'Rogue Agent Pattern in Traffic',
|
|
111
|
-
description: `Potential rogue agent indicators 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: 'Rogue Agent Detection',
|
|
125
|
-
owasp_id: OWASP_ID,
|
|
126
|
-
severity: 'critical',
|
|
127
|
-
description: 'Detects rogue agent vulnerabilities.',
|
|
128
|
-
source: 'static',
|
|
129
|
-
type: 'agentic-security',
|
|
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
|
-
};
|