@oneuptime/common 13.0.4 → 13.0.5

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 (673) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/RumApplication.ts +1 -1
  3. package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
  4. package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
  5. package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
  6. package/Server/API/AIInvestigationAPI.ts +484 -24
  7. package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
  8. package/Server/API/SecurityEventConnectionAPI.ts +250 -0
  9. package/Server/API/TelemetryAPI.ts +402 -265
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Middleware/TelemetryIngest.ts +101 -6
  13. package/Server/Services/ExceptionAggregationService.ts +7 -16
  14. package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
  15. package/Server/Services/Index.ts +4 -0
  16. package/Server/Services/InventoryItemService.ts +28 -0
  17. package/Server/Services/LogAggregationService.ts +17 -23
  18. package/Server/Services/MetricAggregationService.ts +4 -14
  19. package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
  20. package/Server/Services/SecurityEventConnectionService.ts +538 -0
  21. package/Server/Services/TraceAggregationService.ts +77 -28
  22. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  23. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
  24. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
  25. package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
  26. package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
  27. package/Server/Utils/AI/Toolbox/Index.ts +38 -0
  28. package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
  30. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
  31. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
  32. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
  33. package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
  34. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
  35. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
  36. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
  37. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
  38. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
  39. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
  40. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
  41. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
  42. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
  43. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
  44. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
  45. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
  46. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
  47. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
  48. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
  49. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
  50. package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
  51. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
  52. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
  53. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
  55. package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
  56. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
  57. package/Server/Utils/StartServer.ts +10 -0
  58. package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
  59. package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
  60. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
  61. package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
  62. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
  63. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
  64. package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
  65. package/Server/Utils/Telemetry.ts +18 -0
  66. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
  67. package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
  68. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
  69. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
  70. package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
  71. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
  72. package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
  73. package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
  74. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
  75. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
  76. package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
  77. package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
  78. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
  79. package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
  80. package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
  81. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
  82. package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
  83. package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
  84. package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
  85. package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
  86. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
  87. package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
  88. package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
  89. package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
  90. package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
  91. package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
  92. package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
  93. package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
  94. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
  95. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
  96. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
  97. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
  98. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
  99. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
  100. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
  101. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
  102. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
  103. package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
  104. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
  105. package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
  106. package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
  107. package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
  108. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
  109. package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
  110. package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
  111. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
  112. package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
  113. package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
  114. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
  115. package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
  116. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
  118. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
  119. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
  120. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
  121. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
  122. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
  123. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
  124. package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
  125. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
  126. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
  127. package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
  128. package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
  129. package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
  130. package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
  131. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
  132. package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
  133. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
  134. package/Tests/Models/InventoryItemNaming.test.ts +132 -16
  135. package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
  136. package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
  137. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
  138. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
  139. package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
  140. package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
  141. package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
  142. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
  143. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
  144. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
  145. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  146. package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
  147. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
  148. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
  149. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
  150. package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
  151. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
  152. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
  153. package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
  154. package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
  155. package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
  156. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
  157. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
  158. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
  159. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  160. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  161. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  178. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  188. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  189. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  190. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  191. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  192. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  193. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +348 -2
  194. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  195. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  196. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  197. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  198. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  199. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  200. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  201. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  202. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  203. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  204. package/Tests/UI/Components/Card.test.tsx +312 -1
  205. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  206. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  207. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  208. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  209. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  210. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  211. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  212. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  213. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  214. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  215. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  216. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  217. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  218. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  219. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  220. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  221. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  222. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  223. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  224. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  225. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  226. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  227. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  228. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  229. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  230. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  231. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  232. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  233. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  234. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  235. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  236. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  237. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  238. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  239. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  240. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  241. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  242. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  243. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  244. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  245. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  246. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  247. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  248. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  249. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  250. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  251. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  252. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  253. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  254. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  255. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  256. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  257. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  258. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  259. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  260. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  261. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  262. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  263. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  264. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  265. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  266. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  267. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  268. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  269. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  270. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  271. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  272. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  273. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  274. package/Types/AI/AIChatTypes.ts +8 -0
  275. package/Types/AI/InvestigationEvidence.ts +96 -0
  276. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  277. package/Types/Permission.ts +2 -2
  278. package/Types/Rum/SessionReplay.ts +109 -0
  279. package/Types/Rum/SessionReplayApi.ts +31 -0
  280. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  281. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  282. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  283. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  284. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  285. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  286. package/Types/Telemetry/EntityType.ts +14 -0
  287. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  288. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  289. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  290. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  291. package/UI/Components/Card/Card.tsx +118 -83
  292. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  293. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  294. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  295. package/UI/Components/Detail/Detail.tsx +137 -22
  296. package/UI/Components/ErrorBoundary.tsx +38 -0
  297. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  298. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  299. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  300. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  301. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  302. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  303. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  304. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  305. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  306. package/UI/Components/LogsViewer/types.ts +6 -0
  307. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  308. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  309. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  310. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  311. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  312. package/UI/Components/ModelList/ModelList.tsx +33 -8
  313. package/UI/Components/Page/ModelPage.tsx +119 -54
  314. package/UI/Components/Table/Table.tsx +8 -0
  315. package/UI/Components/Table/TableRow.tsx +13 -5
  316. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  317. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  318. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  319. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  320. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  321. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  322. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  323. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  324. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  325. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  326. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  327. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  328. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  329. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  330. package/UI/Components/TelemetryViewer/types.ts +19 -0
  331. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  332. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  333. package/UI/Utils/Clipboard.ts +68 -2
  334. package/UI/Utils/Navigation.ts +17 -2
  335. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  336. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  337. package/Utils/AI/InvestigationReport.ts +1789 -0
  338. package/Utils/API.ts +90 -1
  339. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  340. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  341. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  342. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  343. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  344. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  345. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  346. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  347. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  348. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  349. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  350. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  351. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  352. package/Utils/Telemetry/NetworkHost.ts +43 -0
  353. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  354. package/Utils/Traces/CriticalPath.ts +10 -4
  355. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  356. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  357. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  358. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  359. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  360. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  361. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  362. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  363. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  364. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  365. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  366. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  367. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  368. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  369. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  370. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  371. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  372. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  373. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  374. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  375. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  376. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  377. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  378. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  379. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  380. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  381. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  382. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  383. package/build/dist/Server/Services/Index.js +4 -0
  384. package/build/dist/Server/Services/Index.js.map +1 -1
  385. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  386. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  387. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  388. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  389. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  390. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  392. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  393. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  394. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  395. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  396. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  397. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  398. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  399. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  400. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  401. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  402. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  403. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  404. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  405. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  406. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  407. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  408. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  410. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  411. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  412. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  413. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  414. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  415. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  416. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  417. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  418. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  419. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  420. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  421. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  422. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  423. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  424. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  456. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  457. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  458. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  460. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  461. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  462. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  463. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  464. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  465. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  466. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  467. package/build/dist/Server/Utils/StartServer.js +10 -0
  468. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  469. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  470. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  471. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  472. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  473. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  474. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  475. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  476. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  477. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  478. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  480. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  481. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  482. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry.js +11 -5
  484. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  485. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  486. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  487. package/build/dist/Types/Permission.js +2 -2
  488. package/build/dist/Types/Permission.js.map +1 -1
  489. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  490. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  491. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  492. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  493. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  494. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  495. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  496. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  497. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  498. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  499. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  500. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  501. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  502. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  503. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  504. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  505. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  506. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  507. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  508. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  509. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  510. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  511. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  512. package/build/dist/UI/Components/Card/Card.js +34 -17
  513. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  514. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  515. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  516. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  517. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  518. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  519. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  520. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  521. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  522. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  523. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  524. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  525. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  526. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  527. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  528. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  529. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  530. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  531. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  532. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  533. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  534. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  535. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  536. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  537. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  539. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  540. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  541. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  543. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  544. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  545. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  546. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  547. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  548. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  549. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  550. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  551. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  552. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  553. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  554. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  555. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  556. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  557. package/build/dist/UI/Components/Table/Table.js +3 -1
  558. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  559. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  560. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  561. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  562. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  563. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  564. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  565. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  566. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  567. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  568. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  569. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  570. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  571. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  572. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  573. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  574. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  575. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  576. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  578. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  580. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  582. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  583. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  584. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  585. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  586. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  587. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  588. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  589. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  590. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  591. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  592. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  594. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  595. package/build/dist/UI/Utils/Clipboard.js +59 -2
  596. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  597. package/build/dist/UI/Utils/Navigation.js +11 -2
  598. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  599. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  600. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  601. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  602. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  603. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  604. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  605. package/build/dist/Utils/API.js +25 -0
  606. package/build/dist/Utils/API.js.map +1 -1
  607. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  608. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  609. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  610. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  611. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  612. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  613. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  614. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  615. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  616. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  617. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  618. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  619. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  620. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  621. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  622. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  623. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  624. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  626. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  627. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  628. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  629. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  630. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  631. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  632. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  633. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  634. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  635. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  636. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  637. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  638. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  639. package/package.json +1 -1
  640. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  641. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  642. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  643. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  644. package/UI/Components/GanttChart/Index.tsx +0 -90
  645. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  646. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  647. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  648. package/UI/Components/GanttChart/Rows.tsx +0 -59
  649. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  650. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  651. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  652. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  653. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  654. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  655. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  656. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  657. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  658. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  659. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  660. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  661. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  662. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  663. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  664. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  665. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  666. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  667. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  669. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  671. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  673. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -0,0 +1,1240 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+ import AwsSecurityHubClient, {
3
+ AWS_SECURITY_HUB_DEFAULT_PAGE_SIZE,
4
+ AWS_SECURITY_HUB_MAX_PAGE_SIZE,
5
+ AwsSecurityHubFailureKind,
6
+ AwsSecurityHubFindingCount,
7
+ AwsSecurityHubFindingsPage,
8
+ AwsSecurityHubHttpError,
9
+ AwsSigV4SignResult,
10
+ } from "../../../../../../Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient";
11
+ import {
12
+ DataSourceHttpRequest,
13
+ DataSourceHttpResponse,
14
+ } from "../../../../../../Server/Utils/DataSource/HttpFetch";
15
+ import APIException from "../../../../../../Types/Exception/ApiException";
16
+ import BadDataException from "../../../../../../Types/Exception/BadDataException";
17
+ import { JSONObject, JSONValue } from "../../../../../../Types/JSON";
18
+
19
+ /*
20
+ * The Security Hub client contract as the connector depends on it: the
21
+ * hand-rolled Signature Version 4 signer checked against AWS's published
22
+ * test-suite vectors, the GetFindings request (creation-time filter,
23
+ * ascending order, page size, NextToken), the failure taxonomy per status
24
+ * and AWS error code, and — above everything — that no credential can
25
+ * leave through an error message. The transport is the injected seam;
26
+ * nothing here touches the network.
27
+ */
28
+
29
+ const REGION: string = "us-east-1";
30
+ const ACCESS_KEY_ID: string = "AKIAIOSFODNN7EXAMPLE";
31
+ const SECRET_ACCESS_KEY: string = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
32
+ const SESSION_TOKEN: string =
33
+ "FwoGZXIvYXdzEBYaDHNlc3Npb24tdG9rZW4tZm9yLXRlc3RzLW9ubHktbm90LXJlYWwtdmFsdWU=";
34
+ const START: Date = new Date("2026-09-12T10:00:00.000Z");
35
+ const END: Date = new Date("2026-09-13T10:00:00.000Z");
36
+ const CLOCK: Date = new Date("2026-09-13T10:05:07.123Z");
37
+
38
+ /*
39
+ * AWS Signature Version 4 test suite (the vectors AWS publishes for
40
+ * signer implementations): access key AKIDEXAMPLE, secret
41
+ * wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY, region us-east-1, service
42
+ * "service", date 20150830T123600Z, host example.amazonaws.com.
43
+ */
44
+ const SUITE_ACCESS_KEY_ID: string = "AKIDEXAMPLE";
45
+ const SUITE_SECRET: string = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
46
+ const SUITE_DATE: Date = new Date("2015-08-30T12:36:00.000Z");
47
+ const SUITE_HOST: string = "example.amazonaws.com";
48
+ const SUITE_STS_TOKEN: string =
49
+ "AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==";
50
+ const EMPTY_PAYLOAD_HASH: string =
51
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
52
+
53
+ type Responder = (
54
+ request: DataSourceHttpRequest,
55
+ ) => DataSourceHttpResponse | Promise<DataSourceHttpResponse>;
56
+
57
+ interface Harness {
58
+ requests: Array<DataSourceHttpRequest>;
59
+ client: AwsSecurityHubClient;
60
+ }
61
+
62
+ function ok(body: JSONObject): DataSourceHttpResponse {
63
+ return {
64
+ statusCode: 200,
65
+ bodyText: JSON.stringify(body),
66
+ bodyJson: body,
67
+ headers: {},
68
+ };
69
+ }
70
+
71
+ function status(
72
+ code: number,
73
+ body: string,
74
+ headers: Record<string, string> = {},
75
+ ): DataSourceHttpResponse {
76
+ let bodyJson: unknown = undefined;
77
+
78
+ try {
79
+ bodyJson = JSON.parse(body);
80
+ } catch {
81
+ bodyJson = undefined;
82
+ }
83
+
84
+ return { statusCode: code, bodyText: body, bodyJson, headers };
85
+ }
86
+
87
+ // The GuardDuty finding of the Security Hub integration reference, trimmed.
88
+ function finding(id: string): JSONObject {
89
+ return {
90
+ SchemaVersion: "2018-10-08",
91
+ Id: `arn:aws:guardduty:us-east-1:193043430472:detector/d4b040365221be2b54a6264dc9a4bc64/finding/${id}`,
92
+ ProductArn: "arn:aws:securityhub:us-east-1::product/aws/guardduty",
93
+ GeneratorId:
94
+ "arn:aws:guardduty:us-east-1:193043430472:detector/d4b040365221be2b54a6264dc9a4bc64",
95
+ AwsAccountId: "193043430472",
96
+ Types: ["TTPs/Initial Access/UnauthorizedAccess:EC2-SSHBruteForce"],
97
+ CreatedAt: "2026-09-12T12:34:34.146Z",
98
+ UpdatedAt: "2026-09-12T12:34:34.146Z",
99
+ Severity: { Label: "MEDIUM", Normalized: 40 },
100
+ Title: "199.241.229.197 is performing SSH brute force attacks.",
101
+ Resources: [
102
+ {
103
+ Type: "AwsEc2Instance",
104
+ Id: "arn:aws:ec2:us-east-1:193043430472:instance/i-0c10c2c7863d1a356",
105
+ Region: "us-east-1",
106
+ },
107
+ ],
108
+ Workflow: { Status: "NEW" },
109
+ RecordState: "ACTIVE",
110
+ };
111
+ }
112
+
113
+ function awsError(type: string, message: string): string {
114
+ return JSON.stringify({ __type: type, Message: message });
115
+ }
116
+
117
+ /*
118
+ * Routes findings requests to scripted responders, consumed in order so a
119
+ * test can script a sequence of pages or failures.
120
+ */
121
+ function buildHarness(options: {
122
+ responders?: Array<Responder>;
123
+ requestTimeoutInMs?: number;
124
+ sessionToken?: string;
125
+ region?: string;
126
+ }): Harness {
127
+ const requests: Array<DataSourceHttpRequest> = [];
128
+ const responders: Array<Responder> = [...(options.responders || [])];
129
+
130
+ const client: AwsSecurityHubClient = new AwsSecurityHubClient({
131
+ region: options.region || REGION,
132
+ accessKeyId: ACCESS_KEY_ID,
133
+ secretAccessKey: SECRET_ACCESS_KEY,
134
+ sessionToken: options.sessionToken,
135
+ requestTimeoutInMs: options.requestTimeoutInMs || 20000,
136
+ now: (): Date => {
137
+ return CLOCK;
138
+ },
139
+ transport: async (
140
+ request: DataSourceHttpRequest,
141
+ ): Promise<DataSourceHttpResponse> => {
142
+ requests.push(request);
143
+
144
+ const responder: Responder | undefined = responders.shift();
145
+
146
+ if (!responder) {
147
+ throw new Error(`Unexpected request to ${request.url}`);
148
+ }
149
+
150
+ return responder(request);
151
+ },
152
+ });
153
+
154
+ return { requests, client };
155
+ }
156
+
157
+ function parsedBody(request: DataSourceHttpRequest): JSONObject {
158
+ expect(typeof request.body).toBe("string");
159
+
160
+ return JSON.parse(request.body as string) as JSONObject;
161
+ }
162
+
163
+ async function expectRejection(
164
+ promise: Promise<unknown>,
165
+ ): Promise<APIException> {
166
+ try {
167
+ await promise;
168
+ } catch (error) {
169
+ expect(error).toBeInstanceOf(APIException);
170
+ return error as APIException;
171
+ }
172
+
173
+ throw new Error("Expected the call to reject.");
174
+ }
175
+
176
+ describe("AwsSecurityHubClient", () => {
177
+ describe("construction and validation", () => {
178
+ test("accepts documented Region codes and refuses anything that is not one", () => {
179
+ for (const region of [
180
+ "us-east-1",
181
+ "us-gov-west-1",
182
+ "ap-southeast-3",
183
+ "eu-central-2",
184
+ "il-central-1",
185
+ "cn-north-1",
186
+ ]) {
187
+ expect(() => {
188
+ AwsSecurityHubClient.validateRegion(region);
189
+ }).not.toThrow();
190
+ }
191
+
192
+ for (const region of [
193
+ "",
194
+ "US-EAST-1",
195
+ "us-east",
196
+ "us east 1",
197
+ "us-east-1/../x",
198
+ "securityhub.us-east-1.amazonaws.com",
199
+ ]) {
200
+ expect(() => {
201
+ AwsSecurityHubClient.validateRegion(region);
202
+ }).toThrow(BadDataException);
203
+ }
204
+ });
205
+
206
+ test("applies the IAM access key id shape", () => {
207
+ expect(() => {
208
+ AwsSecurityHubClient.validateAccessKeyId(ACCESS_KEY_ID);
209
+ }).not.toThrow();
210
+ expect(() => {
211
+ AwsSecurityHubClient.validateAccessKeyId(` ${ACCESS_KEY_ID} `);
212
+ }).not.toThrow();
213
+ expect(() => {
214
+ AwsSecurityHubClient.validateAccessKeyId("short");
215
+ }).toThrow(BadDataException);
216
+ expect(() => {
217
+ AwsSecurityHubClient.validateAccessKeyId("AKIA/IOSFODNN7EXAMPLE");
218
+ }).toThrow(BadDataException);
219
+ expect(() => {
220
+ AwsSecurityHubClient.validateAccessKeyId("");
221
+ }).toThrow(BadDataException);
222
+ });
223
+
224
+ test("requires a secret access key and rejects a bad region before anything is contacted", () => {
225
+ expect(() => {
226
+ return new AwsSecurityHubClient({
227
+ region: REGION,
228
+ accessKeyId: ACCESS_KEY_ID,
229
+ secretAccessKey: "",
230
+ transport: (): Promise<DataSourceHttpResponse> => {
231
+ throw new Error("must not be called");
232
+ },
233
+ });
234
+ }).toThrow("Secret access key is required.");
235
+
236
+ expect(() => {
237
+ return new AwsSecurityHubClient({
238
+ region: "nowhere",
239
+ accessKeyId: ACCESS_KEY_ID,
240
+ secretAccessKey: SECRET_ACCESS_KEY,
241
+ transport: (): Promise<DataSourceHttpResponse> => {
242
+ throw new Error("must not be called");
243
+ },
244
+ });
245
+ }).toThrow(BadDataException);
246
+ });
247
+
248
+ test("builds the regional endpoint, with the China partition suffix for cn- regions", () => {
249
+ expect(AwsSecurityHubClient.getEndpointHost("us-east-1")).toBe(
250
+ "securityhub.us-east-1.amazonaws.com",
251
+ );
252
+ expect(AwsSecurityHubClient.getEndpointHost("us-gov-west-1")).toBe(
253
+ "securityhub.us-gov-west-1.amazonaws.com",
254
+ );
255
+ expect(AwsSecurityHubClient.getEndpointHost("cn-north-1")).toBe(
256
+ "securityhub.cn-north-1.amazonaws.com.cn",
257
+ );
258
+ expect(
259
+ buildHarness({ region: "eu-west-2" }).client.getFindingsUrl(),
260
+ ).toBe("https://securityhub.eu-west-2.amazonaws.com/findings");
261
+ });
262
+
263
+ test("clamps MaxResults to the documented maximum of 100 and never below one", () => {
264
+ expect(AwsSecurityHubClient.clampPageSize(undefined)).toBe(
265
+ AWS_SECURITY_HUB_DEFAULT_PAGE_SIZE,
266
+ );
267
+ expect(AwsSecurityHubClient.clampPageSize(0)).toBe(
268
+ AWS_SECURITY_HUB_DEFAULT_PAGE_SIZE,
269
+ );
270
+ expect(AwsSecurityHubClient.clampPageSize(-5)).toBe(1);
271
+ expect(AwsSecurityHubClient.clampPageSize(0.5)).toBe(1);
272
+ expect(AwsSecurityHubClient.clampPageSize(7.9)).toBe(7);
273
+ expect(AwsSecurityHubClient.clampPageSize(5000)).toBe(
274
+ AWS_SECURITY_HUB_MAX_PAGE_SIZE,
275
+ );
276
+ expect(AWS_SECURITY_HUB_MAX_PAGE_SIZE).toBe(100);
277
+ });
278
+ });
279
+
280
+ describe("Signature Version 4 (AWS test-suite vectors)", () => {
281
+ test("get-vanilla: canonical request, string to sign and signature match", () => {
282
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
283
+ method: "GET",
284
+ host: SUITE_HOST,
285
+ path: "/",
286
+ body: "",
287
+ accessKeyId: SUITE_ACCESS_KEY_ID,
288
+ secretAccessKey: SUITE_SECRET,
289
+ region: "us-east-1",
290
+ service: "service",
291
+ date: SUITE_DATE,
292
+ });
293
+
294
+ expect(signed.amzDate).toBe("20150830T123600Z");
295
+ expect(signed.canonicalRequest).toBe(
296
+ `GET\n/\n\nhost:example.amazonaws.com\nx-amz-date:20150830T123600Z\n\nhost;x-amz-date\n${EMPTY_PAYLOAD_HASH}`,
297
+ );
298
+ expect(signed.stringToSign).toBe(
299
+ "AWS4-HMAC-SHA256\n20150830T123600Z\n20150830/us-east-1/service/aws4_request\nbb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63",
300
+ );
301
+ expect(signed.headers["Authorization"]).toBe(
302
+ "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31",
303
+ );
304
+ });
305
+
306
+ test("post-vanilla: the method is part of the signature", () => {
307
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
308
+ method: "POST",
309
+ host: SUITE_HOST,
310
+ path: "/",
311
+ body: "",
312
+ accessKeyId: SUITE_ACCESS_KEY_ID,
313
+ secretAccessKey: SUITE_SECRET,
314
+ region: "us-east-1",
315
+ service: "service",
316
+ date: SUITE_DATE,
317
+ });
318
+
319
+ expect(signed.stringToSign).toBe(
320
+ "AWS4-HMAC-SHA256\n20150830T123600Z\n20150830/us-east-1/service/aws4_request\n553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87",
321
+ );
322
+ expect(signed.signature).toBe(
323
+ "5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b",
324
+ );
325
+ });
326
+
327
+ test("post-x-www-form-urlencoded: a body and a content-type header are hashed and signed", () => {
328
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
329
+ method: "POST",
330
+ host: SUITE_HOST,
331
+ path: "/",
332
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
333
+ body: "Param1=value1",
334
+ accessKeyId: SUITE_ACCESS_KEY_ID,
335
+ secretAccessKey: SUITE_SECRET,
336
+ region: "us-east-1",
337
+ service: "service",
338
+ date: SUITE_DATE,
339
+ });
340
+
341
+ expect(signed.canonicalRequest).toBe(
342
+ "POST\n/\n\ncontent-type:application/x-www-form-urlencoded\nhost:example.amazonaws.com\nx-amz-date:20150830T123600Z\n\ncontent-type;host;x-amz-date\n9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e",
343
+ );
344
+ expect(signed.headers["Authorization"]).toBe(
345
+ "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a",
346
+ );
347
+ });
348
+
349
+ test("post-header-key-sort: header names are lowercased and sorted, values trimmed", () => {
350
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
351
+ method: "POST",
352
+ host: SUITE_HOST,
353
+ path: "/",
354
+ headers: { "My-Header1": " value1 " },
355
+ body: "",
356
+ accessKeyId: SUITE_ACCESS_KEY_ID,
357
+ secretAccessKey: SUITE_SECRET,
358
+ region: "us-east-1",
359
+ service: "service",
360
+ date: SUITE_DATE,
361
+ });
362
+
363
+ expect(signed.canonicalRequest).toBe(
364
+ `POST\n/\n\nhost:example.amazonaws.com\nmy-header1:value1\nx-amz-date:20150830T123600Z\n\nhost;my-header1;x-amz-date\n${EMPTY_PAYLOAD_HASH}`,
365
+ );
366
+ expect(signed.signature).toBe(
367
+ "c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c",
368
+ );
369
+ });
370
+
371
+ test("post-sts-header-before: a session token is signed as x-amz-security-token", () => {
372
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
373
+ method: "POST",
374
+ host: SUITE_HOST,
375
+ path: "/",
376
+ body: "",
377
+ accessKeyId: SUITE_ACCESS_KEY_ID,
378
+ secretAccessKey: SUITE_SECRET,
379
+ sessionToken: SUITE_STS_TOKEN,
380
+ region: "us-east-1",
381
+ service: "service",
382
+ date: SUITE_DATE,
383
+ });
384
+
385
+ expect(signed.stringToSign).toBe(
386
+ "AWS4-HMAC-SHA256\n20150830T123600Z\n20150830/us-east-1/service/aws4_request\nc237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6",
387
+ );
388
+ expect(signed.headers["Authorization"]).toBe(
389
+ "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead",
390
+ );
391
+ expect(signed.headers["X-Amz-Security-Token"]).toBe(SUITE_STS_TOKEN);
392
+ });
393
+
394
+ test("URI-encodes path segments and sorts encoded query parameters per AWS UriEncode", () => {
395
+ expect(AwsSecurityHubClient.uriEncode("a b*c(d)!'~-_.")).toBe(
396
+ "a%20b%2Ac%28d%29%21%27~-_.",
397
+ );
398
+
399
+ const signed: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
400
+ method: "GET",
401
+ host: SUITE_HOST,
402
+ path: "/a b/c",
403
+ query: { "Z-key": "2", "a key": "x y", B: "" },
404
+ body: "",
405
+ accessKeyId: SUITE_ACCESS_KEY_ID,
406
+ secretAccessKey: SUITE_SECRET,
407
+ region: "us-east-1",
408
+ service: "service",
409
+ date: SUITE_DATE,
410
+ });
411
+
412
+ expect(signed.canonicalRequest.split("\n")[1]).toBe("/a%20b/c");
413
+ expect(signed.canonicalRequest.split("\n")[2]).toBe(
414
+ "B=&Z-key=2&a%20key=x%20y",
415
+ );
416
+ });
417
+
418
+ test("formats x-amz-date without separators or fractional seconds", () => {
419
+ expect(AwsSecurityHubClient.toAmzDate(CLOCK)).toBe("20260913T100507Z");
420
+ });
421
+ });
422
+
423
+ describe("findings request", () => {
424
+ test("posts a signed GetFindings for findings created in the window, oldest first, with MaxResults", async () => {
425
+ const harness: Harness = buildHarness({
426
+ requestTimeoutInMs: 4321,
427
+ responders: [
428
+ (): DataSourceHttpResponse => {
429
+ return ok({ Findings: [finding("a")] });
430
+ },
431
+ ],
432
+ });
433
+
434
+ const page: AwsSecurityHubFindingsPage = await harness.client.getFindings(
435
+ { startTime: START, endTime: END, maxResults: 25 },
436
+ );
437
+
438
+ expect(page.findings).toHaveLength(1);
439
+ expect(page.nextToken).toBeNull();
440
+ expect(harness.requests).toHaveLength(1);
441
+
442
+ const request: DataSourceHttpRequest = harness.requests[0]!;
443
+ expect(request.method).toBe("POST");
444
+ expect(request.timeoutInMs).toBe(4321);
445
+
446
+ const url: URL = new URL(request.url);
447
+ expect(url.protocol).toBe("https:");
448
+ expect(url.host).toBe("securityhub.us-east-1.amazonaws.com");
449
+ expect(url.pathname).toBe("/findings");
450
+ expect(Array.from(url.searchParams.keys()).sort()).toEqual([]);
451
+
452
+ expect(Object.keys(request.headers || {}).sort()).toEqual([
453
+ "Authorization",
454
+ "Content-Type",
455
+ "X-Amz-Date",
456
+ ]);
457
+ expect(request.headers?.["Content-Type"]).toBe("application/json");
458
+ expect(request.headers?.["X-Amz-Date"]).toBe("20260913T100507Z");
459
+ expect(request.headers?.["Authorization"]).toMatch(
460
+ new RegExp(
461
+ `^AWS4-HMAC-SHA256 Credential=${ACCESS_KEY_ID}/20260913/us-east-1/securityhub/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=[0-9a-f]{64}$`,
462
+ ),
463
+ );
464
+ expect(request.formUrlEncoded).toBeFalsy();
465
+
466
+ const body: JSONObject = parsedBody(request);
467
+ expect(Object.keys(body).sort()).toEqual([
468
+ "Filters",
469
+ "MaxResults",
470
+ "SortCriteria",
471
+ ]);
472
+ expect(body["Filters"]).toEqual({
473
+ CreatedAt: [
474
+ {
475
+ Start: "2026-09-12T10:00:00.000Z",
476
+ End: "2026-09-13T10:00:00.000Z",
477
+ },
478
+ ],
479
+ });
480
+ expect(body["SortCriteria"]).toEqual([
481
+ { Field: "CreatedAt", SortOrder: "asc" },
482
+ ]);
483
+ expect(body["MaxResults"]).toBe(25);
484
+ });
485
+
486
+ test("signs the exact body string that is sent, so the transport cannot alter the payload hash", async () => {
487
+ const harness: Harness = buildHarness({
488
+ responders: [
489
+ (): DataSourceHttpResponse => {
490
+ return ok({ Findings: [] });
491
+ },
492
+ ],
493
+ });
494
+
495
+ await harness.client.getFindings({ startTime: START, endTime: END });
496
+
497
+ const request: DataSourceHttpRequest = harness.requests[0]!;
498
+ const expected: AwsSigV4SignResult = AwsSecurityHubClient.signRequest({
499
+ method: "POST",
500
+ host: "securityhub.us-east-1.amazonaws.com",
501
+ path: "/findings",
502
+ headers: { "Content-Type": "application/json" },
503
+ body: request.body as string,
504
+ accessKeyId: ACCESS_KEY_ID,
505
+ secretAccessKey: SECRET_ACCESS_KEY,
506
+ region: REGION,
507
+ service: "securityhub",
508
+ date: CLOCK,
509
+ });
510
+
511
+ expect(request.headers?.["Authorization"]).toBe(
512
+ expected.headers["Authorization"],
513
+ );
514
+ });
515
+
516
+ test("sends and signs the session token for temporary credentials", async () => {
517
+ const harness: Harness = buildHarness({
518
+ sessionToken: SESSION_TOKEN,
519
+ responders: [
520
+ (): DataSourceHttpResponse => {
521
+ return ok({ Findings: [] });
522
+ },
523
+ ],
524
+ });
525
+
526
+ await harness.client.getFindings({ startTime: START, endTime: END });
527
+
528
+ const request: DataSourceHttpRequest = harness.requests[0]!;
529
+ expect(Object.keys(request.headers || {}).sort()).toEqual([
530
+ "Authorization",
531
+ "Content-Type",
532
+ "X-Amz-Date",
533
+ "X-Amz-Security-Token",
534
+ ]);
535
+ expect(request.headers?.["X-Amz-Security-Token"]).toBe(SESSION_TOKEN);
536
+ expect(request.headers?.["Authorization"]).toContain(
537
+ "SignedHeaders=content-type;host;x-amz-date;x-amz-security-token,",
538
+ );
539
+ });
540
+
541
+ test("uses the default page size of 100 and clamps larger requests", async () => {
542
+ const harness: Harness = buildHarness({
543
+ responders: [
544
+ (): DataSourceHttpResponse => {
545
+ return ok({ Findings: [] });
546
+ },
547
+ (): DataSourceHttpResponse => {
548
+ return ok({ Findings: [] });
549
+ },
550
+ ],
551
+ });
552
+
553
+ await harness.client.getFindings({ startTime: START, endTime: END });
554
+ await harness.client.getFindings({
555
+ startTime: START,
556
+ endTime: END,
557
+ maxResults: 999,
558
+ });
559
+
560
+ expect(parsedBody(harness.requests[0]!)["MaxResults"]).toBe(100);
561
+ expect(parsedBody(harness.requests[1]!)["MaxResults"]).toBe(100);
562
+ });
563
+
564
+ test("resends the same filter and sort with NextToken on the next page and stops when the last page has none", async () => {
565
+ const harness: Harness = buildHarness({
566
+ responders: [
567
+ (): DataSourceHttpResponse => {
568
+ return ok({ Findings: [finding("a")], NextToken: "page-2-token" });
569
+ },
570
+ (): DataSourceHttpResponse => {
571
+ return ok({ Findings: [finding("b")], NextToken: " " });
572
+ },
573
+ ],
574
+ });
575
+
576
+ const first: AwsSecurityHubFindingsPage =
577
+ await harness.client.getFindings({ startTime: START, endTime: END });
578
+ expect(first.nextToken).toBe("page-2-token");
579
+
580
+ const second: AwsSecurityHubFindingsPage =
581
+ await harness.client.getFindings({
582
+ startTime: START,
583
+ endTime: END,
584
+ nextToken: first.nextToken,
585
+ });
586
+ expect(second.nextToken).toBeNull();
587
+ expect(
588
+ second.findings.map((item: JSONObject): JSONValue | undefined => {
589
+ return item["Id"];
590
+ }),
591
+ ).toEqual([finding("b")["Id"]]);
592
+
593
+ const body: JSONObject = parsedBody(harness.requests[1]!);
594
+ expect(Object.keys(body).sort()).toEqual([
595
+ "Filters",
596
+ "MaxResults",
597
+ "NextToken",
598
+ "SortCriteria",
599
+ ]);
600
+ expect(body["NextToken"]).toBe("page-2-token");
601
+ expect(body["Filters"]).toEqual(
602
+ parsedBody(harness.requests[0]!)["Filters"],
603
+ );
604
+ expect(harness.client.getRequestCount()).toBe(2);
605
+ });
606
+
607
+ test("drops non-object entries from Findings rather than failing the page", async () => {
608
+ const harness: Harness = buildHarness({
609
+ responders: [
610
+ (): DataSourceHttpResponse => {
611
+ return ok({
612
+ Findings: [finding("a"), null, "junk", 7, [finding("b")]],
613
+ } as unknown as JSONObject);
614
+ },
615
+ ],
616
+ });
617
+
618
+ const page: AwsSecurityHubFindingsPage = await harness.client.getFindings(
619
+ { startTime: START, endTime: END },
620
+ );
621
+
622
+ expect(page.findings).toHaveLength(1);
623
+ });
624
+
625
+ test("counts one bounded page and reports whether more exist", async () => {
626
+ const harness: Harness = buildHarness({
627
+ responders: [
628
+ (): DataSourceHttpResponse => {
629
+ return ok({
630
+ Findings: [finding("a"), finding("b")],
631
+ NextToken: "more",
632
+ });
633
+ },
634
+ (): DataSourceHttpResponse => {
635
+ return ok({ Findings: [] });
636
+ },
637
+ ],
638
+ });
639
+
640
+ const withMore: AwsSecurityHubFindingCount =
641
+ await harness.client.countFindingsCreated({
642
+ startTime: START,
643
+ endTime: END,
644
+ maxResults: 2,
645
+ });
646
+ const none: AwsSecurityHubFindingCount =
647
+ await harness.client.countFindingsCreated({
648
+ startTime: START,
649
+ endTime: END,
650
+ });
651
+
652
+ expect(withMore).toEqual({ count: 2, hasMore: true });
653
+ expect(none).toEqual({ count: 0, hasMore: false });
654
+ expect(parsedBody(harness.requests[0]!)["MaxResults"]).toBe(2);
655
+ expect(parsedBody(harness.requests[1]!)["MaxResults"]).toBe(100);
656
+ });
657
+ });
658
+
659
+ describe("failure taxonomy", () => {
660
+ test("names the step and the status on AccessDeniedException and hints at securityhub:GetFindings", async () => {
661
+ const harness: Harness = buildHarness({
662
+ responders: [
663
+ (): DataSourceHttpResponse => {
664
+ return status(
665
+ 403,
666
+ JSON.stringify({
667
+ Message:
668
+ "User: arn:aws:iam::193043430472:user/oneuptime is not authorized to perform: securityhub:GetFindings on resource: arn:aws:securityhub:us-east-1:193043430472:hub/default",
669
+ }),
670
+ { "x-amzn-errortype": "AccessDeniedException" },
671
+ );
672
+ },
673
+ ],
674
+ });
675
+
676
+ const error: APIException = await expectRejection(
677
+ harness.client.getFindings({ startTime: START, endTime: END }),
678
+ );
679
+
680
+ expect(error.message).toMatch(
681
+ /^AWS Security Hub findings request failed \(HTTP 403\): /,
682
+ );
683
+ expect(error.message).toContain("not authorized to perform");
684
+ expect(error.message).toContain("securityhub:GetFindings");
685
+ expect(error.message).toContain("AWSSecurityHubReadOnlyAccess");
686
+ });
687
+
688
+ test("reads the error code from the body __type when there is no header, namespace-qualified or not", () => {
689
+ expect(
690
+ AwsSecurityHubClient.errorCode({
691
+ bodyJson: {
692
+ __type: "com.amazon.coral.service#UnrecognizedClientException",
693
+ },
694
+ }),
695
+ ).toBe("UnrecognizedClientException");
696
+ expect(
697
+ AwsSecurityHubClient.errorCode({
698
+ bodyJson: { Code: "InvalidSignatureException" },
699
+ }),
700
+ ).toBe("InvalidSignatureException");
701
+ expect(
702
+ AwsSecurityHubClient.errorCode({
703
+ bodyJson: {},
704
+ headers: {
705
+ "x-amzn-errortype":
706
+ "AccessDeniedException:http://internal.amazon.com/coral/com.amazonaws.securityhub/",
707
+ },
708
+ }),
709
+ ).toBe("AccessDeniedException");
710
+ expect(AwsSecurityHubClient.errorCode({ bodyJson: "nope" })).toBe("");
711
+ });
712
+
713
+ test("tells an unrecognized access key apart from a wrong secret and from an expired token", async () => {
714
+ const cases: Array<[string, string, string]> = [
715
+ [
716
+ "UnrecognizedClientException",
717
+ "The security token included in the request is invalid.",
718
+ "does not recognize the Access key ID",
719
+ ],
720
+ [
721
+ "InvalidSignatureException",
722
+ "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method.",
723
+ "Secret access key does not match",
724
+ ],
725
+ [
726
+ "ExpiredTokenException",
727
+ "The security token included in the request is expired",
728
+ "temporary credentials have expired",
729
+ ],
730
+ /*
731
+ * This case used to expect the wrong-secret hint. "Signature
732
+ * expired" shares InvalidSignatureException with a wrong secret but
733
+ * means the host clock is off (review finding
734
+ * aws-signature-errors-reported-as-iam-permission), so the hint now
735
+ * points at NTP.
736
+ */
737
+ [
738
+ "InvalidSignatureException",
739
+ "Signature expired: 20260913T100507Z is now earlier than 20260913T101000Z (20260913T101500Z - 5 min.)",
740
+ "Fix NTP",
741
+ ],
742
+ ];
743
+
744
+ for (const [code, message, hint] of cases) {
745
+ const harness: Harness = buildHarness({
746
+ responders: [
747
+ (): DataSourceHttpResponse => {
748
+ return status(403, awsError(code, message));
749
+ },
750
+ ],
751
+ });
752
+
753
+ const error: APIException = await expectRejection(
754
+ harness.client.getFindings({ startTime: START, endTime: END }),
755
+ );
756
+
757
+ expect(error.message).toMatch(
758
+ /^AWS Security Hub findings request failed \(HTTP 403\): /,
759
+ );
760
+ expect(error.message).toContain(code);
761
+ expect(error.message).toContain(hint);
762
+ expect(error).toBeInstanceOf(AwsSecurityHubHttpError);
763
+ expect((error as AwsSecurityHubHttpError).failureKind).toBe(
764
+ "authentication",
765
+ );
766
+ }
767
+ });
768
+
769
+ /*
770
+ * Review finding aws-signature-errors-reported-as-iam-permission: the
771
+ * production transport throws "Data source responded with HTTP <n>:
772
+ * <body>" and drops x-amzn-errortype, and a signature rejection's body
773
+ * is only {"message": ...}. The code is recovered from AWS's wording,
774
+ * so these are authentication failures with the key or clock hint,
775
+ * never "the credentials are valid" with IAM guidance.
776
+ */
777
+ test.each<
778
+ [string, number, string, string, AwsSecurityHubFailureKind, string]
779
+ >([
780
+ [
781
+ "a wrong secret",
782
+ 403,
783
+ "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method.",
784
+ "InvalidSignatureException",
785
+ "authentication",
786
+ "Secret access key does not match",
787
+ ],
788
+ [
789
+ "an unknown key or stray session token",
790
+ 403,
791
+ "The security token included in the request is invalid.",
792
+ "UnrecognizedClientException",
793
+ "authentication",
794
+ "does not recognize the Access key ID",
795
+ ],
796
+ [
797
+ "an expired session token",
798
+ 403,
799
+ "The security token included in the request is expired",
800
+ "ExpiredTokenException",
801
+ "authentication",
802
+ "temporary credentials have expired",
803
+ ],
804
+ [
805
+ "an expired signature",
806
+ 403,
807
+ "Signature expired: 20260913T100507Z is now earlier than 20260913T101000Z (20260913T101500Z - 5 min.)",
808
+ "InvalidSignatureException",
809
+ "authentication",
810
+ "Fix NTP",
811
+ ],
812
+ [
813
+ "an IAM denial",
814
+ 403,
815
+ "User: arn:aws:iam::193043430472:user/oneuptime is not authorized to perform: securityhub:GetFindings",
816
+ "AccessDeniedException",
817
+ "authorization",
818
+ "AWSSecurityHubReadOnlyAccess",
819
+ ],
820
+ [
821
+ "a 403 that names nothing",
822
+ 403,
823
+ "Forbidden",
824
+ "",
825
+ "other",
826
+ "without naming an error code",
827
+ ],
828
+ ])(
829
+ "classifies %s behind the production transport",
830
+ async (
831
+ _label: string,
832
+ httpStatus: number,
833
+ awsMessage: string,
834
+ expectedCode: string,
835
+ expectedKind: AwsSecurityHubFailureKind,
836
+ hint: string,
837
+ ) => {
838
+ const harness: Harness = buildHarness({
839
+ responders: [
840
+ (): Promise<DataSourceHttpResponse> => {
841
+ throw new BadDataException(
842
+ `Data source responded with HTTP ${httpStatus}: ${JSON.stringify(
843
+ {
844
+ message: awsMessage,
845
+ },
846
+ )}`,
847
+ );
848
+ },
849
+ ],
850
+ });
851
+
852
+ const error: APIException = await expectRejection(
853
+ harness.client.getFindings({ startTime: START, endTime: END }),
854
+ );
855
+
856
+ expect(error).toBeInstanceOf(AwsSecurityHubHttpError);
857
+
858
+ const httpError: AwsSecurityHubHttpError =
859
+ error as AwsSecurityHubHttpError;
860
+ expect(httpError.statusCode).toBe(httpStatus);
861
+ expect(httpError.errorCode).toBe(expectedCode);
862
+ expect(httpError.failureKind).toBe(expectedKind);
863
+ expect(error.message).toMatch(
864
+ new RegExp(
865
+ `^AWS Security Hub findings request failed \\(HTTP ${httpStatus}\\): `,
866
+ ),
867
+ );
868
+ expect(error.message).toContain(hint);
869
+
870
+ if (expectedKind !== "authorization") {
871
+ expect(error.message).not.toContain("verified the signature");
872
+ expect(error.message).not.toContain("AWSSecurityHubReadOnlyAccess");
873
+ }
874
+ },
875
+ );
876
+
877
+ test("classifies InvalidAccessException as authorization at the 401 the API reference documents", async () => {
878
+ const harness: Harness = buildHarness({
879
+ responders: [
880
+ (): Promise<DataSourceHttpResponse> => {
881
+ throw new BadDataException(
882
+ `Data source responded with HTTP 401: ${JSON.stringify({
883
+ Code: "InvalidAccessException",
884
+ Message:
885
+ "Account 193043430472 is not subscribed to AWS Security Hub",
886
+ })}`,
887
+ );
888
+ },
889
+ ],
890
+ });
891
+
892
+ const error: APIException = await expectRejection(
893
+ harness.client.getFindings({ startTime: START, endTime: END }),
894
+ );
895
+
896
+ expect((error as AwsSecurityHubHttpError).errorCode).toBe(
897
+ "InvalidAccessException",
898
+ );
899
+ expect((error as AwsSecurityHubHttpError).failureKind).toBe(
900
+ "authorization",
901
+ );
902
+ expect(error.message).toContain("Security Hub is not enabled");
903
+ expect(error.message).not.toContain("AWS refused the credentials");
904
+ });
905
+
906
+ test("a declared code wins over the message wording, and wording is only a fallback", () => {
907
+ expect(
908
+ AwsSecurityHubClient.errorCode({
909
+ bodyJson: {
910
+ __type: "AccessDeniedException",
911
+ message: "The security token included in the request is invalid.",
912
+ },
913
+ }),
914
+ ).toBe("AccessDeniedException");
915
+ expect(
916
+ AwsSecurityHubClient.errorCode({
917
+ bodyJson: undefined,
918
+ bodyText:
919
+ "<html>The request signature we calculated does not match</html>",
920
+ }),
921
+ ).toBe("InvalidSignatureException");
922
+ expect(
923
+ AwsSecurityHubClient.errorCodeFromMessage(
924
+ "The security token included in the request is expired",
925
+ ),
926
+ ).toBe("ExpiredTokenException");
927
+ expect(
928
+ AwsSecurityHubClient.errorCodeFromMessage("Service Unavailable"),
929
+ ).toBe("");
930
+ });
931
+
932
+ test("only AccessDeniedException and InvalidAccessException are authorization failures", () => {
933
+ const cases: Array<[number, string, AwsSecurityHubFailureKind]> = [
934
+ [403, "AccessDeniedException", "authorization"],
935
+ [401, "InvalidAccessException", "authorization"],
936
+ [403, "InvalidAccessException", "authorization"],
937
+ [403, "InvalidSignatureException", "authentication"],
938
+ [403, "UnrecognizedClientException", "authentication"],
939
+ [400, "IncompleteSignatureException", "authentication"],
940
+ [401, "", "authentication"],
941
+ [403, "", "other"],
942
+ [429, "LimitExceededException", "other"],
943
+ [500, "InternalException", "other"],
944
+ ];
945
+
946
+ for (const [httpStatus, code, kind] of cases) {
947
+ expect(AwsSecurityHubClient.classifyFailure(httpStatus, code)).toBe(
948
+ kind,
949
+ );
950
+ }
951
+ });
952
+
953
+ test("hints at the server clock for a skewed request time", async () => {
954
+ const harness: Harness = buildHarness({
955
+ responders: [
956
+ (): DataSourceHttpResponse => {
957
+ return status(
958
+ 403,
959
+ awsError(
960
+ "RequestTimeTooSkewed",
961
+ "The difference between the request time and the current time is too large.",
962
+ ),
963
+ );
964
+ },
965
+ ],
966
+ });
967
+
968
+ const error: APIException = await expectRejection(
969
+ harness.client.getFindings({ startTime: START, endTime: END }),
970
+ );
971
+
972
+ expect(error.message).toContain("Fix NTP");
973
+ });
974
+
975
+ test("hints at enabling Security Hub on InvalidAccessException", async () => {
976
+ const harness: Harness = buildHarness({
977
+ responders: [
978
+ (): DataSourceHttpResponse => {
979
+ return status(
980
+ 403,
981
+ awsError(
982
+ "InvalidAccessException",
983
+ "Account 193043430472 is not subscribed to AWS Security Hub",
984
+ ),
985
+ );
986
+ },
987
+ ],
988
+ });
989
+
990
+ const error: APIException = await expectRejection(
991
+ harness.client.getFindings({ startTime: START, endTime: END }),
992
+ );
993
+
994
+ expect(error.message).toMatch(
995
+ /^AWS Security Hub findings request failed \(HTTP 403\): /,
996
+ );
997
+ expect(error.message).toContain("Security Hub is not enabled");
998
+ });
999
+
1000
+ test("reports 401, 400, 404, throttling (429) and server-side failures (5xx) with their own hints", async () => {
1001
+ const cases: Array<[number, string, string]> = [
1002
+ [401, "", "AWS refused the credentials"],
1003
+ [
1004
+ 400,
1005
+ awsError("InvalidInputException", "Invalid filter"),
1006
+ "report this message to OneUptime support",
1007
+ ],
1008
+ [404, "<html>not found</html>", "Check the Region code"],
1009
+ [
1010
+ 429,
1011
+ awsError("LimitExceededException", "Rate exceeded"),
1012
+ "throttling GetFindings (3 requests per second",
1013
+ ],
1014
+ [
1015
+ 500,
1016
+ awsError("InternalException", "Internal error"),
1017
+ "server-side problem",
1018
+ ],
1019
+ [503, "Service Unavailable", "server-side problem"],
1020
+ ];
1021
+
1022
+ for (const [code, body, hint] of cases) {
1023
+ const harness: Harness = buildHarness({
1024
+ responders: [
1025
+ (): DataSourceHttpResponse => {
1026
+ return status(code, body);
1027
+ },
1028
+ ],
1029
+ });
1030
+
1031
+ const error: APIException = await expectRejection(
1032
+ harness.client.getFindings({ startTime: START, endTime: END }),
1033
+ );
1034
+
1035
+ expect(error.message).toMatch(
1036
+ new RegExp(
1037
+ `^AWS Security Hub findings request failed \\(HTTP ${code}\\): `,
1038
+ ),
1039
+ );
1040
+ expect(error.message).toContain(hint);
1041
+ }
1042
+ });
1043
+
1044
+ test("describes an empty error body instead of printing nothing", async () => {
1045
+ const harness: Harness = buildHarness({
1046
+ responders: [
1047
+ (): DataSourceHttpResponse => {
1048
+ return status(502, "");
1049
+ },
1050
+ ],
1051
+ });
1052
+
1053
+ const error: APIException = await expectRejection(
1054
+ harness.client.getFindings({ startTime: START, endTime: END }),
1055
+ );
1056
+
1057
+ expect(error.message).toContain("(empty response body)");
1058
+ });
1059
+
1060
+ test("recovers the status from the production transport's thrown error", async () => {
1061
+ const harness: Harness = buildHarness({
1062
+ responders: [
1063
+ (): Promise<DataSourceHttpResponse> => {
1064
+ throw new BadDataException(
1065
+ `Data source responded with HTTP 403: ${awsError(
1066
+ "AccessDeniedException",
1067
+ "not authorized to perform: securityhub:GetFindings",
1068
+ )}`,
1069
+ );
1070
+ },
1071
+ ],
1072
+ });
1073
+
1074
+ const error: APIException = await expectRejection(
1075
+ harness.client.getFindings({ startTime: START, endTime: END }),
1076
+ );
1077
+
1078
+ expect(error.message).toMatch(
1079
+ /^AWS Security Hub findings request failed \(HTTP 403\): /,
1080
+ );
1081
+ expect(error.message).toContain("AccessDeniedException");
1082
+ });
1083
+
1084
+ test("surfaces a transport failure without a status under the step prefix", async () => {
1085
+ const harness: Harness = buildHarness({
1086
+ responders: [
1087
+ (): Promise<DataSourceHttpResponse> => {
1088
+ throw new BadDataException(
1089
+ "Could not reach data source: getaddrinfo ENOTFOUND securityhub.us-east-1.amazonaws.com",
1090
+ );
1091
+ },
1092
+ ],
1093
+ });
1094
+
1095
+ const error: APIException = await expectRejection(
1096
+ harness.client.getFindings({ startTime: START, endTime: END }),
1097
+ );
1098
+
1099
+ expect(error.message).toBe(
1100
+ "AWS Security Hub findings request failed: Could not reach data source: getaddrinfo ENOTFOUND securityhub.us-east-1.amazonaws.com",
1101
+ );
1102
+ });
1103
+
1104
+ test("gives up on a transport that never settles once the request timeout elapses", async () => {
1105
+ const harness: Harness = buildHarness({
1106
+ requestTimeoutInMs: 30,
1107
+ responders: [
1108
+ (): Promise<DataSourceHttpResponse> => {
1109
+ return new Promise<DataSourceHttpResponse>((): void => {
1110
+ // Never resolves: simulates a socket that neither answers nor closes.
1111
+ });
1112
+ },
1113
+ ],
1114
+ });
1115
+
1116
+ const error: APIException = await expectRejection(
1117
+ harness.client.getFindings({ startTime: START, endTime: END }),
1118
+ );
1119
+
1120
+ expect(error.message).toMatch(
1121
+ /^AWS Security Hub findings request timed out after \d+ seconds with no response\.$/,
1122
+ );
1123
+ });
1124
+
1125
+ test("reports a non-JSON 200 body without guessing", async () => {
1126
+ const harness: Harness = buildHarness({
1127
+ responders: [
1128
+ (): DataSourceHttpResponse => {
1129
+ return status(200, "<html><title>Sign in</title></html>");
1130
+ },
1131
+ ],
1132
+ });
1133
+
1134
+ const error: APIException = await expectRejection(
1135
+ harness.client.getFindings({ startTime: START, endTime: END }),
1136
+ );
1137
+
1138
+ expect(error.message).toMatch(
1139
+ /^AWS Security Hub findings request returned a non-JSON body: /,
1140
+ );
1141
+ expect(error.message).toContain("Sign in");
1142
+ });
1143
+
1144
+ test("reports a JSON body without a Findings array, and a JSON array body, as unrecognized shapes", async () => {
1145
+ const objectHarness: Harness = buildHarness({
1146
+ responders: [
1147
+ (): DataSourceHttpResponse => {
1148
+ return ok({ Results: [], Marker: "x" });
1149
+ },
1150
+ ],
1151
+ });
1152
+ const objectError: APIException = await expectRejection(
1153
+ objectHarness.client.getFindings({ startTime: START, endTime: END }),
1154
+ );
1155
+ expect(objectError.message).toBe(
1156
+ 'AWS Security Hub findings request returned an unrecognized response shape: expected a "Findings" array, received keys Results, Marker.',
1157
+ );
1158
+
1159
+ const arrayHarness: Harness = buildHarness({
1160
+ responders: [
1161
+ (): DataSourceHttpResponse => {
1162
+ return status(200, "[]");
1163
+ },
1164
+ ],
1165
+ });
1166
+ const arrayError: APIException = await expectRejection(
1167
+ arrayHarness.client.getFindings({ startTime: START, endTime: END }),
1168
+ );
1169
+ expect(arrayError.message).toBe(
1170
+ "AWS Security Hub findings request returned an unrecognized response shape: expected a JSON object, received an array.",
1171
+ );
1172
+ });
1173
+ });
1174
+
1175
+ describe("redaction", () => {
1176
+ test("never echoes the secret access key, session token or access key id that a body reflects back", async () => {
1177
+ const harness: Harness = buildHarness({
1178
+ sessionToken: SESSION_TOKEN,
1179
+ responders: [
1180
+ (): DataSourceHttpResponse => {
1181
+ return status(
1182
+ 403,
1183
+ JSON.stringify({
1184
+ __type: "InvalidSignatureException",
1185
+ Message: `The request signature we calculated does not match. AccessKeyId=${ACCESS_KEY_ID} SecretAccessKey=${SECRET_ACCESS_KEY}`,
1186
+ SecretAccessKey: SECRET_ACCESS_KEY,
1187
+ SessionToken: SESSION_TOKEN,
1188
+ Authorization: `AWS4-HMAC-SHA256 Credential=${ACCESS_KEY_ID}/20260913/us-east-1/securityhub/aws4_request, SignedHeaders=host, Signature=abc`,
1189
+ }),
1190
+ );
1191
+ },
1192
+ ],
1193
+ });
1194
+
1195
+ const error: APIException = await expectRejection(
1196
+ harness.client.getFindings({ startTime: START, endTime: END }),
1197
+ );
1198
+
1199
+ expect(error.message).toContain("InvalidSignatureException");
1200
+ expect(error.message).not.toContain(SECRET_ACCESS_KEY);
1201
+ expect(error.message).not.toContain(SESSION_TOKEN);
1202
+ expect(error.message).not.toContain(ACCESS_KEY_ID);
1203
+ expect(error.message).toContain("[REDACTED]");
1204
+ });
1205
+
1206
+ test("redacts credentials reflected in a non-JSON body and in a transport error", async () => {
1207
+ const textHarness: Harness = buildHarness({
1208
+ responders: [
1209
+ (): DataSourceHttpResponse => {
1210
+ return status(
1211
+ 500,
1212
+ `gateway error while forwarding secret_access_key=${SECRET_ACCESS_KEY}`,
1213
+ );
1214
+ },
1215
+ ],
1216
+ });
1217
+ const textError: APIException = await expectRejection(
1218
+ textHarness.client.getFindings({ startTime: START, endTime: END }),
1219
+ );
1220
+ expect(textError.message).not.toContain(SECRET_ACCESS_KEY);
1221
+
1222
+ const transportHarness: Harness = buildHarness({
1223
+ responders: [
1224
+ (): Promise<DataSourceHttpResponse> => {
1225
+ throw new Error(
1226
+ `proxy refused x-amz-security-token: ${SESSION_TOKEN}`,
1227
+ );
1228
+ },
1229
+ ],
1230
+ });
1231
+ const transportError: APIException = await expectRejection(
1232
+ transportHarness.client.getFindings({ startTime: START, endTime: END }),
1233
+ );
1234
+ expect(transportError.message).toMatch(
1235
+ /^AWS Security Hub findings request failed: /,
1236
+ );
1237
+ expect(transportError.message).not.toContain(SESSION_TOKEN);
1238
+ });
1239
+ });
1240
+ });