@mcp-shark/mcp-shark 1.4.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.
- package/LICENSE +85 -0
- package/README.md +724 -0
- package/bin/mcp-shark.js +93 -0
- package/mcp-server/.editorconfig +15 -0
- package/mcp-server/.prettierignore +11 -0
- package/mcp-server/.prettierrc +12 -0
- package/mcp-server/README.md +280 -0
- package/mcp-server/commitlint.config.cjs +42 -0
- package/mcp-server/eslint.config.js +131 -0
- package/mcp-server/lib/auditor/audit.js +228 -0
- package/mcp-server/lib/common/error.js +15 -0
- package/mcp-server/lib/server/external/all.js +32 -0
- package/mcp-server/lib/server/external/config.js +59 -0
- package/mcp-server/lib/server/external/kv.js +102 -0
- package/mcp-server/lib/server/external/single/client.js +35 -0
- package/mcp-server/lib/server/external/single/request.js +49 -0
- package/mcp-server/lib/server/external/single/run.js +75 -0
- package/mcp-server/lib/server/external/single/transport.js +57 -0
- package/mcp-server/lib/server/internal/handlers/common.js +20 -0
- package/mcp-server/lib/server/internal/handlers/error.js +7 -0
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +22 -0
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +12 -0
- package/mcp-server/lib/server/internal/handlers/resources-list.js +12 -0
- package/mcp-server/lib/server/internal/handlers/resources-read.js +19 -0
- package/mcp-server/lib/server/internal/handlers/tools-call.js +37 -0
- package/mcp-server/lib/server/internal/handlers/tools-list.js +14 -0
- package/mcp-server/lib/server/internal/run.js +49 -0
- package/mcp-server/lib/server/internal/server.js +63 -0
- package/mcp-server/lib/server/internal/session.js +39 -0
- package/mcp-server/mcp-shark.js +72 -0
- package/mcp-server/package-lock.json +4784 -0
- package/mcp-server/package.json +30 -0
- package/package.json +103 -0
- package/ui/README.md +212 -0
- package/ui/index.html +16 -0
- package/ui/package-lock.json +3574 -0
- package/ui/package.json +12 -0
- package/ui/paths.js +282 -0
- package/ui/public/og-image.png +0 -0
- package/ui/server/routes/backups.js +251 -0
- package/ui/server/routes/composite.js +244 -0
- package/ui/server/routes/config.js +175 -0
- package/ui/server/routes/conversations.js +25 -0
- package/ui/server/routes/help.js +43 -0
- package/ui/server/routes/logs.js +32 -0
- package/ui/server/routes/playground.js +152 -0
- package/ui/server/routes/requests.js +235 -0
- package/ui/server/routes/sessions.js +27 -0
- package/ui/server/routes/smartscan/discover.js +117 -0
- package/ui/server/routes/smartscan/scans/clearCache.js +22 -0
- package/ui/server/routes/smartscan/scans/createBatchScans.js +123 -0
- package/ui/server/routes/smartscan/scans/createScan.js +42 -0
- package/ui/server/routes/smartscan/scans/getCachedResults.js +51 -0
- package/ui/server/routes/smartscan/scans/getScan.js +41 -0
- package/ui/server/routes/smartscan/scans/listScans.js +24 -0
- package/ui/server/routes/smartscan/scans.js +13 -0
- package/ui/server/routes/smartscan/token.js +56 -0
- package/ui/server/routes/smartscan/transport.js +53 -0
- package/ui/server/routes/smartscan.js +24 -0
- package/ui/server/routes/statistics.js +83 -0
- package/ui/server/utils/config-update.js +212 -0
- package/ui/server/utils/config.js +98 -0
- package/ui/server/utils/paths.js +23 -0
- package/ui/server/utils/port.js +28 -0
- package/ui/server/utils/process.js +80 -0
- package/ui/server/utils/scan-cache/all-results.js +180 -0
- package/ui/server/utils/scan-cache/directory.js +35 -0
- package/ui/server/utils/scan-cache/file-operations.js +104 -0
- package/ui/server/utils/scan-cache/hash.js +47 -0
- package/ui/server/utils/scan-cache/server-operations.js +80 -0
- package/ui/server/utils/scan-cache.js +12 -0
- package/ui/server/utils/serialization.js +13 -0
- package/ui/server/utils/smartscan-token.js +42 -0
- package/ui/server.js +199 -0
- package/ui/src/App.jsx +153 -0
- package/ui/src/CompositeLogs.jsx +164 -0
- package/ui/src/CompositeSetup.jsx +285 -0
- package/ui/src/HelpGuide/HelpGuideContent.jsx +118 -0
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +58 -0
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +56 -0
- package/ui/src/HelpGuide.jsx +65 -0
- package/ui/src/IntroTour.jsx +140 -0
- package/ui/src/LogDetail.jsx +122 -0
- package/ui/src/LogTable.jsx +242 -0
- package/ui/src/PacketDetail.jsx +190 -0
- package/ui/src/PacketFilters.jsx +222 -0
- package/ui/src/PacketList.jsx +183 -0
- package/ui/src/SmartScan.jsx +178 -0
- package/ui/src/TabNavigation.jsx +143 -0
- package/ui/src/components/App/HelpButton.jsx +64 -0
- package/ui/src/components/App/TrafficTab.jsx +69 -0
- package/ui/src/components/App/useAppState.js +163 -0
- package/ui/src/components/BackupList.jsx +192 -0
- package/ui/src/components/CollapsibleSection.jsx +82 -0
- package/ui/src/components/ConfigFileSection.jsx +84 -0
- package/ui/src/components/ConfigViewerModal.jsx +141 -0
- package/ui/src/components/ConfirmationModal.jsx +129 -0
- package/ui/src/components/DetailsTab/BodySection.jsx +27 -0
- package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +70 -0
- package/ui/src/components/DetailsTab/HeadersSection.jsx +25 -0
- package/ui/src/components/DetailsTab/InfoSection.jsx +28 -0
- package/ui/src/components/DetailsTab/NetworkInfoSection.jsx +63 -0
- package/ui/src/components/DetailsTab/ProtocolInfoSection.jsx +75 -0
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +46 -0
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +66 -0
- package/ui/src/components/DetailsTab.jsx +31 -0
- package/ui/src/components/DetectedPathsList.jsx +171 -0
- package/ui/src/components/FileInput.jsx +144 -0
- package/ui/src/components/GroupHeader.jsx +76 -0
- package/ui/src/components/GroupedByMcpView.jsx +103 -0
- package/ui/src/components/GroupedByServerView.jsx +134 -0
- package/ui/src/components/GroupedBySessionView.jsx +127 -0
- package/ui/src/components/GroupedViews.jsx +2 -0
- package/ui/src/components/HexTab.jsx +188 -0
- package/ui/src/components/LogsDisplay.jsx +93 -0
- package/ui/src/components/LogsToolbar.jsx +193 -0
- package/ui/src/components/McpPlayground/LoadingModal.jsx +113 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +125 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +48 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +45 -0
- package/ui/src/components/McpPlayground/PromptsSection.jsx +106 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +89 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +59 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +45 -0
- package/ui/src/components/McpPlayground/ResourcesSection.jsx +91 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +125 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +48 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +45 -0
- package/ui/src/components/McpPlayground/ToolsSection.jsx +107 -0
- package/ui/src/components/McpPlayground/common/EmptyState.jsx +17 -0
- package/ui/src/components/McpPlayground/common/ErrorState.jsx +17 -0
- package/ui/src/components/McpPlayground/common/LoadingState.jsx +17 -0
- package/ui/src/components/McpPlayground/useMcpPlayground.js +280 -0
- package/ui/src/components/McpPlayground.jsx +171 -0
- package/ui/src/components/MessageDisplay.jsx +28 -0
- package/ui/src/components/PacketDetailHeader.jsx +88 -0
- package/ui/src/components/PacketFilters/ExportControls.jsx +126 -0
- package/ui/src/components/PacketFilters/FilterInput.jsx +59 -0
- package/ui/src/components/RawTab.jsx +142 -0
- package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +155 -0
- package/ui/src/components/RequestRow/RequestRowMain.jsx +240 -0
- package/ui/src/components/RequestRow/ResponseRow.jsx +158 -0
- package/ui/src/components/RequestRow.jsx +70 -0
- package/ui/src/components/ServerControl.jsx +133 -0
- package/ui/src/components/ServiceSelector.jsx +209 -0
- package/ui/src/components/SetupHeader.jsx +30 -0
- package/ui/src/components/SharkLogo.jsx +21 -0
- package/ui/src/components/SmartScan/AnalysisResult.jsx +64 -0
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +215 -0
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +94 -0
- package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +26 -0
- package/ui/src/components/SmartScan/DebugInfoSection.jsx +53 -0
- package/ui/src/components/SmartScan/EmptyState.jsx +57 -0
- package/ui/src/components/SmartScan/ErrorDisplay.jsx +48 -0
- package/ui/src/components/SmartScan/ExpandableSection.jsx +93 -0
- package/ui/src/components/SmartScan/FindingsTable.jsx +257 -0
- package/ui/src/components/SmartScan/ListViewContent.jsx +75 -0
- package/ui/src/components/SmartScan/NotablePatternsSection.jsx +75 -0
- package/ui/src/components/SmartScan/OverallSummarySection.jsx +72 -0
- package/ui/src/components/SmartScan/RawDataSection.jsx +52 -0
- package/ui/src/components/SmartScan/RecommendationsSection.jsx +78 -0
- package/ui/src/components/SmartScan/ScanDetailHeader.jsx +92 -0
- package/ui/src/components/SmartScan/ScanDetailView.jsx +141 -0
- package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +49 -0
- package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +201 -0
- package/ui/src/components/SmartScan/ScanListView.jsx +73 -0
- package/ui/src/components/SmartScan/ScanOverviewSection.jsx +123 -0
- package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +35 -0
- package/ui/src/components/SmartScan/ScanViewContent.jsx +68 -0
- package/ui/src/components/SmartScan/ScanningProgress.jsx +47 -0
- package/ui/src/components/SmartScan/ServerInfoSection.jsx +43 -0
- package/ui/src/components/SmartScan/ServerSelectionRow.jsx +207 -0
- package/ui/src/components/SmartScan/SingleResultDisplay.jsx +269 -0
- package/ui/src/components/SmartScan/SmartScanControls.jsx +290 -0
- package/ui/src/components/SmartScan/SmartScanHeader.jsx +77 -0
- package/ui/src/components/SmartScan/ViewModeTabs.jsx +57 -0
- package/ui/src/components/SmartScan/hooks/useCacheManagement.js +34 -0
- package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +121 -0
- package/ui/src/components/SmartScan/hooks/useScanList.js +193 -0
- package/ui/src/components/SmartScan/hooks/useScanOperations.js +87 -0
- package/ui/src/components/SmartScan/hooks/useServerStatus.js +26 -0
- package/ui/src/components/SmartScan/hooks/useTokenManagement.js +53 -0
- package/ui/src/components/SmartScan/scanDataUtils.js +98 -0
- package/ui/src/components/SmartScan/useSmartScan.js +72 -0
- package/ui/src/components/SmartScan/utils.js +19 -0
- package/ui/src/components/SmartScanIcons.jsx +58 -0
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +111 -0
- package/ui/src/components/TabNavigation/MobileDropdown.jsx +140 -0
- package/ui/src/components/TabNavigation.jsx +97 -0
- package/ui/src/components/TabNavigationIcons.jsx +40 -0
- package/ui/src/components/TableHeader.jsx +164 -0
- package/ui/src/components/TourOverlay.jsx +117 -0
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +117 -0
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +70 -0
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +45 -0
- package/ui/src/components/TourTooltip/useTooltipPosition.js +108 -0
- package/ui/src/components/TourTooltip.jsx +83 -0
- package/ui/src/components/ViewModeTabs.jsx +91 -0
- package/ui/src/components/WhatThisDoesSection.jsx +61 -0
- package/ui/src/config/tourSteps.jsx +141 -0
- package/ui/src/hooks/useAnimation.js +92 -0
- package/ui/src/hooks/useConfigManagement.js +124 -0
- package/ui/src/hooks/useServiceExtraction.js +51 -0
- package/ui/src/index.css +42 -0
- package/ui/src/main.jsx +10 -0
- package/ui/src/theme.js +65 -0
- package/ui/src/utils/animations.js +170 -0
- package/ui/src/utils/groupingUtils.js +93 -0
- package/ui/src/utils/hexUtils.js +24 -0
- package/ui/src/utils/mcpGroupingUtils.js +262 -0
- package/ui/src/utils/requestUtils.js +297 -0
- package/ui/vite.config.js +18 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { colors, fonts } from './theme';
|
|
3
|
+
import { fadeIn } from './utils/animations';
|
|
4
|
+
import FilterInput from './components/PacketFilters/FilterInput';
|
|
5
|
+
import ExportControls from './components/PacketFilters/ExportControls';
|
|
6
|
+
import ConfirmationModal from './components/ConfirmationModal';
|
|
7
|
+
import { IconTrash, IconSearch } from '@tabler/icons-react';
|
|
8
|
+
import anime from 'animejs';
|
|
9
|
+
|
|
10
|
+
function RequestFilters({ filters, onFilterChange, stats, onExport, onClear }) {
|
|
11
|
+
const filtersRef = useRef(null);
|
|
12
|
+
const [showClearModal, setShowClearModal] = useState(false);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (filtersRef.current) {
|
|
16
|
+
fadeIn(filtersRef.current, { duration: 400 });
|
|
17
|
+
}
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
const handleExport = async (format = 'json') => {
|
|
21
|
+
try {
|
|
22
|
+
const queryParams = new URLSearchParams();
|
|
23
|
+
if (filters.search) queryParams.append('search', filters.search);
|
|
24
|
+
if (filters.serverName) queryParams.append('serverName', filters.serverName);
|
|
25
|
+
if (filters.sessionId) queryParams.append('sessionId', filters.sessionId);
|
|
26
|
+
if (filters.method) queryParams.append('method', filters.method);
|
|
27
|
+
if (filters.jsonrpcMethod) queryParams.append('jsonrpcMethod', filters.jsonrpcMethod);
|
|
28
|
+
if (filters.statusCode) queryParams.append('statusCode', filters.statusCode);
|
|
29
|
+
if (filters.jsonrpcId) queryParams.append('jsonrpcId', filters.jsonrpcId);
|
|
30
|
+
queryParams.append('format', format);
|
|
31
|
+
|
|
32
|
+
const response = await fetch(`/api/requests/export?${queryParams}`);
|
|
33
|
+
const blob = await response.blob();
|
|
34
|
+
const url = window.URL.createObjectURL(blob);
|
|
35
|
+
const a = document.createElement('a');
|
|
36
|
+
a.href = url;
|
|
37
|
+
const extension = format === 'csv' ? 'csv' : format === 'txt' ? 'txt' : 'json';
|
|
38
|
+
a.download = `mcp-shark-traffic-${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
|
|
39
|
+
document.body.appendChild(a);
|
|
40
|
+
a.click();
|
|
41
|
+
window.URL.revokeObjectURL(url);
|
|
42
|
+
document.body.removeChild(a);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Failed to export traffic:', error);
|
|
45
|
+
alert('Failed to export traffic. Please try again.');
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
ref={filtersRef}
|
|
52
|
+
data-tour="filters"
|
|
53
|
+
style={{
|
|
54
|
+
padding: '12px 16px',
|
|
55
|
+
borderBottom: `1px solid ${colors.borderLight}`,
|
|
56
|
+
background: colors.bgSecondary,
|
|
57
|
+
display: 'flex',
|
|
58
|
+
gap: '10px',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
flexWrap: 'wrap',
|
|
61
|
+
boxShadow: `0 1px 3px ${colors.shadowSm}`,
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
<div style={{ position: 'relative', width: '300px' }}>
|
|
65
|
+
<IconSearch
|
|
66
|
+
size={16}
|
|
67
|
+
stroke={1.5}
|
|
68
|
+
style={{
|
|
69
|
+
position: 'absolute',
|
|
70
|
+
left: '12px',
|
|
71
|
+
top: '50%',
|
|
72
|
+
transform: 'translateY(-50%)',
|
|
73
|
+
color: colors.textTertiary,
|
|
74
|
+
pointerEvents: 'none',
|
|
75
|
+
zIndex: 1,
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
<FilterInput
|
|
79
|
+
type="text"
|
|
80
|
+
placeholder="Search everything (partial match)..."
|
|
81
|
+
value={filters.search || ''}
|
|
82
|
+
onChange={(e) => onFilterChange({ ...filters, search: e.target.value || null })}
|
|
83
|
+
style={{
|
|
84
|
+
width: '100%',
|
|
85
|
+
fontWeight: filters.search ? '500' : '400',
|
|
86
|
+
paddingLeft: '36px',
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<FilterInput
|
|
92
|
+
type="text"
|
|
93
|
+
placeholder="MCP Server Name..."
|
|
94
|
+
value={filters.serverName || ''}
|
|
95
|
+
onChange={(e) => onFilterChange({ ...filters, serverName: e.target.value || null })}
|
|
96
|
+
style={{ width: '200px' }}
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<FilterInput
|
|
100
|
+
type="text"
|
|
101
|
+
placeholder="Session ID..."
|
|
102
|
+
value={filters.sessionId || ''}
|
|
103
|
+
onChange={(e) => onFilterChange({ ...filters, sessionId: e.target.value || null })}
|
|
104
|
+
style={{ width: '200px' }}
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
<FilterInput
|
|
108
|
+
type="text"
|
|
109
|
+
placeholder="HTTP Method..."
|
|
110
|
+
value={filters.method || ''}
|
|
111
|
+
onChange={(e) => onFilterChange({ ...filters, method: e.target.value || null })}
|
|
112
|
+
style={{ width: '150px' }}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<FilterInput
|
|
116
|
+
type="text"
|
|
117
|
+
placeholder="JSON-RPC Method..."
|
|
118
|
+
value={filters.jsonrpcMethod || ''}
|
|
119
|
+
onChange={(e) => onFilterChange({ ...filters, jsonrpcMethod: e.target.value || null })}
|
|
120
|
+
style={{ width: '200px' }}
|
|
121
|
+
/>
|
|
122
|
+
|
|
123
|
+
<FilterInput
|
|
124
|
+
type="number"
|
|
125
|
+
placeholder="Status Code..."
|
|
126
|
+
value={filters.statusCode || ''}
|
|
127
|
+
onChange={(e) =>
|
|
128
|
+
onFilterChange({
|
|
129
|
+
...filters,
|
|
130
|
+
statusCode: e.target.value ? parseInt(e.target.value) : null,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
style={{ width: '120px' }}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<FilterInput
|
|
137
|
+
type="text"
|
|
138
|
+
placeholder="JSON-RPC ID..."
|
|
139
|
+
value={filters.jsonrpcId || ''}
|
|
140
|
+
onChange={(e) => onFilterChange({ ...filters, jsonrpcId: e.target.value || null })}
|
|
141
|
+
style={{ width: '150px' }}
|
|
142
|
+
/>
|
|
143
|
+
|
|
144
|
+
<ExportControls stats={stats} onExport={handleExport} />
|
|
145
|
+
|
|
146
|
+
<button
|
|
147
|
+
onClick={() => setShowClearModal(true)}
|
|
148
|
+
style={{
|
|
149
|
+
padding: '8px 14px',
|
|
150
|
+
background: colors.buttonDanger,
|
|
151
|
+
border: 'none',
|
|
152
|
+
color: colors.textInverse,
|
|
153
|
+
fontSize: '12px',
|
|
154
|
+
fontFamily: fonts.body,
|
|
155
|
+
fontWeight: '500',
|
|
156
|
+
borderRadius: '8px',
|
|
157
|
+
cursor: 'pointer',
|
|
158
|
+
display: 'flex',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
gap: '6px',
|
|
161
|
+
transition: 'all 0.2s',
|
|
162
|
+
boxShadow: `0 2px 4px ${colors.shadowSm}`,
|
|
163
|
+
marginLeft: '12px',
|
|
164
|
+
}}
|
|
165
|
+
onMouseEnter={(e) => {
|
|
166
|
+
anime({
|
|
167
|
+
targets: e.currentTarget,
|
|
168
|
+
background: colors.buttonDangerHover,
|
|
169
|
+
translateY: -1,
|
|
170
|
+
boxShadow: [`0 2px 4px ${colors.shadowSm}`, `0 4px 8px ${colors.shadowMd}`],
|
|
171
|
+
duration: 200,
|
|
172
|
+
easing: 'easeOutQuad',
|
|
173
|
+
});
|
|
174
|
+
}}
|
|
175
|
+
onMouseLeave={(e) => {
|
|
176
|
+
anime({
|
|
177
|
+
targets: e.currentTarget,
|
|
178
|
+
background: colors.buttonDanger,
|
|
179
|
+
translateY: 0,
|
|
180
|
+
boxShadow: [`0 4px 8px ${colors.shadowMd}`, `0 2px 4px ${colors.shadowSm}`],
|
|
181
|
+
duration: 200,
|
|
182
|
+
easing: 'easeOutQuad',
|
|
183
|
+
});
|
|
184
|
+
}}
|
|
185
|
+
title="Clear all captured traffic"
|
|
186
|
+
>
|
|
187
|
+
<IconTrash size={14} stroke={1.5} />
|
|
188
|
+
Clear
|
|
189
|
+
</button>
|
|
190
|
+
|
|
191
|
+
<ConfirmationModal
|
|
192
|
+
isOpen={showClearModal}
|
|
193
|
+
onClose={() => setShowClearModal(false)}
|
|
194
|
+
onConfirm={async () => {
|
|
195
|
+
try {
|
|
196
|
+
const response = await fetch('/api/requests/clear', {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
});
|
|
199
|
+
if (response.ok) {
|
|
200
|
+
if (onClear) {
|
|
201
|
+
onClear();
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
const error = await response.json();
|
|
205
|
+
alert(`Failed to clear traffic: ${error.error || 'Unknown error'}`);
|
|
206
|
+
}
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error('Failed to clear traffic:', error);
|
|
209
|
+
alert('Failed to clear traffic. Please try again.');
|
|
210
|
+
}
|
|
211
|
+
}}
|
|
212
|
+
title="Clear All Captured Traffic"
|
|
213
|
+
message="Are you sure you want to delete all captured traffic? This action cannot be undone and will permanently remove all requests and responses from the database."
|
|
214
|
+
confirmText="Clear All"
|
|
215
|
+
cancelText="Cancel"
|
|
216
|
+
danger={true}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export default RequestFilters;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { colors, fonts } from './theme';
|
|
3
|
+
import RequestRow from './components/RequestRow';
|
|
4
|
+
import TableHeader from './components/TableHeader';
|
|
5
|
+
import ViewModeTabs from './components/ViewModeTabs';
|
|
6
|
+
import GroupedByMcpView from './components/GroupedByMcpView';
|
|
7
|
+
import { groupByMcpSessionAndCategory } from './utils/mcpGroupingUtils.js';
|
|
8
|
+
import { pairRequestsWithResponses } from './utils/requestUtils.js';
|
|
9
|
+
import { staggerIn } from './utils/animations';
|
|
10
|
+
import anime from 'animejs';
|
|
11
|
+
|
|
12
|
+
function RequestList({ requests, selected, onSelect, firstRequestTime }) {
|
|
13
|
+
const [viewMode, setViewMode] = useState('general');
|
|
14
|
+
const [columnWidths] = useState({
|
|
15
|
+
frame: 90, // Frame numbers like "#1234"
|
|
16
|
+
time: 120, // Relative time like "0.123456"
|
|
17
|
+
datetime: 180, // Full date/time like "11/30/2024, 19:25:30"
|
|
18
|
+
source: 200, // IP addresses or hostnames
|
|
19
|
+
destination: 200, // IP addresses or hostnames
|
|
20
|
+
protocol: 90, // Usually "HTTP"
|
|
21
|
+
method: 90, // HTTP methods like "POST", "GET"
|
|
22
|
+
status: 80, // Status codes like "200", "404"
|
|
23
|
+
endpoint: 500, // JSON-RPC methods or URLs (most important, needs space)
|
|
24
|
+
});
|
|
25
|
+
const [expandedResponses, setExpandedResponses] = useState(new Set());
|
|
26
|
+
const [expandedMcpSessions, setExpandedMcpSessions] = useState(new Set());
|
|
27
|
+
const [expandedMcpCategories, setExpandedMcpCategories] = useState(new Set());
|
|
28
|
+
const tbodyRef = useRef(null);
|
|
29
|
+
const prevRequestsLengthRef = useRef(0);
|
|
30
|
+
|
|
31
|
+
const groupedByMcp = useMemo(() => groupByMcpSessionAndCategory(requests), [requests]);
|
|
32
|
+
|
|
33
|
+
// Animate rows when requests change
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (tbodyRef.current && requests.length > 0) {
|
|
36
|
+
const rows = tbodyRef.current.querySelectorAll('tr');
|
|
37
|
+
if (rows.length > 0) {
|
|
38
|
+
// Only animate new rows if the list has grown
|
|
39
|
+
if (requests.length > prevRequestsLengthRef.current) {
|
|
40
|
+
const newRows = Array.from(rows).slice(prevRequestsLengthRef.current);
|
|
41
|
+
if (newRows.length > 0) {
|
|
42
|
+
staggerIn(newRows, { delay: 30, duration: 300 });
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
// Animate all rows if the list was reset
|
|
46
|
+
staggerIn(rows, { delay: 20, duration: 300 });
|
|
47
|
+
}
|
|
48
|
+
prevRequestsLengthRef.current = requests.length;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}, [requests]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (viewMode === 'groupedByMcp') {
|
|
55
|
+
// Auto-expand all MCP sessions and categories
|
|
56
|
+
const allSessionIds = new Set(groupedByMcp.map((g) => g.sessionId || '__NO_SESSION__'));
|
|
57
|
+
setExpandedMcpSessions((prev) => {
|
|
58
|
+
const updated = new Set(prev);
|
|
59
|
+
allSessionIds.forEach((id) => updated.add(id));
|
|
60
|
+
return updated;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
setExpandedMcpCategories((prev) => {
|
|
64
|
+
const updated = new Set(prev);
|
|
65
|
+
groupedByMcp.forEach((sessionGroup) => {
|
|
66
|
+
const sessionKey = sessionGroup.sessionId || '__NO_SESSION__';
|
|
67
|
+
sessionGroup.categories.forEach((category) => {
|
|
68
|
+
updated.add(`${sessionKey}::${category.category}`);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return updated;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}, [groupedByMcp, viewMode]);
|
|
75
|
+
|
|
76
|
+
const toggleMcpSession = (sessionKey) => {
|
|
77
|
+
setExpandedMcpSessions((prev) => {
|
|
78
|
+
const updated = new Set(prev);
|
|
79
|
+
if (updated.has(sessionKey)) {
|
|
80
|
+
updated.delete(sessionKey);
|
|
81
|
+
} else {
|
|
82
|
+
updated.add(sessionKey);
|
|
83
|
+
}
|
|
84
|
+
return updated;
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const toggleMcpCategory = (categoryKey) => {
|
|
89
|
+
setExpandedMcpCategories((prev) => {
|
|
90
|
+
const updated = new Set(prev);
|
|
91
|
+
if (updated.has(categoryKey)) {
|
|
92
|
+
updated.delete(categoryKey);
|
|
93
|
+
} else {
|
|
94
|
+
updated.add(categoryKey);
|
|
95
|
+
}
|
|
96
|
+
return updated;
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const pairedRequests = useMemo(() => pairRequestsWithResponses(requests), [requests]);
|
|
101
|
+
|
|
102
|
+
const toggleResponse = (frameNumber) => {
|
|
103
|
+
setExpandedResponses((prev) => {
|
|
104
|
+
const updated = new Set(prev);
|
|
105
|
+
if (updated.has(frameNumber)) {
|
|
106
|
+
updated.delete(frameNumber);
|
|
107
|
+
} else {
|
|
108
|
+
updated.add(frameNumber);
|
|
109
|
+
}
|
|
110
|
+
return updated;
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const renderGeneralView = () => (
|
|
115
|
+
<tbody ref={tbodyRef}>
|
|
116
|
+
{pairedRequests.map((pair) => (
|
|
117
|
+
<RequestRow
|
|
118
|
+
key={pair.frame_number}
|
|
119
|
+
pair={pair}
|
|
120
|
+
selected={selected}
|
|
121
|
+
firstRequestTime={firstRequestTime}
|
|
122
|
+
onSelect={onSelect}
|
|
123
|
+
isExpanded={expandedResponses.has(pair.frame_number)}
|
|
124
|
+
onToggleExpand={() => toggleResponse(pair.frame_number)}
|
|
125
|
+
/>
|
|
126
|
+
))}
|
|
127
|
+
</tbody>
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div
|
|
132
|
+
style={{
|
|
133
|
+
flex: 1,
|
|
134
|
+
display: 'flex',
|
|
135
|
+
flexDirection: 'column',
|
|
136
|
+
background: colors.bgPrimary,
|
|
137
|
+
minHeight: 0,
|
|
138
|
+
overflow: 'hidden',
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<ViewModeTabs viewMode={viewMode} onViewModeChange={setViewMode} />
|
|
142
|
+
|
|
143
|
+
<div
|
|
144
|
+
style={{
|
|
145
|
+
flex: 1,
|
|
146
|
+
overflowY: 'auto',
|
|
147
|
+
overflowX: 'auto',
|
|
148
|
+
minHeight: 0,
|
|
149
|
+
WebkitOverflowScrolling: 'touch',
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
<table
|
|
153
|
+
style={{
|
|
154
|
+
width: '100%',
|
|
155
|
+
borderCollapse: 'separate',
|
|
156
|
+
borderSpacing: 0,
|
|
157
|
+
fontSize: '12px',
|
|
158
|
+
fontFamily: fonts.body,
|
|
159
|
+
background: colors.bgPrimary,
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
<TableHeader columnWidths={columnWidths} />
|
|
163
|
+
{viewMode === 'general' ? (
|
|
164
|
+
renderGeneralView()
|
|
165
|
+
) : (
|
|
166
|
+
<GroupedByMcpView
|
|
167
|
+
groupedData={groupedByMcp}
|
|
168
|
+
expandedSessions={expandedMcpSessions}
|
|
169
|
+
expandedCategories={expandedMcpCategories}
|
|
170
|
+
onToggleSession={toggleMcpSession}
|
|
171
|
+
onToggleCategory={toggleMcpCategory}
|
|
172
|
+
selected={selected}
|
|
173
|
+
firstRequestTime={firstRequestTime}
|
|
174
|
+
onSelect={onSelect}
|
|
175
|
+
/>
|
|
176
|
+
)}
|
|
177
|
+
</table>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export default RequestList;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { colors } from './theme';
|
|
3
|
+
import SmartScanHeader from './components/SmartScan/SmartScanHeader';
|
|
4
|
+
import SmartScanControls from './components/SmartScan/SmartScanControls';
|
|
5
|
+
import ViewModeTabs from './components/SmartScan/ViewModeTabs';
|
|
6
|
+
import ScanViewContent from './components/SmartScan/ScanViewContent';
|
|
7
|
+
import ListViewContent from './components/SmartScan/ListViewContent';
|
|
8
|
+
import { useSmartScan } from './components/SmartScan/useSmartScan';
|
|
9
|
+
|
|
10
|
+
function SmartScan() {
|
|
11
|
+
const [viewMode, setViewMode] = useState('scan'); // 'scan' or 'list'
|
|
12
|
+
const {
|
|
13
|
+
apiToken,
|
|
14
|
+
setApiToken,
|
|
15
|
+
discoveredServers,
|
|
16
|
+
selectedServers,
|
|
17
|
+
setSelectedServers,
|
|
18
|
+
loadingData,
|
|
19
|
+
scanning,
|
|
20
|
+
scanResult,
|
|
21
|
+
scanResults,
|
|
22
|
+
error,
|
|
23
|
+
saveToken,
|
|
24
|
+
discoverMcpData,
|
|
25
|
+
runScan,
|
|
26
|
+
clearCache,
|
|
27
|
+
clearingCache,
|
|
28
|
+
allScans,
|
|
29
|
+
loadingScans,
|
|
30
|
+
loadAllScans,
|
|
31
|
+
selectedScan,
|
|
32
|
+
setSelectedScan,
|
|
33
|
+
loadingScanDetail,
|
|
34
|
+
loadScanDetail,
|
|
35
|
+
} = useSmartScan();
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-tab-content
|
|
40
|
+
style={{
|
|
41
|
+
flex: 1,
|
|
42
|
+
overflow: 'hidden',
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexDirection: 'column',
|
|
45
|
+
background: colors.bgPrimary,
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
{/* Top Bar - Controls */}
|
|
49
|
+
<div
|
|
50
|
+
style={{
|
|
51
|
+
background: colors.bgCard,
|
|
52
|
+
borderBottom: `1px solid ${colors.borderLight}`,
|
|
53
|
+
padding: '16px 24px',
|
|
54
|
+
boxShadow: `0 2px 4px ${colors.shadowSm}`,
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<div
|
|
58
|
+
style={{
|
|
59
|
+
display: 'flex',
|
|
60
|
+
alignItems: 'center',
|
|
61
|
+
gap: '24px',
|
|
62
|
+
flexWrap: 'wrap',
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<SmartScanHeader />
|
|
66
|
+
<ViewModeTabs
|
|
67
|
+
viewMode={viewMode}
|
|
68
|
+
setViewMode={setViewMode}
|
|
69
|
+
onSwitchToScan={() => setSelectedScan(null)}
|
|
70
|
+
onSwitchToList={() => {
|
|
71
|
+
setSelectedScan(null);
|
|
72
|
+
if (allScans.length === 0) {
|
|
73
|
+
loadAllScans();
|
|
74
|
+
}
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
{viewMode === 'scan' && (
|
|
78
|
+
<SmartScanControls
|
|
79
|
+
apiToken={apiToken}
|
|
80
|
+
setApiToken={setApiToken}
|
|
81
|
+
saveToken={saveToken}
|
|
82
|
+
loadingData={loadingData}
|
|
83
|
+
discoverMcpData={discoverMcpData}
|
|
84
|
+
discoveredServers={discoveredServers}
|
|
85
|
+
selectedServers={selectedServers}
|
|
86
|
+
setSelectedServers={setSelectedServers}
|
|
87
|
+
runScan={runScan}
|
|
88
|
+
scanning={scanning}
|
|
89
|
+
clearCache={clearCache}
|
|
90
|
+
clearingCache={clearingCache}
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Content based on view mode */}
|
|
97
|
+
{viewMode === 'scan' ? (
|
|
98
|
+
<ScanViewContent
|
|
99
|
+
discoveredServers={discoveredServers}
|
|
100
|
+
selectedServers={selectedServers}
|
|
101
|
+
setSelectedServers={setSelectedServers}
|
|
102
|
+
runScan={runScan}
|
|
103
|
+
scanning={scanning}
|
|
104
|
+
apiToken={apiToken}
|
|
105
|
+
error={error}
|
|
106
|
+
scanResults={scanResults}
|
|
107
|
+
scanResult={scanResult}
|
|
108
|
+
selectedScan={selectedScan}
|
|
109
|
+
loadingScanDetail={loadingScanDetail}
|
|
110
|
+
setSelectedScan={setSelectedScan}
|
|
111
|
+
loadScanDetail={loadScanDetail}
|
|
112
|
+
onViewScan={(scanData) => {
|
|
113
|
+
console.log('onViewScan - scanData:', scanData);
|
|
114
|
+
if (scanData.scan_id && apiToken) {
|
|
115
|
+
loadScanDetail(scanData.scan_id);
|
|
116
|
+
} else if (scanData.data && typeof scanData.data === 'object') {
|
|
117
|
+
const actualScan = scanData.data;
|
|
118
|
+
setSelectedScan({
|
|
119
|
+
...actualScan,
|
|
120
|
+
scan_id: scanData.scan_id || actualScan.id || actualScan.scan_id,
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
setSelectedScan(scanData);
|
|
124
|
+
}
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
) : (
|
|
128
|
+
<div
|
|
129
|
+
style={{
|
|
130
|
+
flex: 1,
|
|
131
|
+
overflowY: 'auto',
|
|
132
|
+
overflowX: 'hidden',
|
|
133
|
+
padding: '24px',
|
|
134
|
+
background: colors.bgPrimary,
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<ListViewContent
|
|
138
|
+
error={error}
|
|
139
|
+
loadingScans={loadingScans}
|
|
140
|
+
selectedScan={selectedScan}
|
|
141
|
+
loadingScanDetail={loadingScanDetail}
|
|
142
|
+
allScans={allScans}
|
|
143
|
+
setSelectedScan={setSelectedScan}
|
|
144
|
+
loadScanDetail={loadScanDetail}
|
|
145
|
+
onViewScan={(scanData) => {
|
|
146
|
+
console.log('onViewScan - scanData:', scanData);
|
|
147
|
+
const scanId = scanData.scan_id || scanData.id || scanData.hash;
|
|
148
|
+
const matchingScan = allScans.find(
|
|
149
|
+
(s) => s.data?.scan_id === scanId || s.data?.data?.scan_id === scanId
|
|
150
|
+
);
|
|
151
|
+
const serverName =
|
|
152
|
+
matchingScan?.serverName || scanData.serverName || 'Unknown Server';
|
|
153
|
+
|
|
154
|
+
if (scanData && scanData.data && typeof scanData.data === 'object') {
|
|
155
|
+
const actualScan = scanData.data;
|
|
156
|
+
setSelectedScan({
|
|
157
|
+
...actualScan,
|
|
158
|
+
scan_id: scanId || actualScan.id || actualScan.scan_id || actualScan.hash,
|
|
159
|
+
serverName: serverName,
|
|
160
|
+
});
|
|
161
|
+
} else if (scanData && typeof scanData === 'object') {
|
|
162
|
+
setSelectedScan({
|
|
163
|
+
...scanData,
|
|
164
|
+
scan_id: scanId || scanData.id || scanData.hash,
|
|
165
|
+
serverName: serverName,
|
|
166
|
+
});
|
|
167
|
+
} else {
|
|
168
|
+
console.warn('Invalid scanData structure:', scanData);
|
|
169
|
+
}
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export default SmartScan;
|