@oneuptime/common 13.0.3 → 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 (687) 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/LoggerCore.test.ts +597 -0
  160. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  161. package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  178. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  179. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  188. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  189. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  190. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  191. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  192. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
  193. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  194. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  195. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  196. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +868 -0
  197. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  198. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  199. package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
  200. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
  201. package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
  202. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  203. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  204. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  205. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  206. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  207. package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
  208. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  209. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  210. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  211. package/Tests/UI/Components/Card.test.tsx +312 -1
  212. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  213. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  214. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  215. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  216. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  217. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  218. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  219. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  220. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  221. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  222. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  223. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  224. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  225. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  226. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  227. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  228. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  229. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  230. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  231. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  232. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  233. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  234. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  235. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  236. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  237. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  238. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  239. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  240. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  241. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  242. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  243. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  244. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  245. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  246. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  247. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  248. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  249. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  250. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  251. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  252. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  253. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  254. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  255. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  256. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  257. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  258. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  259. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  260. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  261. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  262. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  263. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  264. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  265. package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
  266. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  267. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  268. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  269. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  270. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  271. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  272. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  273. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  274. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  275. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  276. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  277. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  278. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  279. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  280. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  281. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  282. package/Types/AI/AIChatTypes.ts +8 -0
  283. package/Types/AI/InvestigationEvidence.ts +96 -0
  284. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  285. package/Types/OnCallDutyPolicy/Layer.ts +12 -0
  286. package/Types/Permission.ts +2 -2
  287. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  288. package/Types/Rum/SessionReplay.ts +109 -0
  289. package/Types/Rum/SessionReplayApi.ts +31 -0
  290. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  291. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  292. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  293. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  294. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  295. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  296. package/Types/Telemetry/EntityType.ts +14 -0
  297. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  298. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  299. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  300. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  301. package/UI/Components/Card/Card.tsx +118 -83
  302. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  303. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  304. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  305. package/UI/Components/Detail/Detail.tsx +137 -22
  306. package/UI/Components/ErrorBoundary.tsx +38 -0
  307. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  310. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  311. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  312. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  313. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  314. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  315. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  316. package/UI/Components/LogsViewer/types.ts +6 -0
  317. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  318. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  319. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  320. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  321. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  322. package/UI/Components/ModelList/ModelList.tsx +33 -8
  323. package/UI/Components/Page/ModelPage.tsx +119 -54
  324. package/UI/Components/Table/Table.tsx +8 -0
  325. package/UI/Components/Table/TableRow.tsx +13 -5
  326. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  327. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  328. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  329. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  330. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  331. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  332. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  333. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  334. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  335. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  336. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  337. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  338. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  339. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  340. package/UI/Components/TelemetryViewer/types.ts +19 -0
  341. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  342. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  343. package/UI/Utils/Clipboard.ts +68 -2
  344. package/UI/Utils/Navigation.ts +17 -2
  345. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  346. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  347. package/Utils/AI/InvestigationReport.ts +1789 -0
  348. package/Utils/API.ts +90 -1
  349. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  350. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  351. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  352. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  353. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  354. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  355. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  356. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  357. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  358. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  359. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  360. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  361. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  362. package/Utils/Telemetry/NetworkHost.ts +43 -0
  363. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  364. package/Utils/Traces/CriticalPath.ts +10 -4
  365. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  366. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  368. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  370. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  371. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  372. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  373. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  374. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  375. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  376. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  377. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  378. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  379. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  380. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  381. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  382. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  383. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  384. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  385. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  386. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  387. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  388. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  389. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  390. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  392. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  393. package/build/dist/Server/Services/Index.js +4 -0
  394. package/build/dist/Server/Services/Index.js.map +1 -1
  395. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  396. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  397. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  398. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  399. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  400. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  401. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  402. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  403. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  404. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  405. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  406. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  407. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  408. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  410. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  411. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  412. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  413. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  414. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  415. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  416. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  417. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  418. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  419. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  420. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  421. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  422. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  456. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  457. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  458. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  460. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  461. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  462. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  463. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  464. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  465. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  466. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  467. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  468. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  469. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  470. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  471. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  472. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  473. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  474. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  475. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  476. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  477. package/build/dist/Server/Utils/StartServer.js +10 -0
  478. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  480. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  481. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  482. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  484. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  485. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  486. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  487. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  488. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  489. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  490. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  491. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  492. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  493. package/build/dist/Server/Utils/Telemetry.js +11 -5
  494. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  495. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  496. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  497. package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
  498. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  499. package/build/dist/Types/Permission.js +2 -2
  500. package/build/dist/Types/Permission.js.map +1 -1
  501. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +9 -0
  502. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  503. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  504. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  505. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  506. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  507. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  508. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  509. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  510. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  511. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  512. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  513. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  514. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  515. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  516. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  517. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  518. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  519. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  520. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  521. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  522. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  523. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  524. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  525. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  526. package/build/dist/UI/Components/Card/Card.js +34 -17
  527. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  528. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  529. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  530. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  531. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  532. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  533. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  534. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  535. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  536. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  537. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  539. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  540. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  541. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  543. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  544. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  545. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  546. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  547. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  548. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  549. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  550. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  551. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  552. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  553. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  554. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  555. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  556. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  557. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  558. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  559. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  560. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  561. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  562. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  563. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  564. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  565. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  566. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  567. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  568. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  569. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  570. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  571. package/build/dist/UI/Components/Table/Table.js +3 -1
  572. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  573. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  574. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  575. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  576. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  578. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  580. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  582. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  583. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  584. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  585. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  586. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  587. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  588. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  589. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  590. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  591. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  592. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  594. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  595. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  596. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  597. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  598. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  599. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  600. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  601. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  602. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  603. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  604. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  605. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  606. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  607. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  608. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  609. package/build/dist/UI/Utils/Clipboard.js +59 -2
  610. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  611. package/build/dist/UI/Utils/Navigation.js +11 -2
  612. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  613. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  614. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  615. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  616. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  617. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  618. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  619. package/build/dist/Utils/API.js +25 -0
  620. package/build/dist/Utils/API.js.map +1 -1
  621. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  622. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  623. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  624. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  626. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  627. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  628. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  629. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  630. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  631. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  632. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  633. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  634. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  635. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  636. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  637. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  638. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  639. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  640. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  641. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  642. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  643. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  644. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  645. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  646. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  647. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  648. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  649. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  650. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  651. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  652. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  653. package/package.json +1 -1
  654. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  655. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  656. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  657. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  658. package/UI/Components/GanttChart/Index.tsx +0 -90
  659. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  660. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  661. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  662. package/UI/Components/GanttChart/Rows.tsx +0 -59
  663. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  664. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  665. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  666. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  667. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  669. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  671. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  673. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  674. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  675. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  676. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  677. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  678. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  679. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  680. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  681. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  682. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  683. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  684. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  685. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  686. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  687. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -0,0 +1,1568 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+ import CrowdStrikeFalconConnector from "../../../../../../Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector";
