@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.
Files changed (158) hide show
  1. package/README.md +482 -56
  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 +255 -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/aauth-visibility.json +117 -0
  26. package/core/cli/data/rule-packs/agentic-security-2026.json +180 -0
  27. package/core/cli/data/rule-packs/general-security.json +173 -0
  28. package/core/cli/data/rule-packs/owasp-mcp-2026.json +244 -0
  29. package/core/cli/data/rule-packs/toxic-flow-heuristics.json +21 -0
  30. package/core/cli/data/rule-sources.json +5 -0
  31. package/core/cli/data/secret-patterns.json +18 -0
  32. package/core/cli/data/tool-classifications.json +111 -0
  33. package/core/cli/data/toxic-flow-rules.json +47 -0
  34. package/core/cli/index.js +23 -0
  35. package/core/cli/output/Banner.js +52 -0
  36. package/core/cli/output/Formatter.js +183 -0
  37. package/core/cli/output/JsonFormatter.js +106 -0
  38. package/core/cli/output/index.js +16 -0
  39. package/core/cli/secureRegistryFetch.js +157 -0
  40. package/core/cli/symbols.js +16 -0
  41. package/core/configs/environment.js +3 -1
  42. package/core/configs/index.js +3 -64
  43. package/core/container/DependencyContainer.js +4 -1
  44. package/core/mcp-server/index.js +4 -1
  45. package/core/mcp-server/server/external/all.js +10 -3
  46. package/core/mcp-server/server/external/config.js +62 -5
  47. package/core/models/RequestFilters.js +3 -0
  48. package/core/repositories/PacketRepository.js +16 -0
  49. package/core/services/AuditService.js +2 -0
  50. package/core/services/ConfigService.js +9 -1
  51. package/core/services/ConfigTransformService.js +34 -2
  52. package/core/services/RequestService.js +58 -5
  53. package/core/services/ServerManagementService.js +59 -4
  54. package/core/services/security/StaticRulesService.js +69 -13
  55. package/core/services/security/TrafficAnalysisService.js +19 -1
  56. package/core/services/security/TrafficToxicFlowService.js +154 -0
  57. package/core/services/security/aauthGraph.js +199 -0
  58. package/core/services/security/aauthParser.js +274 -0
  59. package/core/services/security/aauthSelfTest.js +346 -0
  60. package/core/services/security/index.js +2 -1
  61. package/core/services/security/rules/index.js +25 -59
  62. package/core/services/security/rules/scans/configPermissions.js +91 -0
  63. package/core/services/security/rules/scans/duplicateToolNames.js +85 -0
  64. package/core/services/security/rules/scans/insecureTransport.js +148 -0
  65. package/core/services/security/rules/scans/missingContainment.js +123 -0
  66. package/core/services/security/rules/scans/shellEnvInjection.js +101 -0
  67. package/core/services/security/rules/scans/unsafeDefaults.js +99 -0
  68. package/core/services/security/toolsListFromTrafficParser.js +70 -0
  69. package/core/tui/App.js +144 -0
  70. package/core/tui/FindingsPanel.js +115 -0
  71. package/core/tui/FixPanel.js +132 -0
  72. package/core/tui/Header.js +51 -0
  73. package/core/tui/HelpBar.js +42 -0
  74. package/core/tui/ServersPanel.js +109 -0
  75. package/core/tui/ToxicFlowsPanel.js +100 -0
  76. package/core/tui/h.js +8 -0
  77. package/core/tui/index.js +11 -0
  78. package/core/tui/render.js +22 -0
  79. package/package.json +24 -16
  80. package/ui/dist/assets/index-D6zDrtMV.js +81 -0
  81. package/ui/dist/index.html +1 -1
  82. package/ui/server/controllers/AauthController.js +279 -0
  83. package/ui/server/controllers/RequestController.js +12 -1
  84. package/ui/server/controllers/SecurityFindingsController.js +46 -1
  85. package/ui/server/routes/aauth.js +18 -0
  86. package/ui/server/routes/requests.js +8 -1
  87. package/ui/server/routes/security.js +5 -1
  88. package/ui/server/setup.js +224 -6
  89. package/ui/server/swagger/paths/components.js +55 -0
  90. package/ui/server/swagger/paths/securityTrafficFlows.js +59 -0
  91. package/ui/server/swagger/paths.js +2 -2
  92. package/ui/server/swagger/swagger.js +5 -2
  93. package/ui/server.js +1 -1
  94. package/ui/src/App.jsx +26 -52
  95. package/ui/src/PacketFilters.jsx +31 -1
  96. package/ui/src/PacketList.jsx +2 -2
  97. package/ui/src/Security.jsx +10 -0
  98. package/ui/src/TabNavigation.jsx +8 -0
  99. package/ui/src/components/AAuthBadge.jsx +92 -0
  100. package/ui/src/components/AauthExplorer/AauthExplorerGraph.jsx +231 -0
  101. package/ui/src/components/AauthExplorer/AauthExplorerView.jsx +387 -0
  102. package/ui/src/components/AauthExplorer/NodeDetailPanel.jsx +272 -0
  103. package/ui/src/components/App/ActionMenu.jsx +4 -31
  104. package/ui/src/components/App/ApiDocsButton.jsx +0 -1
  105. package/ui/src/components/App/ShutdownButton.jsx +0 -1
  106. package/ui/src/components/App/useAppState.js +19 -26
  107. package/ui/src/components/DetailsTab/AAuthIdentitySection.jsx +119 -0
  108. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +2 -0
  109. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +2 -0
  110. package/ui/src/components/DetectedPathsList.jsx +1 -5
  111. package/ui/src/components/FileInput.jsx +0 -1
  112. package/ui/src/components/PacketFilters/AAuthPostureFilter.jsx +81 -0
  113. package/ui/src/components/RequestRow/RequestRowMain.jsx +7 -1
  114. package/ui/src/components/Security/AAuthPosturePanel.jsx +360 -0
  115. package/ui/src/components/Security/ScannerContent.jsx +33 -1
  116. package/ui/src/components/Security/TrafficToxicFlowsPanel.jsx +253 -0
  117. package/ui/src/components/Security/securityApi.js +15 -0
  118. package/ui/src/components/Security/useSecurity.js +60 -3
  119. package/ui/src/components/ServerControl.jsx +0 -1
  120. package/ui/src/components/TabNavigation/DesktopTabs.jsx +0 -11
  121. package/ui/src/components/TabNavigationIcons.jsx +5 -0
  122. package/ui/src/components/ViewModeTabs.jsx +0 -1
  123. package/ui/src/utils/animations.js +26 -9
  124. package/core/services/security/rules/scans/agentic01GoalHijack.js +0 -130
  125. package/core/services/security/rules/scans/agentic02ToolMisuse.js +0 -129
  126. package/core/services/security/rules/scans/agentic03IdentityAbuse.js +0 -130
  127. package/core/services/security/rules/scans/agentic04SupplyChain.js +0 -130
  128. package/core/services/security/rules/scans/agentic06MemoryPoisoning.js +0 -130
  129. package/core/services/security/rules/scans/agentic07InsecureCommunication.js +0 -135
  130. package/core/services/security/rules/scans/agentic08CascadingFailures.js +0 -135
  131. package/core/services/security/rules/scans/agentic09TrustExploitation.js +0 -135
  132. package/core/services/security/rules/scans/agentic10RogueAgent.js +0 -130
  133. package/core/services/security/rules/scans/hardcodedSecrets.js +0 -130
  134. package/core/services/security/rules/scans/mcp01TokenMismanagement.js +0 -127
  135. package/core/services/security/rules/scans/mcp02ScopeCreep.js +0 -130
  136. package/core/services/security/rules/scans/mcp03ToolPoisoning.js +0 -132
  137. package/core/services/security/rules/scans/mcp04SupplyChain.js +0 -131
  138. package/core/services/security/rules/scans/mcp06PromptInjection.js +0 -200
  139. package/core/services/security/rules/scans/mcp07InsufficientAuth.js +0 -130
  140. package/core/services/security/rules/scans/mcp08LackAudit.js +0 -129
  141. package/core/services/security/rules/scans/mcp09ShadowServers.js +0 -129
  142. package/core/services/security/rules/scans/mcp10ContextInjection.js +0 -130
  143. package/ui/dist/assets/index-CiCSDYf-.js +0 -97
  144. package/ui/server/routes/help.js +0 -44
  145. package/ui/server/swagger/paths/help.js +0 -82
  146. package/ui/src/HelpGuide/HelpGuideContent.jsx +0 -118
  147. package/ui/src/HelpGuide/HelpGuideFooter.jsx +0 -59
  148. package/ui/src/HelpGuide/HelpGuideHeader.jsx +0 -57
  149. package/ui/src/HelpGuide.jsx +0 -78
  150. package/ui/src/IntroTour.jsx +0 -154
  151. package/ui/src/components/App/HelpButton.jsx +0 -90
  152. package/ui/src/components/TourOverlay.jsx +0 -117
  153. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +0 -120
  154. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +0 -71
  155. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +0 -54
  156. package/ui/src/components/TourTooltip/useTooltipPosition.js +0 -135
  157. package/ui/src/components/TourTooltip.jsx +0 -91
  158. package/ui/src/config/tourSteps.jsx +0 -140
