@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
@@ -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-CiCSDYf-.js"></script>
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(requestService, exportService, serializationLib, logger) {
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(securityDetectionService, serverManagementService, logger) {
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(requestService, exportService, serializationLib, logger);
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;