3
+ import CrowdStrikeFalconClient, {
4
+ CROWDSTRIKE_ALERTS_OFFSET_CEILING,
5
+ CROWDSTRIKE_ALERTS_PAGE_SIZE,
6
+ } from "../../../../../../Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient";
7
+ import {
8
+ ConnectorFetchOptions,
9
+ ConnectorFetchResult,
10
+ SecurityConnectorSettings,
11
+ } from "../../../../../../Server/Utils/SecurityEvent/Connectors/Types";
12
+ import {
13
+ DataSourceHttpRequest,
14
+ DataSourceHttpResponse,
15
+ } from "../../../../../../Server/Utils/DataSource/HttpFetch";
16
+ import BadDataException from "../../../../../../Types/Exception/BadDataException";
17
+ import APIException from "../../../../../../Types/Exception/ApiException";
18
+ import Dictionary from "../../../../../../Types/Dictionary";
19
+ import { JSONObject } from "../../../../../../Types/JSON";
20
+ import { SecurityConnectorCheck } from "../../../../../../Types/SecurityEvent/Connectors/ConnectorDiagnostics";
21
+ import SecurityEventConnectorProvider from "../../../../../../Types/SecurityEvent/Connectors/SecurityEventConnectorProvider";
22
+ import {
23
+ SecurityEventConnectorDefinition,
24
+ getSecurityEventConnectorDefinition,
25
+ } from "../../../../../../Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog";
26
+ import OcsfSeverity from "../../../../../../Types/SecurityEvent/OcsfSeverity";
27
+
28
+ /*
29
+ * The connector as the framework depends on it: settings are read by the
30
+ * catalog's keys, the three test checks carry the keys the report UI
31
+ * groups on, fetchEvents pages by created_timestamp and stops — with
32
+ * complete=false, a warning and the created_timestamp of the last alert
33
+ * read as resumeAfter — at every bound, and nothing secret can appear in
34
+ * a check message. The transport is the injected seam; nothing
35
+ * here touches the network.
36
+ */
37
+
38
+ const CLIENT_ID: string = "a1b2c3d4e5f60718293a4b5c6d7e8f90";
39
+ const CLIENT_SECRET: string = "Zx9YwV8uT7sR6qP5oN4mL3kJ2iH1gF0eDcBa";
40
+ const ACCESS_TOKEN: string =
41
+ "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmYWxjb24ifQ.c2lnbmF0dXJlLXNpZ25hdHVyZS1zaWduYXR1cmU";
42
+ const CID: string = "92012896127c4a8236ba7601b886b0";
43
+ const START: Date = new Date("2026-09-12T10:00:00.000Z");
44
+ const END: Date = new Date("2026-09-13T10:00:00.000Z");
45
+
46
+ const DEFINITION: SecurityEventConnectorDefinition =
47
+ getSecurityEventConnectorDefinition(
48
+ SecurityEventConnectorProvider.CrowdStrikeFalcon,
49
+ )!;
50
+
51
+ type Responder = (
52
+ request: DataSourceHttpRequest,
53
+ ) => DataSourceHttpResponse | Promise<DataSourceHttpResponse>;
54
+
55
+ interface Harness {
56
+ requests: Array<DataSourceHttpRequest>;
57
+ connector: CrowdStrikeFalconConnector;
58
+ }
59
+
60
+ function settings(overrides?: {
61
+ config?: JSONObject;
62
+ secrets?: JSONObject;
63
+ }): SecurityConnectorSettings {
64
+ return {
65
+ provider: SecurityEventConnectorProvider.CrowdStrikeFalcon,
66
+ config: overrides?.config || { clientId: CLIENT_ID, cloud: "us-2" },
67
+ secrets: overrides?.secrets || { clientSecret: CLIENT_SECRET },
68
+ alertingOnly: false,
69
+ };
70
+ }
71
+
72
+ function fetchOptions(
73
+ overrides?: Partial<ConnectorFetchOptions>,
74
+ ): ConnectorFetchOptions {
75
+ return {
76
+ maxRequests: 20,
77
+ maxEvents: 10000,
78
+ requestTimeoutInMs: 5000,
79
+ sampleLimit: 25,
80
+ ...overrides,
81
+ };
82
+ }
83
+
84
+ function ok(
85
+ body: JSONObject,
86
+ statusCode: number = 200,
87
+ headers?: Dictionary<string>,
88
+ ): DataSourceHttpResponse {
89
+ return {
90
+ statusCode,
91
+ bodyText: JSON.stringify(body),
92
+ bodyJson: body,
93
+ headers: headers || {},
94
+ };
95
+ }
96
+
97
+ function status(
98
+ code: number,
99
+ body: string,
100
+ headers?: Dictionary<string>,
101
+ ): DataSourceHttpResponse {
102
+ let bodyJson: unknown = undefined;
103
+
104
+ try {
105
+ bodyJson = JSON.parse(body);
106
+ } catch {
107
+ bodyJson = undefined;
108
+ }
109
+
110
+ return { statusCode: code, bodyText: body, bodyJson, headers: headers || {} };
111
+ }
112
+
113
+ function tokenResponse(): DataSourceHttpResponse {
114
+ return ok(
115
+ { access_token: ACCESS_TOKEN, expires_in: 1799, token_type: "bearer" },
116
+ 201,
117
+ );
118
+ }
119
+
120
+ function compositeId(index: number): string {
121
+ return `${CID}:ind:2ce412d17b334ad4adc8c1c54dbfec4b:${String(index).padStart(12, "0")}-5761-42627600`;
122
+ }
123
+
124
+ function idsPage(
125
+ ids: Array<string>,
126
+ total: number | null,
127
+ ): DataSourceHttpResponse {
128
+ return ok({
129
+ meta: {
130
+ query_time: 0.012,
131
+ ...(total === null
132
+ ? {}
133
+ : { pagination: { total, offset: 0, limit: 1000 } }),
134
+ },
135
+ resources: ids,
136
+ errors: [],
137
+ });
138
+ }
139
+
140
+ function alert(id: string, overrides?: JSONObject): JSONObject {
141
+ return {
142
+ composite_id: id,
143
+ id: id.split(":").slice(1).join(":"),
144
+ cid: CID,
145
+ name: "PrewittPupAdwareSensorDetect-Lowest",
146
+ display_name: "Adware/PUP detected",
147
+ description:
148
+ "This file meets the Adware/PUP Anti-malware ML algorithm's lowest-confidence threshold.",
149
+ severity: 21,
150
+ severity_name: "low",
151
+ status: "new",
152
+ tactic: "MachineLearning",
153
+ tactic_id: "CSTA0004",
154
+ technique: "Adware/PUP",
155
+ technique_id: "CST0000",
156
+ created_timestamp: "2026-09-12T18:01:23.995Z",
157
+ timestamp: "2026-09-12T18:00:22.328Z",
158
+ updated_timestamp: "2026-09-12T19:00:23.985Z",
159
+ product: "epp",
160
+ type: "ldt",
161
+ device: {
162
+ hostname: "ABC709-1175",
163
+ local_ip: "10.20.30.40",
164
+ external_ip: "81.2.69.142",
165
+ platform_name: "Windows",
166
+ },
167
+ user_name: "mohit.jha",
168
+ user_id: "S-1-5-21-1909377054-3469629671-4104191496-4425",
169
+ filename: "openvpn-install.exe",
170
+ cmdline: '"C:\\Users\\mohit.jha\\Downloads\\openvpn-install.exe"',
171
+ sha256: "b26a6791b72753d2317efd5e1363d93fdd33e611c8b9e08a3b24ea4d755b81fd",
172
+ ...overrides,
173
+ };
174
+ }
175
+
176
+ function entitiesFor(ids: Array<string>): DataSourceHttpResponse {
177
+ return ok({
178
+ meta: { query_time: 0.02 },
179
+ resources: ids.map((id: string): JSONObject => {
180
+ return alert(id);
181
+ }),
182
+ errors: [],
183
+ });
184
+ }
185
+
186
+ /*
187
+ * A created_timestamp one second after START per index, so ids listed in
188
+ * index order are in the ascending creation order the query asks for.
189
+ */
190
+ function createdAtFor(index: number): string {
191
+ return new Date(START.getTime() + index * 1000).toISOString();
192
+ }
193
+
194
+ function datedEntitiesFor(
195
+ ids: Array<string>,
196
+ indexOf: (id: string) => number,
197
+ ): DataSourceHttpResponse {
198
+ return ok({
199
+ meta: { query_time: 0.02 },
200
+ resources: ids.map((id: string): JSONObject => {
201
+ return alert(id, { created_timestamp: createdAtFor(indexOf(id)) });
202
+ }),
203
+ errors: [],
204
+ });
205
+ }
206
+
207
+ function compositeIdIndex(id: string): number {
208
+ const match: RegExpMatchArray | null = id.match(/:(\d{12})-5761-42627600/);
209
+
210
+ if (!match) {
211
+ throw new Error(`Unexpected composite id ${id}`);
212
+ }
213
+
214
+ return Number(match[1]);
215
+ }
216
+
217
+ function entityRequestIds(request: DataSourceHttpRequest): Array<string> {
218
+ return (JSON.parse(request.body as string) as JSONObject)[
219
+ "composite_ids"
220
+ ] as Array<string>;
221
+ }
222
+
223
+ /*
224
+ * Entity responders default to "echo an alert for every id in the body",
225
+ * which keeps pagination tests about the ids pages; override for the
226
+ * rejected/malformed cases.
227
+ */
228
+ function buildHarness(options: {
229
+ token?: Responder;
230
+ queries?: Array<Responder>;
231
+ entities?: Array<Responder>;
232
+ }): Harness {
233
+ const requests: Array<DataSourceHttpRequest> = [];
234
+ const queryResponders: Array<Responder> = [...(options.queries || [])];
235
+ const entityResponders: Array<Responder> = [...(options.entities || [])];
236
+
237
+ const connector: CrowdStrikeFalconConnector = new CrowdStrikeFalconConnector(
238
+ async (request: DataSourceHttpRequest): Promise<DataSourceHttpResponse> => {
239
+ requests.push(request);
240
+
241
+ const url: URL = new URL(request.url);
242
+
243
+ if (url.pathname === "/oauth2/token") {
244
+ return (options.token || tokenResponse)(request);
245
+ }
246
+
247
+ if (url.pathname === "/alerts/queries/alerts/v2") {
248
+ const responder: Responder | undefined = queryResponders.shift();
249
+
250
+ if (!responder) {
251
+ throw new Error(`Unexpected alerts query: ${request.url}`);
252
+ }
253
+
254
+ return responder(request);
255
+ }
256
+
257
+ if (url.pathname === "/alerts/entities/alerts/v2") {
258
+ const responder: Responder | undefined = entityResponders.shift();
259
+
260
+ if (responder) {
261
+ return responder(request);
262
+ }
263
+
264
+ const body: JSONObject = JSON.parse(request.body as string);
265
+ return entitiesFor(body["composite_ids"] as Array<string>);
266
+ }
267
+
268
+ throw new Error(`Unexpected request to ${request.url}`);
269
+ },
270
+ );
271
+
272
+ return { requests, connector };
273
+ }
274
+
275
+ function pathnames(harness: Harness): Array<string> {
276
+ return harness.requests.map((request: DataSourceHttpRequest): string => {
277
+ return new URL(request.url).pathname;
278
+ });
279
+ }
280
+
281
+ function queryRequests(harness: Harness): Array<URL> {
282
+ return harness.requests
283
+ .filter((request: DataSourceHttpRequest): boolean => {
284
+ return request.url.includes("/alerts/queries/alerts/v2");
285
+ })
286
+ .map((request: DataSourceHttpRequest): URL => {
287
+ return new URL(request.url);
288
+ });
289
+ }
290
+
291
+ function checkByKey(
292
+ checks: Array<SecurityConnectorCheck>,
293
+ key: string,
294
+ ): SecurityConnectorCheck {
295
+ const check: SecurityConnectorCheck | undefined = checks.find(
296
+ (candidate: SecurityConnectorCheck): boolean => {
297
+ return candidate.key === key;
298
+ },
299
+ );
300
+
301
+ if (!check) {
302
+ throw new Error(`No check with key ${key}`);
303
+ }
304
+
305
+ return check;
306
+ }
307
+
308
+ function expectNoSecrets(checks: Array<SecurityConnectorCheck>): void {
309
+ const serialized: string = JSON.stringify(checks);
310
+ expect(serialized).not.toContain(CLIENT_SECRET);
311
+ expect(serialized).not.toContain(ACCESS_TOKEN);
312
+ }
313
+
314
+ describe("CrowdStrikeFalconConnector", () => {
315
+ test("registers under the catalog's provider key", () => {
316
+ expect(new CrowdStrikeFalconConnector().provider).toBe(
317
+ SecurityEventConnectorProvider.CrowdStrikeFalcon,
318
+ );
319
+ expect(new CrowdStrikeFalconConnector().provider).toBe(
320
+ "crowdstrike-falcon",
321
+ );
322
+ });
323
+
324
+ describe("validateSettings", () => {
325
+ test("accepts the catalog's config and secret keys, cloud case-insensitively", () => {
326
+ const connector: CrowdStrikeFalconConnector =
327
+ new CrowdStrikeFalconConnector();
328
+
329
+ expect(() => {
330
+ connector.validateSettings(settings());
331
+ }).not.toThrow();
332
+ expect(() => {
333
+ connector.validateSettings(
334
+ settings({ config: { clientId: CLIENT_ID, cloud: "US-GOV-1" } }),
335
+ );
336
+ }).not.toThrow();
337
+ });
338
+
339
+ test("defaults the cloud to us-1 when absent", () => {
340
+ expect(() => {
341
+ new CrowdStrikeFalconConnector().validateSettings(
342
+ settings({ config: { clientId: CLIENT_ID } }),
343
+ );
344
+ }).not.toThrow();
345
+ });
346
+
347
+ test("names the Client ID field from the catalog when it is missing or malformed", () => {
348
+ const connector: CrowdStrikeFalconConnector =
349
+ new CrowdStrikeFalconConnector();
350
+
351
+ for (const clientId of [
352
+ "",
353
+ " ",
354
+ "has space",
355
+ "x".repeat(200),
356
+ "short",
357
+ ]) {
358
+ expect(() => {
359
+ connector.validateSettings(
360
+ settings({ config: { clientId, cloud: "us-1" } }),
361
+ );
362
+ }).toThrow(BadDataException);
363
+ expect(() => {
364
+ connector.validateSettings(
365
+ settings({ config: { clientId, cloud: "us-1" } }),
366
+ );
367
+ }).toThrow(/^Client ID must be the Falcon API client ID/);
368
+ }
369
+ });
370
+
371
+ test("names the Falcon cloud field and lists the valid clouds on an unknown cloud", () => {
372
+ expect(() => {
373
+ new CrowdStrikeFalconConnector().validateSettings(
374
+ settings({ config: { clientId: CLIENT_ID, cloud: "ap-1" } }),
375
+ );
376
+ }).toThrow("Falcon cloud must be one of: us-1, us-2, eu-1, us-gov-1.");
377
+ });
378
+
379
+ test("names the Client secret field when the secret is missing", () => {
380
+ expect(() => {
381
+ new CrowdStrikeFalconConnector().validateSettings(
382
+ settings({ secrets: {} }),
383
+ );
384
+ }).toThrow("Client secret is required.");
385
+ });
386
+
387
+ test("field titles come from the catalog entry", () => {
388
+ const titles: Array<string> = [
389
+ ...DEFINITION.configFields,
390
+ ...DEFINITION.secretFields,
391
+ ].map((field: { title: string }): string => {
392
+ return field.title;
393
+ });
394
+
395
+ expect(titles).toEqual(["Client ID", "Falcon cloud", "Client secret"]);
396
+ });
397
+ });
398
+
399
+ describe("testConnection", () => {
400
+ test("returns authentication, read-permission and detections-available checks, all passing", async () => {
401
+ const harness: Harness = buildHarness({
402
+ queries: [
403
+ (): DataSourceHttpResponse => {
404
+ return idsPage([compositeId(1)], 12);
405
+ },
406
+ (): DataSourceHttpResponse => {
407
+ return idsPage([compositeId(1)], 12);
408
+ },
409
+ (): DataSourceHttpResponse => {
410
+ return idsPage([compositeId(1)], 140);
411
+ },
412
+ ],
413
+ });
414
+ const before: number = Date.now();
415
+
416
+ const checks: Array<SecurityConnectorCheck> =
417
+ await harness.connector.testConnection(settings(), {
418
+ requestTimeoutInMs: 20000,
419
+ });
420
+
421
+ expect(
422
+ checks.map((check: SecurityConnectorCheck): string => {
423
+ return check.key;
424
+ }),
425
+ ).toEqual(["authentication", "read-permission", "detections-available"]);
426
+ expect(
427
+ checks.map((check: SecurityConnectorCheck): string => {
428
+ return check.status;
429
+ }),
430
+ ).toEqual(["pass", "pass", "pass"]);
431
+
432
+ expect(checkByKey(checks, "authentication").message).toContain(
433
+ "api.us-2.crowdstrike.com",
434
+ );
435
+ expect(checkByKey(checks, "read-permission").message).toContain(
436
+ "Alerts: Read",
437
+ );
438
+
439
+ const available: SecurityConnectorCheck = checkByKey(
440
+ checks,
441
+ "detections-available",
442
+ );
443
+ expect(available.message).toBe(
444
+ "12 alerts created in the last 24 hours and 140 in the last 7 days.",
445
+ );
446
+ expect(available.details).toEqual({
447
+ createdLast24h: "12",
448
+ createdLast7d: "140",
449
+ hasMoreLast24h: false,
450
+ hasMoreLast7d: false,
451
+ });
452
+ expect(available.remediation).toBeUndefined();
453
+ expectNoSecrets(checks);
454
+
455
+ // One token request, then three one-record probes.
456
+ expect(pathnames(harness)).toEqual([
457
+ "/oauth2/token",
458
+ "/alerts/queries/alerts/v2",
459
+ "/alerts/queries/alerts/v2",
460
+ "/alerts/queries/alerts/v2",
461
+ ]);
462
+
463
+ for (const request of harness.requests) {
464
+ expect(request.timeoutInMs).toBe(20000);
465
+ }
466
+
467
+ const probes: Array<URL> = queryRequests(harness);
468
+
469
+ for (const probe of probes) {
470
+ expect(Array.from(probe.searchParams.keys()).sort()).toEqual([
471
+ "filter",
472
+ "limit",
473
+ "offset",
474
+ "sort",
475
+ ]);
476
+ expect(probe.searchParams.get("limit")).toBe("1");
477
+ expect(probe.searchParams.get("offset")).toBe("0");
478
+ }
479
+
480
+ /*
481
+ * The read probe and the 24h count cover the last day; the 7d count
482
+ * covers the last week. Parsed from the FQL rather than matched as a
483
+ * substring so a reordered filter still passes.
484
+ */
485
+ const windows: Array<[Date, Date]> = probes.map(
486
+ (probe: URL): [Date, Date] => {
487
+ const filter: string = probe.searchParams.get("filter") || "";
488
+ const match: RegExpMatchArray | null = filter.match(
489
+ /^created_timestamp:>='([^']+)'\+created_timestamp:<'([^']+)'$/,
490
+ );
491
+ expect(match).not.toBeNull();
492
+ return [new Date(match![1]!), new Date(match![2]!)];
493
+ },
494
+ );
495
+
496
+ const dayInMs: number = 24 * 60 * 60 * 1000;
497
+ expect(windows[0]![1].getTime() - windows[0]![0].getTime()).toBe(dayInMs);
498
+ expect(windows[1]![1].getTime() - windows[1]![0].getTime()).toBe(dayInMs);
499
+ expect(windows[2]![1].getTime() - windows[2]![0].getTime()).toBe(
500
+ 7 * dayInMs,
501
+ );
502
+ expect(windows[0]![1].getTime()).toBeGreaterThanOrEqual(before);
503
+ });
504
+
505
+ test("warns instead of passing when no alert was created in the last 7 days", async () => {
506
+ const harness: Harness = buildHarness({
507
+ queries: [
508
+ (): DataSourceHttpResponse => {
509
+ return idsPage([], 0);
510
+ },
511
+ (): DataSourceHttpResponse => {
512
+ return idsPage([], 0);
513
+ },
514
+ (): DataSourceHttpResponse => {
515
+ return idsPage([], 0);
516
+ },
517
+ ],
518
+ });
519
+
520
+ const checks: Array<SecurityConnectorCheck> =
521
+ await harness.connector.testConnection(settings(), {
522
+ requestTimeoutInMs: 20000,
523
+ });
524
+
525
+ const available: SecurityConnectorCheck = checkByKey(
526
+ checks,
527
+ "detections-available",
528
+ );
529
+ expect(available.status).toBe("warn");
530
+ expect(available.message).toBe(
531
+ "No alerts were created in the last 7 days. Polling will import new alerts as Falcon creates them.",
532
+ );
533
+ expect(available.remediation).toContain("Falcon console");
534
+ });
535
+
536
+ test("labels counts as N+ when Falcon omits the pagination total", async () => {
537
+ const harness: Harness = buildHarness({
538
+ queries: [
539
+ (): DataSourceHttpResponse => {
540
+ return idsPage([compositeId(1)], null);
541
+ },
542
+ (): DataSourceHttpResponse => {
543
+ return idsPage([compositeId(1)], null);
544
+ },
545
+ (): DataSourceHttpResponse => {
546
+ return idsPage([compositeId(1)], null);
547
+ },
548
+ ],
549
+ });
550
+
551
+ const checks: Array<SecurityConnectorCheck> =
552
+ await harness.connector.testConnection(settings(), {
553
+ requestTimeoutInMs: 20000,
554
+ });
555
+
556
+ const available: SecurityConnectorCheck = checkByKey(
557
+ checks,
558
+ "detections-available",
559
+ );
560
+ expect(available.status).toBe("pass");
561
+ expect(available.message).toBe(
562
+ "1+ alerts created in the last 24 hours and 1+ in the last 7 days.",
563
+ );
564
+ expect(available.details?.["hasMoreLast24h"]).toBe(true);
565
+ });
566
+
567
+ test("fails authentication without contacting Falcon when the settings are malformed", async () => {
568
+ const harness: Harness = buildHarness({});
569
+
570
+ const checks: Array<SecurityConnectorCheck> =
571
+ await harness.connector.testConnection(
572
+ settings({ config: { clientId: CLIENT_ID, cloud: "mars-1" } }),
573
+ { requestTimeoutInMs: 20000 },
574
+ );
575
+
576
+ expect(checks).toHaveLength(1);
577
+ expect(checks[0]!.key).toBe("authentication");
578
+ expect(checks[0]!.status).toBe("fail");
579
+ expect(checks[0]!.message).toContain("Falcon cloud must be one of");
580
+ expect(harness.requests).toHaveLength(0);
581
+ });
582
+
583
+ test("fails authentication on a 401 with cloud and credential remediation, and skips the later checks", async () => {
584
+ const harness: Harness = buildHarness({
585
+ token: (): DataSourceHttpResponse => {
586
+ return status(
587
+ 401,
588
+ JSON.stringify({
589
+ errors: [
590
+ {
591
+ code: 401,
592
+ message: `access denied, invalid bearer token client_secret=${CLIENT_SECRET}`,
593
+ },
594
+ ],
595
+ }),
596
+ );
597
+ },
598
+ });
599
+
600
+ const checks: Array<SecurityConnectorCheck> =
601
+ await harness.connector.testConnection(settings(), {
602
+ requestTimeoutInMs: 20000,
603
+ });
604
+
605
+ expect(
606
+ checks.map((check: SecurityConnectorCheck): [string, string] => {
607
+ return [check.key, check.status];
608
+ }),
609
+ ).toEqual([
610
+ ["authentication", "fail"],
611
+ ["read-permission", "skip"],
612
+ ["detections-available", "skip"],
613
+ ]);
614
+
615
+ const auth: SecurityConnectorCheck = checkByKey(checks, "authentication");
616
+ expect(auth.message).toMatch(
617
+ /^CrowdStrike Falcon token request failed \(HTTP 401\): /,
618
+ );
619
+ expect(auth.remediation).toContain("Falcon cloud (us-2)");
620
+ expect(auth.remediation).toContain("Client secret");
621
+ expectNoSecrets(checks);
622
+ expect(pathnames(harness)).toEqual(["/oauth2/token"]);
623
+ });
624
+
625
+ test("fails authentication on a timeout with a connectivity remediation naming the cloud host", async () => {
626
+ const harness: Harness = buildHarness({
627
+ token: (): DataSourceHttpResponse => {
628
+ throw new BadDataException(
629
+ "Could not reach data source: timeout of 20000ms exceeded",
630
+ );
631
+ },
632
+ });
633
+
634
+ const checks: Array<SecurityConnectorCheck> =
635
+ await harness.connector.testConnection(settings(), {
636
+ requestTimeoutInMs: 20000,
637
+ });
638
+
639
+ const auth: SecurityConnectorCheck = checkByKey(checks, "authentication");
640
+ expect(auth.status).toBe("fail");
641
+ expect(auth.message).toBe(
642
+ "CrowdStrike Falcon token request could not be completed: Could not reach data source: timeout of 20000ms exceeded",
643
+ );
644
+ expect(auth.remediation).toContain("api.us-2.crowdstrike.com");
645
+ });
646
+
647
+ test("fails read-permission on a 403 with the Alerts: Read scope remediation and skips the count", async () => {
648
+ const harness: Harness = buildHarness({
649
+ queries: [
650
+ (): DataSourceHttpResponse => {
651
+ return status(
652
+ 403,
653
+ JSON.stringify({
654
+ errors: [
655
+ { code: 403, message: "access denied, authorization failed" },
656
+ ],
657
+ }),
658
+ );
659
+ },
660
+ ],
661
+ });
662
+
663
+ const checks: Array<SecurityConnectorCheck> =
664
+ await harness.connector.testConnection(settings(), {
665
+ requestTimeoutInMs: 20000,
666
+ });
667
+
668
+ expect(
669
+ checks.map((check: SecurityConnectorCheck): [string, string] => {
670
+ return [check.key, check.status];
671
+ }),
672
+ ).toEqual([
673
+ ["authentication", "pass"],
674
+ ["read-permission", "fail"],
675
+ ["detections-available", "skip"],
676
+ ]);
677
+
678
+ const read: SecurityConnectorCheck = checkByKey(
679
+ checks,
680
+ "read-permission",
681
+ );
682
+ expect(read.message).toMatch(
683
+ /^CrowdStrike Falcon alerts query failed \(HTTP 403\): /,
684
+ );
685
+ expect(read.remediation).toContain("Alerts: Read");
686
+ expectNoSecrets(checks);
687
+ });
688
+
689
+ test("names the rate limit header in the read-permission remediation on a 429", async () => {
690
+ const harness: Harness = buildHarness({
691
+ queries: [
692
+ (): DataSourceHttpResponse => {
693
+ return status(
694
+ 429,
695
+ JSON.stringify({
696
+ errors: [{ code: 429, message: "Too Many Requests" }],
697
+ }),
698
+ {
699
+ "x-ratelimit-retryafter": "1757757600",
700
+ },
701
+ );
702
+ },
703
+ ],
704
+ });
705
+
706
+ const checks: Array<SecurityConnectorCheck> =
707
+ await harness.connector.testConnection(settings(), {
708
+ requestTimeoutInMs: 20000,
709
+ });
710
+
711
+ const read: SecurityConnectorCheck = checkByKey(
712
+ checks,
713
+ "read-permission",
714
+ );
715
+ expect(read.status).toBe("fail");
716
+ expect(read.message).toContain("X-RateLimit-RetryAfter: 1757757600");
717
+ expect(read.remediation).toContain("X-RateLimit-RetryAfter");
718
+ });
719
+
720
+ test("fails detections-available when a count request errors after the read probe passed", async () => {
721
+ const harness: Harness = buildHarness({
722
+ queries: [
723
+ (): DataSourceHttpResponse => {
724
+ return idsPage([compositeId(1)], 1);
725
+ },
726
+ (): DataSourceHttpResponse => {
727
+ return idsPage([compositeId(1)], 1);
728
+ },
729
+ (): DataSourceHttpResponse => {
730
+ return status(500, "upstream connect error");
731
+ },
732
+ ],
733
+ });
734
+
735
+ const checks: Array<SecurityConnectorCheck> =
736
+ await harness.connector.testConnection(settings(), {
737
+ requestTimeoutInMs: 20000,
738
+ });
739
+
740
+ expect(checkByKey(checks, "read-permission").status).toBe("pass");
741
+
742
+ const available: SecurityConnectorCheck = checkByKey(
743
+ checks,
744
+ "detections-available",
745
+ );
746
+ expect(available.status).toBe("fail");
747
+ expect(available.message).toMatch(
748
+ /^CrowdStrike Falcon alerts query failed \(HTTP 500\): /,
749
+ );
750
+ expect(available.remediation).toBeTruthy();
751
+ });
752
+ });
753
+
754
+ describe("fetchEvents", () => {
755
+ test("queries one page by created_timestamp, fetches its entities and normalizes them", async () => {
756
+ const harness: Harness = buildHarness({
757
+ queries: [
758
+ (): DataSourceHttpResponse => {
759
+ return idsPage([compositeId(1), compositeId(2)], 2);
760
+ },
761
+ ],
762
+ });
763
+
764
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
765
+ settings(),
766
+ { startTime: START, endTime: END },
767
+ fetchOptions({ requestTimeoutInMs: 7777 }),
768
+ );
769
+
770
+ expect(result.complete).toBe(true);
771
+ expect(result.resumeAfter).toBeUndefined();
772
+ expect(result.warnings).toEqual([]);
773
+ expect(result.fetchedCount).toBe(2);
774
+ expect(result.rejectedCount).toBe(0);
775
+ expect(result.failedCount).toBe(0);
776
+ expect(result.requestCount).toBe(3);
777
+ expect(result.events).toHaveLength(2);
778
+
779
+ const first: JSONObject = result.events[0] as unknown as JSONObject;
780
+ expect(first["eventUid"]).toBe(compositeId(1));
781
+ expect(first["vendorName"]).toBe(DEFINITION.vendorName);
782
+ expect(first["productName"]).toBe(DEFINITION.productName);
783
+ expect(first["classUid"]).toBe(2004);
784
+ expect(first["severityName"]).toBe(OcsfSeverity.Low);
785
+ expect(first["principalHost"]).toBe("ABC709-1175");
786
+ expect(first["mitreTactics"]).toEqual(["CSTA0004"]);
787
+ expect((first["time"] as Date).toISOString()).toBe(
788
+ "2026-09-12T18:01:23.995Z",
789
+ );
790
+
791
+ expect(result.samples).toEqual([
792
+ {
793
+ id: compositeId(1),
794
+ title:
795
+ "This file meets the Adware/PUP Anti-malware ML algorithm's lowest-confidence threshold.",
796
+ severity: OcsfSeverity.Low,
797
+ createdTime: "2026-09-12T18:01:23.995Z",
798
+ eventTime: "2026-09-12T18:00:22.328Z",
799
+ },
800
+ {
801
+ id: compositeId(2),
802
+ title:
803
+ "This file meets the Adware/PUP Anti-malware ML algorithm's lowest-confidence threshold.",
804
+ severity: OcsfSeverity.Low,
805
+ createdTime: "2026-09-12T18:01:23.995Z",
806
+ eventTime: "2026-09-12T18:00:22.328Z",
807
+ },
808
+ ]);
809
+
810
+ expect(pathnames(harness)).toEqual([
811
+ "/oauth2/token",
812
+ "/alerts/queries/alerts/v2",
813
+ "/alerts/entities/alerts/v2",
814
+ ]);
815
+
816
+ const query: URL = queryRequests(harness)[0]!;
817
+ expect(query.origin).toBe("https://api.us-2.crowdstrike.com");
818
+ expect(Array.from(query.searchParams.keys()).sort()).toEqual([
819
+ "filter",
820
+ "limit",
821
+ "offset",
822
+ "sort",
823
+ ]);
824
+ expect(query.searchParams.get("filter")).toBe(
825
+ CrowdStrikeFalconClient.buildCreatedTimestampFilter(START, END),
826
+ );
827
+ expect(query.searchParams.get("sort")).toBe("created_timestamp.asc");
828
+ expect(query.searchParams.get("limit")).toBe(
829
+ String(CROWDSTRIKE_ALERTS_PAGE_SIZE),
830
+ );
831
+ expect(query.searchParams.get("offset")).toBe("0");
832
+
833
+ const entityRequest: DataSourceHttpRequest = harness.requests[2]!;
834
+ expect(entityRequest.method).toBe("POST");
835
+ expect(JSON.parse(entityRequest.body as string)).toEqual({
836
+ composite_ids: [compositeId(1), compositeId(2)],
837
+ });
838
+
839
+ for (const request of harness.requests) {
840
+ expect(request.timeoutInMs).toBe(7777);
841
+ }
842
+ });
843
+
844
+ test("returns an empty, complete result for a quiet window without an entity request", async () => {
845
+ const harness: Harness = buildHarness({
846
+ queries: [
847
+ (): DataSourceHttpResponse => {
848
+ return idsPage([], 0);
849
+ },
850
+ ],
851
+ });
852
+
853
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
854
+ settings(),
855
+ { startTime: START, endTime: END },
856
+ fetchOptions(),
857
+ );
858
+
859
+ expect(result.complete).toBe(true);
860
+ expect(result.events).toEqual([]);
861
+ expect(result.fetchedCount).toBe(0);
862
+ expect(result.requestCount).toBe(2);
863
+ expect(pathnames(harness)).toEqual([
864
+ "/oauth2/token",
865
+ "/alerts/queries/alerts/v2",
866
+ ]);
867
+ });
868
+
869
+ test("pages by offset until the reported total is reached", async () => {
870
+ const firstPage: Array<string> = [];
871
+ const secondPage: Array<string> = [];
872
+
873
+ for (
874
+ let index: number = 0;
875
+ index < CROWDSTRIKE_ALERTS_PAGE_SIZE;
876
+ index++
877
+ ) {
878
+ firstPage.push(compositeId(index));
879
+ }
880
+
881
+ for (let index: number = 0; index < 5; index++) {
882
+ secondPage.push(compositeId(CROWDSTRIKE_ALERTS_PAGE_SIZE + index));
883
+ }
884
+
885
+ const harness: Harness = buildHarness({
886
+ queries: [
887
+ (): DataSourceHttpResponse => {
888
+ return idsPage(firstPage, 1005);
889
+ },
890
+ (): DataSourceHttpResponse => {
891
+ return idsPage(secondPage, 1005);
892
+ },
893
+ ],
894
+ });
895
+
896
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
897
+ settings(),
898
+ { startTime: START, endTime: END },
899
+ fetchOptions(),
900
+ );
901
+
902
+ expect(result.complete).toBe(true);
903
+ expect(result.resumeAfter).toBeUndefined();
904
+ expect(result.warnings).toEqual([]);
905
+ expect(result.fetchedCount).toBe(1005);
906
+ expect(result.events).toHaveLength(1005);
907
+ expect(result.requestCount).toBe(5);
908
+ expect(result.samples).toHaveLength(25);
909
+
910
+ const offsets: Array<string | null> = queryRequests(harness).map(
911
+ (url: URL): string | null => {
912
+ return url.searchParams.get("offset");
913
+ },
914
+ );
915
+ expect(offsets).toEqual(["0", "1000"]);
916
+ });
917
+
918
+ test("stops when a page is full but the total says the window is fully read", async () => {
919
+ const fullPage: Array<string> = [];
920
+
921
+ for (
922
+ let index: number = 0;
923
+ index < CROWDSTRIKE_ALERTS_PAGE_SIZE;
924
+ index++
925
+ ) {
926
+ fullPage.push(compositeId(index));
927
+ }
928
+
929
+ const harness: Harness = buildHarness({
930
+ queries: [
931
+ (): DataSourceHttpResponse => {
932
+ return idsPage(fullPage, CROWDSTRIKE_ALERTS_PAGE_SIZE);
933
+ },
934
+ ],
935
+ });
936
+
937
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
938
+ settings(),
939
+ { startTime: START, endTime: END },
940
+ fetchOptions(),
941
+ );
942
+
943
+ expect(result.complete).toBe(true);
944
+ expect(result.fetchedCount).toBe(CROWDSTRIKE_ALERTS_PAGE_SIZE);
945
+ expect(queryRequests(harness)).toHaveLength(1);
946
+ });
947
+
948
+ test("keeps paging without a total until Falcon returns a short page", async () => {
949
+ const fullPage: Array<string> = [];
950
+
951
+ for (
952
+ let index: number = 0;
953
+ index < CROWDSTRIKE_ALERTS_PAGE_SIZE;
954
+ index++
955
+ ) {
956
+ fullPage.push(compositeId(index));
957
+ }
958
+
959
+ const harness: Harness = buildHarness({
960
+ queries: [
961
+ (): DataSourceHttpResponse => {
962
+ return idsPage(fullPage, null);
963
+ },
964
+ (): DataSourceHttpResponse => {
965
+ return idsPage([], null);
966
+ },
967
+ ],
968
+ });
969
+
970
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
971
+ settings(),
972
+ { startTime: START, endTime: END },
973
+ fetchOptions(),
974
+ );
975
+
976
+ expect(result.complete).toBe(true);
977
+ expect(result.fetchedCount).toBe(CROWDSTRIKE_ALERTS_PAGE_SIZE);
978
+ expect(queryRequests(harness)).toHaveLength(2);
979
+ expect(pathnames(harness)).toEqual([
980
+ "/oauth2/token",
981
+ "/alerts/queries/alerts/v2",
982
+ "/alerts/entities/alerts/v2",
983
+ "/alerts/queries/alerts/v2",
984
+ ]);
985
+ });
986
+
987
+ test("drops a composite id repeated across pages by live offset paging", async () => {
988
+ const harness: Harness = buildHarness({
989
+ queries: [
990
+ (): DataSourceHttpResponse => {
991
+ return idsPage(
992
+ Array.from(
993
+ { length: CROWDSTRIKE_ALERTS_PAGE_SIZE },
994
+ (_: unknown, index: number): string => {
995
+ return compositeId(index);
996
+ },
997
+ ),
998
+ 1002,
999
+ );
1000
+ },
1001
+ (): DataSourceHttpResponse => {
1002
+ return idsPage([compositeId(999), compositeId(1000)], 1002);
1003
+ },
1004
+ ],
1005
+ });
1006
+
1007
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1008
+ settings(),
1009
+ { startTime: START, endTime: END },
1010
+ fetchOptions(),
1011
+ );
1012
+
1013
+ expect(result.complete).toBe(true);
1014
+ expect(result.fetchedCount).toBe(1001);
1015
+
1016
+ const secondEntityBody: JSONObject = JSON.parse(
1017
+ harness.requests[4]!.body as string,
1018
+ );
1019
+ expect(secondEntityBody["composite_ids"]).toEqual([compositeId(1000)]);
1020
+ });
1021
+
1022
+ /*
1023
+ * Review finding connector-bound-hit-permanent-stall: every bound used to
1024
+ * return complete=false with nothing to resume from and a warning
1025
+ * promising "the poll cursor is held so the next poll continues from the
1026
+ * same window", so a CID with more than 9,000 alerts in a window re-read
1027
+ * the same first 9,000 forever. Each bound now reports the
1028
+ * created_timestamp of the last alert read as resumeAfter.
1029
+ */
1030
+ test("stops at the request bound with complete=false, resumeAfter at the last alert read, and a warning naming it", async () => {
1031
+ /*
1032
+ * Each page carries its own ids: the connector drops ids it has
1033
+ * already seen, so a repeated fixture would skip the entity fetch
1034
+ * and change the request arithmetic under test.
1035
+ */
1036
+ const pageOfIds: (page: number) => Array<string> = (
1037
+ page: number,
1038
+ ): Array<string> => {
1039
+ const ids: Array<string> = [];
1040
+
1041
+ for (
1042
+ let index: number = 0;
1043
+ index < CROWDSTRIKE_ALERTS_PAGE_SIZE;
1044
+ index++
1045
+ ) {
1046
+ ids.push(compositeId(page * CROWDSTRIKE_ALERTS_PAGE_SIZE + index));
1047
+ }
1048
+
1049
+ return ids;
1050
+ };
1051
+
1052
+ const harness: Harness = buildHarness({
1053
+ queries: [
1054
+ (): DataSourceHttpResponse => {
1055
+ return idsPage(pageOfIds(0), 5000);
1056
+ },
1057
+ (): DataSourceHttpResponse => {
1058
+ return idsPage(pageOfIds(1), 5000);
1059
+ },
1060
+ (): DataSourceHttpResponse => {
1061
+ return idsPage(pageOfIds(2), 5000);
1062
+ },
1063
+ ],
1064
+ entities: [
1065
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1066
+ return datedEntitiesFor(
1067
+ entityRequestIds(request),
1068
+ compositeIdIndex,
1069
+ );
1070
+ },
1071
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1072
+ return datedEntitiesFor(
1073
+ entityRequestIds(request),
1074
+ compositeIdIndex,
1075
+ );
1076
+ },
1077
+ ],
1078
+ });
1079
+
1080
+ // token + (query + fetch) * 2 = 5; a third page would need 7.
1081
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1082
+ settings(),
1083
+ { startTime: START, endTime: END },
1084
+ fetchOptions({ maxRequests: 6 }),
1085
+ );
1086
+
1087
+ const lastRead: string = createdAtFor(
1088
+ 2 * CROWDSTRIKE_ALERTS_PAGE_SIZE - 1,
1089
+ );
1090
+
1091
+ expect(result.complete).toBe(false);
1092
+ expect(result.requestCount).toBe(5);
1093
+ expect(result.fetchedCount).toBe(2 * CROWDSTRIKE_ALERTS_PAGE_SIZE);
1094
+ expect(result.resumeAfter).toEqual(new Date(lastRead));
1095
+ expect(result.warnings).toEqual([
1096
+ `Stopped after 5 requests before reading the whole window. Alerts are read oldest first; every alert created before ${lastRead} was read.`,
1097
+ ]);
1098
+ });
1099
+
1100
+ test("stops at the request bound the poller really uses (20) after 9 pages, with resumeAfter so the next poll moves on", async () => {
1101
+ const queries: Array<Responder> = [];
1102
+
1103
+ for (let page: number = 0; page < 10; page++) {
1104
+ queries.push((): DataSourceHttpResponse => {
1105
+ return idsPage(
1106
+ Array.from(
1107
+ { length: CROWDSTRIKE_ALERTS_PAGE_SIZE },
1108
+ (_: unknown, index: number): string => {
1109
+ return compositeId(page * CROWDSTRIKE_ALERTS_PAGE_SIZE + index);
1110
+ },
1111
+ ),
1112
+ 12000,
1113
+ );
1114
+ });
1115
+ }
1116
+
1117
+ const entities: Array<Responder> = [];
1118
+
1119
+ for (let page: number = 0; page < 10; page++) {
1120
+ entities.push(
1121
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1122
+ return datedEntitiesFor(
1123
+ entityRequestIds(request),
1124
+ compositeIdIndex,
1125
+ );
1126
+ },
1127
+ );
1128
+ }
1129
+
1130
+ const harness: Harness = buildHarness({ queries, entities });
1131
+
1132
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1133
+ settings(),
1134
+ { startTime: START, endTime: END },
1135
+ fetchOptions({ maxRequests: 20, maxEvents: 10000 }),
1136
+ );
1137
+
1138
+ const lastRead: string = createdAtFor(
1139
+ 9 * CROWDSTRIKE_ALERTS_PAGE_SIZE - 1,
1140
+ );
1141
+
1142
+ expect(queryRequests(harness)).toHaveLength(9);
1143
+ expect(result.requestCount).toBe(19);
1144
+ expect(result.fetchedCount).toBe(9 * CROWDSTRIKE_ALERTS_PAGE_SIZE);
1145
+ expect(result.complete).toBe(false);
1146
+ expect(result.resumeAfter).toEqual(new Date(lastRead));
1147
+ expect(result.warnings).toEqual([
1148
+ `Stopped after 19 requests before reading the whole window. Alerts are read oldest first; every alert created before ${lastRead} was read.`,
1149
+ ]);
1150
+ });
1151
+
1152
+ test("stops at the event bound mid-page with complete=false, fetching only the ids that fit and resuming after the last one", async () => {
1153
+ const harness: Harness = buildHarness({
1154
+ queries: [
1155
+ (): DataSourceHttpResponse => {
1156
+ return idsPage([compositeId(1), compositeId(2), compositeId(3)], 3);
1157
+ },
1158
+ ],
1159
+ entities: [
1160
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1161
+ return datedEntitiesFor(
1162
+ entityRequestIds(request),
1163
+ compositeIdIndex,
1164
+ );
1165
+ },
1166
+ ],
1167
+ });
1168
+
1169
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1170
+ settings(),
1171
+ { startTime: START, endTime: END },
1172
+ fetchOptions({ maxEvents: 2 }),
1173
+ );
1174
+
1175
+ expect(result.complete).toBe(false);
1176
+ expect(result.fetchedCount).toBe(2);
1177
+ expect(result.events).toHaveLength(2);
1178
+ expect(result.resumeAfter).toEqual(new Date(createdAtFor(2)));
1179
+ expect(result.warnings).toEqual([
1180
+ `Stopped after collecting 2 alerts; the window holds more. Alerts are read oldest first; every alert created before ${createdAtFor(2)} was read.`,
1181
+ ]);
1182
+ expect(JSON.parse(harness.requests[2]!.body as string)).toEqual({
1183
+ composite_ids: [compositeId(1), compositeId(2)],
1184
+ });
1185
+ });
1186
+
1187
+ test("stops at the event bound on a page boundary when more pages remain", async () => {
1188
+ const fullPage: Array<string> = [];
1189
+
1190
+ for (
1191
+ let index: number = 0;
1192
+ index < CROWDSTRIKE_ALERTS_PAGE_SIZE;
1193
+ index++
1194
+ ) {
1195
+ fullPage.push(compositeId(index));
1196
+ }
1197
+
1198
+ const harness: Harness = buildHarness({
1199
+ queries: [
1200
+ (): DataSourceHttpResponse => {
1201
+ return idsPage(fullPage, 3000);
1202
+ },
1203
+ ],
1204
+ entities: [
1205
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1206
+ return datedEntitiesFor(
1207
+ entityRequestIds(request),
1208
+ compositeIdIndex,
1209
+ );
1210
+ },
1211
+ ],
1212
+ });
1213
+
1214
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1215
+ settings(),
1216
+ { startTime: START, endTime: END },
1217
+ fetchOptions({ maxEvents: CROWDSTRIKE_ALERTS_PAGE_SIZE }),
1218
+ );
1219
+
1220
+ expect(result.complete).toBe(false);
1221
+ expect(result.fetchedCount).toBe(CROWDSTRIKE_ALERTS_PAGE_SIZE);
1222
+ expect(result.resumeAfter).toEqual(
1223
+ new Date(createdAtFor(CROWDSTRIKE_ALERTS_PAGE_SIZE - 1)),
1224
+ );
1225
+ expect(result.warnings).toHaveLength(1);
1226
+ expect(result.warnings[0]).toContain(
1227
+ `Stopped after collecting ${CROWDSTRIKE_ALERTS_PAGE_SIZE} alerts`,
1228
+ );
1229
+ expect(queryRequests(harness)).toHaveLength(1);
1230
+ });
1231
+
1232
+ /*
1233
+ * Review findings connector-bound-hit-permanent-stall and
1234
+ * crowdstrike-docs-quote-unreachable-warning: the ceiling warning used
1235
+ * to hold the cursor and advise shortening the poll interval, which
1236
+ * could not unpin a held window. The ceiling now resumes like every
1237
+ * other bound, and the next poll queries from offset 0 past it.
1238
+ */
1239
+ test("stops at Falcon's 10,000 offset ceiling with complete=false and resumeAfter so the next poll queries past it", async () => {
1240
+ const pages: number =
1241
+ CROWDSTRIKE_ALERTS_OFFSET_CEILING / CROWDSTRIKE_ALERTS_PAGE_SIZE;
1242
+ const queries: Array<Responder> = [];
1243
+ const entities: Array<Responder> = [];
1244
+
1245
+ for (let page: number = 0; page < pages; page++) {
1246
+ queries.push((): DataSourceHttpResponse => {
1247
+ return idsPage(
1248
+ Array.from(
1249
+ { length: CROWDSTRIKE_ALERTS_PAGE_SIZE },
1250
+ (_: unknown, index: number): string => {
1251
+ return compositeId(page * CROWDSTRIKE_ALERTS_PAGE_SIZE + index);
1252
+ },
1253
+ ),
1254
+ CROWDSTRIKE_ALERTS_OFFSET_CEILING + 500,
1255
+ );
1256
+ });
1257
+ entities.push(
1258
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1259
+ return datedEntitiesFor(
1260
+ entityRequestIds(request),
1261
+ compositeIdIndex,
1262
+ );
1263
+ },
1264
+ );
1265
+ }
1266
+
1267
+ const harness: Harness = buildHarness({ queries, entities });
1268
+
1269
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1270
+ settings(),
1271
+ { startTime: START, endTime: END },
1272
+ fetchOptions({ maxRequests: 100, maxEvents: 100000 }),
1273
+ );
1274
+
1275
+ const lastRead: string = createdAtFor(
1276
+ CROWDSTRIKE_ALERTS_OFFSET_CEILING - 1,
1277
+ );
1278
+
1279
+ expect(result.complete).toBe(false);
1280
+ expect(result.fetchedCount).toBe(CROWDSTRIKE_ALERTS_OFFSET_CEILING);
1281
+ expect(queryRequests(harness)).toHaveLength(pages);
1282
+ expect(result.resumeAfter).toEqual(new Date(lastRead));
1283
+ expect(result.warnings).toEqual([
1284
+ `Falcon's alerts query cannot page past ${CROWDSTRIKE_ALERTS_OFFSET_CEILING} results and this window holds more. Alerts are read oldest first; every alert created before ${lastRead} was read.`,
1285
+ ]);
1286
+ expect(result.warnings[0]).not.toContain("Poll interval");
1287
+ expect(result.warnings[0]).not.toContain("cursor is held");
1288
+ });
1289
+
1290
+ test("puts entities back in the ids query's order before naming the resume point", async () => {
1291
+ const harness: Harness = buildHarness({
1292
+ queries: [
1293
+ (): DataSourceHttpResponse => {
1294
+ return idsPage([compositeId(1), compositeId(2), compositeId(3)], 9);
1295
+ },
1296
+ ],
1297
+ entities: [
1298
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1299
+ /*
1300
+ * Falcon may answer the entity fetch in any order. Read in
1301
+ * response order these would look out of order and name no
1302
+ * resume point at all.
1303
+ */
1304
+ return datedEntitiesFor(
1305
+ [...entityRequestIds(request)].reverse(),
1306
+ compositeIdIndex,
1307
+ );
1308
+ },
1309
+ ],
1310
+ });
1311
+
1312
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1313
+ settings(),
1314
+ { startTime: START, endTime: END },
1315
+ fetchOptions({ maxEvents: 2 }),
1316
+ );
1317
+
1318
+ expect(result.complete).toBe(false);
1319
+ expect(result.resumeAfter).toEqual(new Date(createdAtFor(2)));
1320
+ expect(result.warnings).toHaveLength(1);
1321
+ expect(
1322
+ result.events.map((event: { eventUid: string }): string => {
1323
+ return event.eventUid;
1324
+ }),
1325
+ ).toEqual([compositeId(1), compositeId(2)]);
1326
+ });
1327
+
1328
+ test("names no resume point, and says why, when the ids query did not honour ascending creation order", async () => {
1329
+ const harness: Harness = buildHarness({
1330
+ queries: [
1331
+ (): DataSourceHttpResponse => {
1332
+ return idsPage([compositeId(5), compositeId(2), compositeId(7)], 9);
1333
+ },
1334
+ ],
1335
+ entities: [
1336
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1337
+ return datedEntitiesFor(
1338
+ entityRequestIds(request),
1339
+ compositeIdIndex,
1340
+ );
1341
+ },
1342
+ ],
1343
+ });
1344
+
1345
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1346
+ settings(),
1347
+ { startTime: START, endTime: END },
1348
+ fetchOptions({ maxEvents: 2 }),
1349
+ );
1350
+
1351
+ expect(result.complete).toBe(false);
1352
+ expect(result.resumeAfter).toBeUndefined();
1353
+ expect(result.warnings).toEqual([
1354
+ "Stopped after collecting 2 alerts; the window holds more.",
1355
+ "Falcon returned alerts out of created_timestamp order, so this run cannot name a point to resume from.",
1356
+ ]);
1357
+ });
1358
+
1359
+ test("never resumes past an alert that failed normalization, so the poller can retry it", async () => {
1360
+ const harness: Harness = buildHarness({
1361
+ queries: [
1362
+ (): DataSourceHttpResponse => {
1363
+ return idsPage(
1364
+ [compositeId(1), compositeId(2), compositeId(3), compositeId(4)],
1365
+ 9,
1366
+ );
1367
+ },
1368
+ ],
1369
+ entities: [
1370
+ (request: DataSourceHttpRequest): DataSourceHttpResponse => {
1371
+ const response: DataSourceHttpResponse = datedEntitiesFor(
1372
+ entityRequestIds(request),
1373
+ compositeIdIndex,
1374
+ );
1375
+ const resources: Array<JSONObject> = (
1376
+ response.bodyJson as JSONObject
1377
+ )["resources"] as Array<JSONObject>;
1378
+ // A getter that throws makes the normalizer fail on this alert only.
1379
+ Object.defineProperty(resources[1]!, "display_name", {
1380
+ enumerable: true,
1381
+ get: (): string => {
1382
+ throw new Error("boom");
1383
+ },
1384
+ });
1385
+ return response;
1386
+ },
1387
+ ],
1388
+ });
1389
+
1390
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1391
+ settings(),
1392
+ { startTime: START, endTime: END },
1393
+ fetchOptions({ maxEvents: 3 }),
1394
+ );
1395
+
1396
+ expect(result.complete).toBe(false);
1397
+ expect(result.failedCount).toBe(1);
1398
+ expect(result.resumeAfter).toEqual(new Date(createdAtFor(2)));
1399
+ });
1400
+
1401
+ test("counts unrecognized entity objects as rejected without failing the fetch", async () => {
1402
+ const harness: Harness = buildHarness({
1403
+ queries: [
1404
+ (): DataSourceHttpResponse => {
1405
+ return idsPage([compositeId(1), compositeId(2)], 2);
1406
+ },
1407
+ ],
1408
+ entities: [
1409
+ (): DataSourceHttpResponse => {
1410
+ return ok({
1411
+ resources: [
1412
+ alert(compositeId(1)),
1413
+ { unrelated: true, message: "not an alert" },
1414
+ ],
1415
+ });
1416
+ },
1417
+ ],
1418
+ });
1419
+
1420
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1421
+ settings(),
1422
+ { startTime: START, endTime: END },
1423
+ fetchOptions(),
1424
+ );
1425
+
1426
+ expect(result.complete).toBe(true);
1427
+ expect(result.fetchedCount).toBe(2);
1428
+ expect(result.rejectedCount).toBe(1);
1429
+ expect(result.events).toHaveLength(1);
1430
+ });
1431
+
1432
+ test("caps samples at sampleLimit while normalizing every alert", async () => {
1433
+ const harness: Harness = buildHarness({
1434
+ queries: [
1435
+ (): DataSourceHttpResponse => {
1436
+ return idsPage([compositeId(1), compositeId(2), compositeId(3)], 3);
1437
+ },
1438
+ ],
1439
+ });
1440
+
1441
+ const result: ConnectorFetchResult = await harness.connector.fetchEvents(
1442
+ settings(),
1443
+ { startTime: START, endTime: END },
1444
+ fetchOptions({ sampleLimit: 1 }),
1445
+ );
1446
+
1447
+ expect(result.events).toHaveLength(3);
1448
+ expect(result.samples).toHaveLength(1);
1449
+ });
1450
+
1451
+ test("throws the client's step-prefixed error on an authentication failure", async () => {
1452
+ const harness: Harness = buildHarness({
1453
+ token: (): DataSourceHttpResponse => {
1454
+ return status(
1455
+ 401,
1456
+ JSON.stringify({
1457
+ errors: [{ code: 401, message: "access denied" }],
1458
+ }),
1459
+ );
1460
+ },
1461
+ });
1462
+
1463
+ await expect(
1464
+ harness.connector.fetchEvents(
1465
+ settings(),
1466
+ { startTime: START, endTime: END },
1467
+ fetchOptions(),
1468
+ ),
1469
+ ).rejects.toThrow(
1470
+ /^CrowdStrike Falcon token request failed \(HTTP 401\): /,
1471
+ );
1472
+ });
1473
+
1474
+ test("throws the client's step-prefixed error on a 403 from the alerts query", async () => {
1475
+ const harness: Harness = buildHarness({
1476
+ queries: [
1477
+ (): DataSourceHttpResponse => {
1478
+ return status(
1479
+ 403,
1480
+ JSON.stringify({
1481
+ errors: [
1482
+ { code: 403, message: "access denied, authorization failed" },
1483
+ ],
1484
+ }),
1485
+ );
1486
+ },
1487
+ ],
1488
+ });
1489
+
1490
+ let caught: unknown = null;
1491
+
1492
+ try {
1493
+ await harness.connector.fetchEvents(
1494
+ settings(),
1495
+ { startTime: START, endTime: END },
1496
+ fetchOptions(),
1497
+ );
1498
+ } catch (error) {
1499
+ caught = error;
1500
+ }
1501
+
1502
+ expect(caught).toBeInstanceOf(APIException);
1503
+ expect((caught as APIException).message).toMatch(
1504
+ /^CrowdStrike Falcon alerts query failed \(HTTP 403\): /,
1505
+ );
1506
+ });
1507
+
1508
+ test("throws the client's step-prefixed error on a 500 from the entity fetch", async () => {
1509
+ const harness: Harness = buildHarness({
1510
+ queries: [
1511
+ (): DataSourceHttpResponse => {
1512
+ return idsPage([compositeId(1)], 1);
1513
+ },
1514
+ ],
1515
+ entities: [
1516
+ (): DataSourceHttpResponse => {
1517
+ return status(500, "boom");
1518
+ },
1519
+ ],
1520
+ });
1521
+
1522
+ await expect(
1523
+ harness.connector.fetchEvents(
1524
+ settings(),
1525
+ { startTime: START, endTime: END },
1526
+ fetchOptions(),
1527
+ ),
1528
+ ).rejects.toThrow(
1529
+ /^CrowdStrike Falcon alerts fetch failed \(HTTP 500\): /,
1530
+ );
1531
+ });
1532
+
1533
+ test("surfaces a transport timeout with the step named", async () => {
1534
+ const harness: Harness = buildHarness({
1535
+ queries: [
1536
+ (): DataSourceHttpResponse => {
1537
+ throw new BadDataException(
1538
+ "Could not reach data source: timeout of 5000ms exceeded",
1539
+ );
1540
+ },
1541
+ ],
1542
+ });
1543
+
1544
+ await expect(
1545
+ harness.connector.fetchEvents(
1546
+ settings(),
1547
+ { startTime: START, endTime: END },
1548
+ fetchOptions(),
1549
+ ),
1550
+ ).rejects.toThrow(
1551
+ "CrowdStrike Falcon alerts query could not be completed: Could not reach data source: timeout of 5000ms exceeded",
1552
+ );
1553
+ });
1554
+
1555
+ test("rejects malformed settings before contacting Falcon", async () => {
1556
+ const harness: Harness = buildHarness({});
1557
+
1558
+ await expect(
1559
+ harness.connector.fetchEvents(
1560
+ settings({ secrets: {} }),
1561
+ { startTime: START, endTime: END },
1562
+ fetchOptions(),
1563
+ ),
1564
+ ).rejects.toThrow(BadDataException);
1565
+ expect(harness.requests).toHaveLength(0);
1566
+ });
1567
+ });
1568
+ });