@@ -3,6 +3,7 @@ import { SharkLogo } from './components/SharkLogo';
3
3
  import DesktopTabs from './components/TabNavigation/DesktopTabs';
4
4
  import MobileDropdown from './components/TabNavigation/MobileDropdown';
5
5
  import {
6
+ AauthIcon,
6
7
  LogsIcon,
7
8
  NetworkIcon,
8
9
  PlaygroundIcon,
@@ -44,6 +45,13 @@ function TabNavigation({ activeTab, onTabChange }) {
44
45
  icon: SecurityIcon,
45
46
  description: 'Local static analysis of captured MCP traffic',
46
47
  },
48
+ {
49
+ id: 'aauth-explorer',
50
+ label: 'AAuth Explorer',
51
+ icon: AauthIcon,
52
+ description:
53
+ 'Live knowledge graph of AAuth signals (agents, missions, resources, signing, access) observed in captured traffic',
54
+ },
47
55
  {
48
56
  id: 'smart-scan',
49
57
  label: 'Smart Scan',
@@ -0,0 +1,92 @@
1
+ import { IconKey, IconKeyOff, IconLockOpen, IconShieldCheck } from '@tabler/icons-react';
2
+ import { colors, fonts } from '../theme';
3
+
4
+ const POSTURES = {
5
+ signed: {
6
+ label: 'Signed',
7
+ color: colors.success,
8
+ bg: 'rgba(56, 161, 105, 0.10)',
9
+ border: 'rgba(56, 161, 105, 0.35)',
10
+ Icon: IconShieldCheck,
11
+ description:
12
+ 'AAuth identity headers and an HTTP Message Signature were observed. mcp-shark does not verify signatures.',
13
+ },
14
+ 'aauth-aware': {
15
+ label: 'AAuth-aware',
16
+ color: colors.accentBlue,
17
+ bg: 'rgba(45, 55, 72, 0.08)',
18
+ border: 'rgba(45, 55, 72, 0.30)',
19
+ Icon: IconKey,
20
+ description: 'AAuth headers were observed but no full HTTP Message Signature was present.',
21
+ },
22
+ bearer: {
23
+ label: 'Bearer',
24
+ color: colors.warning,
25
+ bg: 'rgba(214, 158, 46, 0.12)',
26
+ border: 'rgba(214, 158, 46, 0.40)',
27
+ Icon: IconKeyOff,
28
+ description:
29
+ 'A Bearer token was observed in the Authorization header. No AAuth identity present.',
30
+ },
31
+ none: {
32
+ label: 'No auth',
33
+ color: colors.textTertiary,
34
+ bg: 'rgba(128, 134, 139, 0.10)',
35
+ border: 'rgba(128, 134, 139, 0.30)',
36
+ Icon: IconLockOpen,
37
+ description: 'No AAuth identity or Bearer credential was observed.',
38
+ },
39
+ };
40
+
41
+ function buildTitle(aauth, descriptor) {
42
+ const parts = [descriptor.description];
43
+ if (aauth?.agent) {
44
+ parts.push(`Agent: ${aauth.agent}`);
45
+ }
46
+ if (aauth?.sig_alg) {
47
+ parts.push(`Algorithm: ${aauth.sig_alg}`);
48
+ }
49
+ if (aauth?.mission) {
50
+ parts.push(`Mission: ${aauth.mission}`);
51
+ }
52
+ return parts.join('\n');
53
+ }
54
+
55
+ /**
56
+ * Compact, posture-coloured chip used in tables and detail panels.
57
+ * Always describes signals as observed; never claims cryptographic verification.
58
+ */
59
+ export default function AAuthBadge({ aauth, size = 'md', showLabel = true }) {
60
+ const posture = aauth?.posture || 'none';
61
+ const descriptor = POSTURES[posture] || POSTURES.none;
62
+ const Icon = descriptor.Icon;
63
+
64
+ const dimensions =
65
+ size === 'sm'
66
+ ? { padding: '2px 6px', fontSize: '10px', iconSize: 11 }
67
+ : { padding: '3px 8px', fontSize: '11px', iconSize: 13 };
68
+
69
+ return (
70
+ <span
71
+ title={buildTitle(aauth, descriptor)}
72
+ style={{
73
+ display: 'inline-flex',
74
+ alignItems: 'center',
75
+ gap: '4px',
76
+ padding: dimensions.padding,
77
+ background: descriptor.bg,
78
+ border: `1px solid ${descriptor.border}`,
79
+ borderRadius: '999px',
80
+ color: descriptor.color,
81
+ fontSize: dimensions.fontSize,
82
+ fontFamily: fonts.body,
83
+ fontWeight: 500,
84
+ lineHeight: 1.2,
85
+ whiteSpace: 'nowrap',
86
+ }}
87
+ >
88
+ <Icon size={dimensions.iconSize} stroke={1.6} />
89
+ {showLabel && <span>{descriptor.label}</span>}
90
+ </span>
91
+ );
92
+ }
@@ -0,0 +1,231 @@
1
+ import ReactECharts from 'echarts-for-react';
2
+ import { useMemo, useRef } from 'react';
3
+ import { colors, fonts } from '../../theme.js';
4
+
5
+ const CATEGORY_PALETTE = {
6
+ agent: { color: '#1a73e8', symbolSize: 38, label: 'Agent' },
7
+ mission: { color: '#e8710a', symbolSize: 32, label: 'Mission' },
8
+ resource: { color: '#137333', symbolSize: 38, label: 'Resource' },
9
+ signing: { color: '#9334e6', symbolSize: 28, label: 'Signing' },
10
+ access: { color: '#b06000', symbolSize: 28, label: 'Access' },
11
+ };
12
+
13
+ const KIND_TO_LABEL = {
14
+ calls: 'calls',
15
+ pursues: 'pursues',
16
+ targets: 'targets',
17
+ 'signs-with': 'signs with',
18
+ requires: 'requires',
19
+ };
20
+
21
+ /**
22
+ * Force-directed AAuth knowledge graph.
23
+ *
24
+ * Backed by GET /api/aauth/graph; nodes are colored by category and sized by
25
+ * observed packet count. Clicking a node bubbles up so the parent can show a
26
+ * detail panel with the underlying packets.
27
+ */
28
+ export default function AauthExplorerGraph({ graph, onNodeSelect, selectedNodeId }) {
29
+ const chartRef = useRef(null);
30
+
31
+ const option = useMemo(() => {
32
+ const categories = (graph?.categories || []).map((c) => ({
33
+ name: c.label,
34
+ itemStyle: { color: CATEGORY_PALETTE[c.id]?.color || '#888' },
35
+ }));
36
+ const categoryIndex = Object.fromEntries((graph?.categories || []).map((c, i) => [c.id, i]));
37
+
38
+ const maxPackets = (graph?.nodes || []).reduce((m, n) => Math.max(m, n.packet_count || 0), 1);
39
+
40
+ const data = (graph?.nodes || []).map((n) => {
41
+ const palette = CATEGORY_PALETTE[n.category] || { symbolSize: 24, color: '#888' };
42
+ const sizeBoost = Math.min(20, Math.round((n.packet_count / maxPackets) * 18));
43
+ return {
44
+ id: n.id,
45
+ name: n.name,
46
+ category: categoryIndex[n.category] ?? 0,
47
+ symbolSize: palette.symbolSize + sizeBoost,
48
+ value: n.packet_count,
49
+ label: {
50
+ show: true,
51
+ formatter: shortenLabel(n.name, n.category),
52
+ fontSize: 11,
53
+ fontFamily: fonts.body,
54
+ color: colors.textPrimary,
55
+ },
56
+ itemStyle: {
57
+ color: palette.color,
58
+ borderColor: n.id === selectedNodeId ? colors.textPrimary : 'transparent',
59
+ borderWidth: n.id === selectedNodeId ? 3 : 0,
60
+ shadowBlur: n.id === selectedNodeId ? 12 : 0,
61
+ shadowColor: palette.color,
62
+ },
63
+ _raw: n,
64
+ };
65
+ });
66
+
67
+ const links = (graph?.edges || []).map((e) => ({
68
+ source: e.source,
69
+ target: e.target,
70
+ value: e.weight,
71
+ lineStyle: {
72
+ width: Math.max(1, Math.min(6, e.weight)),
73
+ color: 'source',
74
+ opacity: 0.55,
75
+ curveness: 0.12,
76
+ },
77
+ label: {
78
+ show: false,
79
+ formatter: KIND_TO_LABEL[e.kind] || e.kind,
80
+ fontSize: 10,
81
+ color: colors.textTertiary,
82
+ },
83
+ _kind: e.kind,
84
+ }));
85
+
86
+ return {
87
+ backgroundColor: 'transparent',
88
+ tooltip: {
89
+ trigger: 'item',
90
+ backgroundColor: '#fff',
91
+ borderColor: colors.borderLight,
92
+ textStyle: { color: colors.textPrimary, fontFamily: fonts.body, fontSize: 12 },
93
+ formatter: (params) => {
94
+ if (params.dataType === 'edge') {
95
+ return `<strong>${KIND_TO_LABEL[params.data._kind] || params.data._kind}</strong><br/>weight: ${params.data.value}`;
96
+ }
97
+ const raw = params.data._raw || {};
98
+ const lines = [
99
+ `<strong>${escapeHtml(raw.name)}</strong>`,
100
+ `<span style="color:${colors.textSecondary}">${CATEGORY_PALETTE[raw.category]?.label || raw.category}</span>`,
101
+ `${raw.packet_count} observation${raw.packet_count === 1 ? '' : 's'}`,
102
+ ];
103
+ return lines.join('<br/>');
104
+ },
105
+ },
106
+ legend: [
107
+ {
108
+ data: categories.map((c) => c.name),
109
+ orient: 'horizontal',
110
+ top: 0,
111
+ left: 'center',
112
+ textStyle: { color: colors.textSecondary, fontFamily: fonts.body, fontSize: 11 },
113
+ itemWidth: 10,
114
+ itemHeight: 10,
115
+ },
116
+ ],
117
+ animationDuration: 800,
118
+ animationEasingUpdate: 'quinticInOut',
119
+ series: [
120
+ {
121
+ name: 'AAuth observations',
122
+ type: 'graph',
123
+ layout: 'force',
124
+ legendHoverLink: true,
125
+ roam: true,
126
+ draggable: true,
127
+ focusNodeAdjacency: true,
128
+ categories,
129
+ data,
130
+ links,
131
+ force: {
132
+ repulsion: 220,
133
+ edgeLength: [80, 180],
134
+ gravity: 0.08,
135
+ friction: 0.6,
136
+ },
137
+ emphasis: {
138
+ focus: 'adjacency',
139
+ label: { show: true, fontSize: 12, fontFamily: fonts.body },
140
+ lineStyle: { width: 3 },
141
+ },
142
+ label: {
143
+ position: 'right',
144
+ color: colors.textPrimary,
145
+ },
146
+ lineStyle: { color: 'source', curveness: 0.1 },
147
+ },
148
+ ],
149
+ };
150
+ }, [graph, selectedNodeId]);
151
+
152
+ if (!graph || (graph.nodes?.length || 0) === 0) {
153
+ return (
154
+ <div
155
+ style={{
156
+ height: '100%',
157
+ display: 'flex',
158
+ alignItems: 'center',
159
+ justifyContent: 'center',
160
+ flexDirection: 'column',
161
+ gap: '12px',
162
+ padding: '40px',
163
+ color: colors.textSecondary,
164
+ fontFamily: fonts.body,
165
+ }}
166
+ >
167
+ <div style={{ fontSize: '32px' }}>·· · ··</div>
168
+ <div style={{ fontSize: '13px', textAlign: 'center', maxWidth: 520, lineHeight: 1.5 }}>
169
+ No AAuth signals have been observed yet. To preview this view with fake data, click{' '}
170
+ <strong>Generate sample data</strong> in the header above. For real signals, capture
171
+ AAuth-bearing traffic through the mcp-shark proxy (e.g. by exercising any AAuth-aware MCP
172
+ from your IDE).
173
+ </div>
174
+ </div>
175
+ );
176
+ }
177
+
178
+ return (
179
+ <ReactECharts
180
+ ref={chartRef}
181
+ option={option}
182
+ notMerge
183
+ lazyUpdate
184
+ style={{ height: '100%', width: '100%' }}
185
+ onEvents={{
186
+ click: (params) => {
187
+ if (params.dataType === 'node' && typeof onNodeSelect === 'function') {
188
+ const raw = params.data._raw || {};
189
+ onNodeSelect({
190
+ category: getCategoryIdFromIndex(graph, params.data.category),
191
+ id: raw.name,
192
+ nodeKey: params.data.id,
193
+ raw,
194
+ });
195
+ }
196
+ },
197
+ }}
198
+ />
199
+ );
200
+ }
201
+
202
+ function getCategoryIdFromIndex(graph, index) {
203
+ return graph?.categories?.[index]?.id || null;
204
+ }
205
+
206
+ function shortenLabel(name, category) {
207
+ if (!name) {
208
+ return '';
209
+ }
210
+ if (category === 'resource' && name.length > 22) {
211
+ return `${name.slice(0, 20)}…`;
212
+ }
213
+ if (category === 'mission' && name.length > 28) {
214
+ return `${name.slice(0, 26)}…`;
215
+ }
216
+ if (category === 'agent' && name.length > 28) {
217
+ return `${name.slice(0, 26)}…`;
218
+ }
219
+ return name;
220
+ }
221
+
222
+ function escapeHtml(s) {
223
+ if (typeof s !== 'string') {
224
+ return '';
225
+ }
226
+ return s
227
+ .replace(/&/g, '&amp;')
228
+ .replace(/</g, '&lt;')
229
+ .replace(/>/g, '&gt;')
230
+ .replace(/"/g, '&quot;');
231
+ }