@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
package/ui/dist/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<title>MCP Shark</title>
|
|
8
8
|
<link rel="icon" type="image/png" href="/og-image.png" />
|
|
9
9
|
<link rel="apple-touch-icon" href="/og-image.png" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-D6zDrtMV.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-Cc-IUa83.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { Defaults } from '#core/constants/Defaults.js';
|
|
2
|
+
import { StatusCodes } from '#core/constants/StatusCodes.js';
|
|
3
|
+
import { RequestFilters } from '#core/models/RequestFilters.js';
|
|
4
|
+
import { buildAauthGraph } from '#core/services/security/aauthGraph.js';
|
|
5
|
+
import { parseAauthForPacket, summarizeAauth } from '#core/services/security/aauthParser.js';
|
|
6
|
+
import { detectHttpUpstreams, runAauthSelfTest } from '#core/services/security/aauthSelfTest.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Controller for AAuth-visibility HTTP endpoints.
|
|
10
|
+
*
|
|
11
|
+
* Every response describes what was OBSERVED on the wire. Nothing is
|
|
12
|
+
* cryptographically verified by mcp-shark.
|
|
13
|
+
*/
|
|
14
|
+
export class AauthController {
|
|
15
|
+
constructor(requestService, auditService, logger) {
|
|
16
|
+
this.requestService = requestService;
|
|
17
|
+
this.auditService = auditService;
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* GET /api/aauth/posture
|
|
23
|
+
* Summary of AAuth posture across captured traffic.
|
|
24
|
+
*/
|
|
25
|
+
getPosture(_req, res) {
|
|
26
|
+
try {
|
|
27
|
+
const filters = new RequestFilters({
|
|
28
|
+
limit: Defaults.EXPORT_LIMIT,
|
|
29
|
+
offset: Defaults.DEFAULT_OFFSET,
|
|
30
|
+
});
|
|
31
|
+
const packets = this.requestService.getRequests(filters);
|
|
32
|
+
const summary = summarizeAauth(packets);
|
|
33
|
+
const total = summary.total || 0;
|
|
34
|
+
const signedRatio = total > 0 ? summary.counts.signed / total : 0;
|
|
35
|
+
|
|
36
|
+
res.json({
|
|
37
|
+
observed: true,
|
|
38
|
+
verified: false,
|
|
39
|
+
total_packets: total,
|
|
40
|
+
counts: summary.counts,
|
|
41
|
+
signed_ratio: signedRatio,
|
|
42
|
+
unique_agents: summary.unique_agents,
|
|
43
|
+
unique_missions: summary.unique_missions,
|
|
44
|
+
note: 'mcp-shark records AAuth signals as observed only. No signature verification is performed.',
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
this.logger.error({ error: error.message }, 'Error in AAuth posture');
|
|
48
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
49
|
+
error: 'Failed to compute AAuth posture',
|
|
50
|
+
details: error.message,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* GET /api/aauth/missions
|
|
57
|
+
* List unique mission IDs observed, with packet counts and time spans.
|
|
58
|
+
*/
|
|
59
|
+
getMissions(_req, res) {
|
|
60
|
+
try {
|
|
61
|
+
const filters = new RequestFilters({
|
|
62
|
+
limit: Defaults.EXPORT_LIMIT,
|
|
63
|
+
offset: Defaults.DEFAULT_OFFSET,
|
|
64
|
+
});
|
|
65
|
+
const packets = this.requestService.getRequests(filters);
|
|
66
|
+
const missions = new Map();
|
|
67
|
+
|
|
68
|
+
for (const p of packets) {
|
|
69
|
+
const mission = p?.aauth?.mission;
|
|
70
|
+
if (!mission) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const existing = missions.get(mission) || {
|
|
74
|
+
mission_id: mission,
|
|
75
|
+
packet_count: 0,
|
|
76
|
+
first_frame: p.frame_number,
|
|
77
|
+
last_frame: p.frame_number,
|
|
78
|
+
first_ts_iso: p.timestamp_iso,
|
|
79
|
+
last_ts_iso: p.timestamp_iso,
|
|
80
|
+
agents: new Set(),
|
|
81
|
+
servers: new Set(),
|
|
82
|
+
};
|
|
83
|
+
existing.packet_count += 1;
|
|
84
|
+
if (p.frame_number < existing.first_frame) {
|
|
85
|
+
existing.first_frame = p.frame_number;
|
|
86
|
+
existing.first_ts_iso = p.timestamp_iso;
|
|
87
|
+
}
|
|
88
|
+
if (p.frame_number > existing.last_frame) {
|
|
89
|
+
existing.last_frame = p.frame_number;
|
|
90
|
+
existing.last_ts_iso = p.timestamp_iso;
|
|
91
|
+
}
|
|
92
|
+
if (p.aauth?.agent) {
|
|
93
|
+
existing.agents.add(p.aauth.agent);
|
|
94
|
+
}
|
|
95
|
+
if (p.remote_address) {
|
|
96
|
+
existing.servers.add(p.remote_address);
|
|
97
|
+
}
|
|
98
|
+
missions.set(mission, existing);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = [...missions.values()].map((m) => ({
|
|
102
|
+
...m,
|
|
103
|
+
agents: [...m.agents],
|
|
104
|
+
servers: [...m.servers],
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
res.json({ observed: true, verified: false, missions: result });
|
|
108
|
+
} catch (error) {
|
|
109
|
+
this.logger.error({ error: error.message }, 'Error in AAuth missions');
|
|
110
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
111
|
+
error: 'Failed to compute AAuth missions',
|
|
112
|
+
details: error.message,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* GET /api/aauth/graph
|
|
119
|
+
* Knowledge-graph of AAuth signals observed across captured traffic.
|
|
120
|
+
*
|
|
121
|
+
* Returns nodes (agents, missions, resources, signing algs, access modes)
|
|
122
|
+
* and edges (calls, pursues, targets, signs-with, requires) plus a
|
|
123
|
+
* categories list. Suitable for direct rendering in a force-layout.
|
|
124
|
+
*/
|
|
125
|
+
getGraph(_req, res) {
|
|
126
|
+
try {
|
|
127
|
+
const filters = new RequestFilters({
|
|
128
|
+
limit: Defaults.EXPORT_LIMIT,
|
|
129
|
+
offset: Defaults.DEFAULT_OFFSET,
|
|
130
|
+
});
|
|
131
|
+
const packets = this.requestService.getRequests(filters);
|
|
132
|
+
const graph = buildAauthGraph(packets);
|
|
133
|
+
res.json({ observed: true, verified: false, ...graph });
|
|
134
|
+
} catch (error) {
|
|
135
|
+
this.logger.error({ error: error.message }, 'Error building AAuth graph');
|
|
136
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
137
|
+
error: 'Failed to build AAuth graph',
|
|
138
|
+
details: error.message,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* GET /api/aauth/upstreams
|
|
145
|
+
* Lists the HTTP MCP upstreams currently configured for mcp-shark — used by
|
|
146
|
+
* the UI to show "we'll target N detected upstreams" before running a self
|
|
147
|
+
* test.
|
|
148
|
+
*/
|
|
149
|
+
getUpstreams(_req, res) {
|
|
150
|
+
try {
|
|
151
|
+
const upstreams = detectHttpUpstreams();
|
|
152
|
+
res.json({ count: upstreams.length, upstreams });
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.logger.error({ error: error.message }, 'Error reading mcp-shark upstreams');
|
|
155
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
156
|
+
error: 'Failed to read upstreams',
|
|
157
|
+
details: error.message,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* GET /api/aauth/node/:category/:id
|
|
164
|
+
* Returns the recently observed packets backing a single graph node.
|
|
165
|
+
* This is what powers the "node detail" side panel on the AAuth Explorer.
|
|
166
|
+
*/
|
|
167
|
+
getNodePackets(req, res) {
|
|
168
|
+
try {
|
|
169
|
+
const { category, id } = req.params;
|
|
170
|
+
if (!category || !id) {
|
|
171
|
+
res.status(StatusCodes.BAD_REQUEST).json({ error: 'category and id are required' });
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const decodedId = decodeURIComponent(id);
|
|
175
|
+
const filters = new RequestFilters({
|
|
176
|
+
limit: Defaults.EXPORT_LIMIT,
|
|
177
|
+
offset: Defaults.DEFAULT_OFFSET,
|
|
178
|
+
});
|
|
179
|
+
const packets = this.requestService.getRequests(filters);
|
|
180
|
+
|
|
181
|
+
const matching = [];
|
|
182
|
+
for (const p of packets) {
|
|
183
|
+
const aauth = parseAauthForPacket(p);
|
|
184
|
+
if (matchesNode(p, aauth, category, decodedId)) {
|
|
185
|
+
matching.push({
|
|
186
|
+
frame_number: p.frame_number,
|
|
187
|
+
timestamp_iso: p.timestamp_iso,
|
|
188
|
+
direction: p.direction,
|
|
189
|
+
method: p.method,
|
|
190
|
+
url: p.url,
|
|
191
|
+
host: p.host,
|
|
192
|
+
status_code: p.status_code,
|
|
193
|
+
jsonrpc_method: p.jsonrpc_method,
|
|
194
|
+
posture: aauth.posture,
|
|
195
|
+
agent: aauth.agent,
|
|
196
|
+
mission: aauth.mission,
|
|
197
|
+
sig_alg: aauth.sig_alg,
|
|
198
|
+
sig_keyid_short: aauth.sig_keyid_short,
|
|
199
|
+
requirement: aauth.requirement,
|
|
200
|
+
});
|
|
201
|
+
if (matching.length >= 100) {
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
res.json({
|
|
208
|
+
category,
|
|
209
|
+
id: decodedId,
|
|
210
|
+
packet_count: matching.length,
|
|
211
|
+
packets: matching,
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
this.logger.error({ error: error.message }, 'Error fetching node packets');
|
|
215
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
216
|
+
error: 'Failed to fetch node packets',
|
|
217
|
+
details: error.message,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* POST /api/aauth/self-test
|
|
224
|
+
* Generate synthetic AAuth-shaped traffic and write it through the audit
|
|
225
|
+
* pipeline so it appears identically to live captured traffic. Auto-detects
|
|
226
|
+
* configured HTTP upstreams; falls back to a placeholder triple. Body is
|
|
227
|
+
* optional and accepts `{ rounds: number }`.
|
|
228
|
+
*/
|
|
229
|
+
runSelfTest(req, res) {
|
|
230
|
+
try {
|
|
231
|
+
const result = runAauthSelfTest(
|
|
232
|
+
{ auditService: this.auditService, logger: this.logger },
|
|
233
|
+
{ rounds: req.body?.rounds }
|
|
234
|
+
);
|
|
235
|
+
res.json({ observed: true, verified: false, ...result });
|
|
236
|
+
} catch (error) {
|
|
237
|
+
this.logger.error({ error: error.message }, 'Error running AAuth self-test');
|
|
238
|
+
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
|
|
239
|
+
error: 'Failed to run AAuth self-test',
|
|
240
|
+
details: error.message,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function matchesNode(packet, aauth, category, id) {
|
|
247
|
+
switch (category) {
|
|
248
|
+
case 'agent':
|
|
249
|
+
return aauth.agent === id;
|
|
250
|
+
case 'mission':
|
|
251
|
+
return aauth.mission === id;
|
|
252
|
+
case 'resource': {
|
|
253
|
+
const host =
|
|
254
|
+
packet?.host ||
|
|
255
|
+
packet?.remote_address ||
|
|
256
|
+
(typeof packet?.url === 'string'
|
|
257
|
+
? (() => {
|
|
258
|
+
try {
|
|
259
|
+
return new URL(packet.url).host;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
})()
|
|
264
|
+
: null);
|
|
265
|
+
return host === id;
|
|
266
|
+
}
|
|
267
|
+
case 'signing':
|
|
268
|
+
return aauth.sig_alg === id;
|
|
269
|
+
case 'access': {
|
|
270
|
+
if (!aauth.requirement) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
const m = aauth.requirement.match(/mode\s*=\s*"?([A-Za-z0-9_-]+)"?/i);
|
|
274
|
+
return m && m[1] === id;
|
|
275
|
+
}
|
|
276
|
+
default:
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -8,11 +8,18 @@ import { RequestFilters } from '#core/models/RequestFilters.js';
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export class RequestController {
|
|
11
|
-
constructor(
|
|
11
|
+
constructor(
|
|
12
|
+
requestService,
|
|
13
|
+
exportService,
|
|
14
|
+
serializationLib,
|
|
15
|
+
logger,
|
|
16
|
+
trafficToxicFlowService = null
|
|
17
|
+
) {
|
|
12
18
|
this.requestService = requestService;
|
|
13
19
|
this.exportService = exportService;
|
|
14
20
|
this.serializationLib = serializationLib;
|
|
15
21
|
this.logger = logger;
|
|
22
|
+
this.trafficToxicFlowService = trafficToxicFlowService;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
/**
|
|
@@ -43,6 +50,9 @@ export class RequestController {
|
|
|
43
50
|
endTime: reqQuery.endTime || null,
|
|
44
51
|
limit: reqQuery.limit,
|
|
45
52
|
offset: reqQuery.offset,
|
|
53
|
+
aauthPosture: reqQuery.aauthPosture ? String(reqQuery.aauthPosture).trim() : null,
|
|
54
|
+
aauthAgent: reqQuery.aauthAgent ? String(reqQuery.aauthAgent).trim() : null,
|
|
55
|
+
aauthMission: reqQuery.aauthMission ? String(reqQuery.aauthMission).trim() : null,
|
|
46
56
|
});
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -90,6 +100,7 @@ export class RequestController {
|
|
|
90
100
|
clearRequests(_req, res) {
|
|
91
101
|
try {
|
|
92
102
|
const result = this.requestService.clearRequests();
|
|
103
|
+
this.trafficToxicFlowService?.clear();
|
|
93
104
|
res.json({
|
|
94
105
|
success: true,
|
|
95
106
|
message: `Cleared ${result.clearedTables.length} table(s): ${result.clearedTables.join(', ')}. All captured traffic has been cleared.`,
|
|
@@ -5,10 +5,16 @@ import { handleError, handleValidationError } from '../utils/errorHandler.js';
|
|
|
5
5
|
* Controller for Security Detection and Findings HTTP endpoints
|
|
6
6
|
*/
|
|
7
7
|
export class SecurityFindingsController {
|
|
8
|
-
constructor(
|
|
8
|
+
constructor(
|
|
9
|
+
securityDetectionService,
|
|
10
|
+
serverManagementService,
|
|
11
|
+
logger,
|
|
12
|
+
trafficToxicFlowService = null
|
|
13
|
+
) {
|
|
9
14
|
this.securityService = securityDetectionService;
|
|
10
15
|
this.serverManagementService = serverManagementService;
|
|
11
16
|
this.logger = logger;
|
|
17
|
+
this.trafficToxicFlowService = trafficToxicFlowService;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
/**
|
|
@@ -198,6 +204,7 @@ export class SecurityFindingsController {
|
|
|
198
204
|
clearFindings = (_req, res) => {
|
|
199
205
|
try {
|
|
200
206
|
const deletedCount = this.securityService.clearAllFindings();
|
|
207
|
+
this.trafficToxicFlowService?.clear();
|
|
201
208
|
return res.json({
|
|
202
209
|
success: true,
|
|
203
210
|
message: `Cleared ${deletedCount} finding${deletedCount !== 1 ? 's' : ''}`,
|
|
@@ -229,4 +236,42 @@ export class SecurityFindingsController {
|
|
|
229
236
|
handleError(error, res, this.logger, 'Error deleting scan findings');
|
|
230
237
|
}
|
|
231
238
|
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Cross-server toxic flows from observed tools/list traffic (live registry + last recompute).
|
|
242
|
+
*/
|
|
243
|
+
getTrafficToxicFlows = (_req, res) => {
|
|
244
|
+
try {
|
|
245
|
+
if (!this.trafficToxicFlowService) {
|
|
246
|
+
return res.status(StatusCodes.SERVICE_UNAVAILABLE).json({
|
|
247
|
+
success: false,
|
|
248
|
+
error: 'Traffic toxic flow service is not available',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return res.json(this.trafficToxicFlowService.getSnapshot());
|
|
252
|
+
} catch (error) {
|
|
253
|
+
handleError(error, res, this.logger, 'Error getting traffic toxic flows');
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Rebuild toxic-flow model from stored response packets (batch replay).
|
|
259
|
+
*/
|
|
260
|
+
replayTrafficToxicFlows = (_req, res) => {
|
|
261
|
+
try {
|
|
262
|
+
if (!this.trafficToxicFlowService) {
|
|
263
|
+
return res.status(StatusCodes.SERVICE_UNAVAILABLE).json({
|
|
264
|
+
success: false,
|
|
265
|
+
error: 'Traffic toxic flow service is not available',
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const stats = this.trafficToxicFlowService.rebuildFromDatabase();
|
|
269
|
+
return res.json({
|
|
270
|
+
...this.trafficToxicFlowService.getSnapshot(),
|
|
271
|
+
replay: stats,
|
|
272
|
+
});
|
|
273
|
+
} catch (error) {
|
|
274
|
+
handleError(error, res, this.logger, 'Error replaying traffic toxic flows');
|
|
275
|
+
}
|
|
276
|
+
};
|
|
232
277
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AauthController } from '../controllers/AauthController.js';
|
|
2
|
+
|
|
3
|
+
export function createAauthRoutes(container) {
|
|
4
|
+
const requestService = container.getService('request');
|
|
5
|
+
const auditService = container.getService('audit');
|
|
6
|
+
const logger = container.getLibrary('logger');
|
|
7
|
+
|
|
8
|
+
const controller = new AauthController(requestService, auditService, logger);
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
getPosture: (req, res) => controller.getPosture(req, res),
|
|
12
|
+
getMissions: (req, res) => controller.getMissions(req, res),
|
|
13
|
+
getGraph: (req, res) => controller.getGraph(req, res),
|
|
14
|
+
getUpstreams: (req, res) => controller.getUpstreams(req, res),
|
|
15
|
+
getNodePackets: (req, res) => controller.getNodePackets(req, res),
|
|
16
|
+
runSelfTest: (req, res) => controller.runSelfTest(req, res),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -5,8 +5,15 @@ export function createRequestsRoutes(container) {
|
|
|
5
5
|
const exportService = container.getService('export');
|
|
6
6
|
const serializationLib = container.getLibrary('serialization');
|
|
7
7
|
const logger = container.getLibrary('logger');
|
|
8
|
+
const trafficToxicFlowService = container.getService('trafficToxicFlow');
|
|
8
9
|
|
|
9
|
-
const controller = new RequestController(
|
|
10
|
+
const controller = new RequestController(
|
|
11
|
+
requestService,
|
|
12
|
+
exportService,
|
|
13
|
+
serializationLib,
|
|
14
|
+
logger,
|
|
15
|
+
trafficToxicFlowService
|
|
16
|
+
);
|
|
10
17
|
|
|
11
18
|
const router = {};
|
|
12
19
|
|
|
@@ -13,10 +13,12 @@ export function createSecurityRoutes(container) {
|
|
|
13
13
|
const yaraEngineService = container.getService('yaraEngine');
|
|
14
14
|
const logger = container.getLibrary('logger');
|
|
15
15
|
|
|
16
|
+
const trafficToxicFlowService = container.getService('trafficToxicFlow');
|
|
16
17
|
const findingsController = new SecurityFindingsController(
|
|
17
18
|
securityDetectionService,
|
|
18
19
|
serverManagementService,
|
|
19
|
-
logger
|
|
20
|
+
logger,
|
|
21
|
+
trafficToxicFlowService
|
|
20
22
|
);
|
|
21
23
|
|
|
22
24
|
const sourcesController = new RuleSourcesController(
|
|
@@ -40,6 +42,8 @@ export function createSecurityRoutes(container) {
|
|
|
40
42
|
router.getScanHistory = findingsController.getScanHistory;
|
|
41
43
|
router.clearFindings = findingsController.clearFindings;
|
|
42
44
|
router.deleteScanFindings = findingsController.deleteScanFindings;
|
|
45
|
+
router.getTrafficToxicFlows = findingsController.getTrafficToxicFlows;
|
|
46
|
+
router.replayTrafficToxicFlows = findingsController.replayTrafficToxicFlows;
|
|
43
47
|
|
|
44
48
|
// YARA engine (RuleSourcesController)
|
|
45
49
|
router.getEngineStatus = sourcesController.getEngineStatus;
|