@oneuptime/common 13.0.4 → 13.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (673) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/RumApplication.ts +1 -1
  3. package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
  4. package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
  5. package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
  6. package/Server/API/AIInvestigationAPI.ts +484 -24
  7. package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
  8. package/Server/API/SecurityEventConnectionAPI.ts +250 -0
  9. package/Server/API/TelemetryAPI.ts +402 -265
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Middleware/TelemetryIngest.ts +101 -6
  13. package/Server/Services/ExceptionAggregationService.ts +7 -16
  14. package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
  15. package/Server/Services/Index.ts +4 -0
  16. package/Server/Services/InventoryItemService.ts +28 -0
  17. package/Server/Services/LogAggregationService.ts +17 -23
  18. package/Server/Services/MetricAggregationService.ts +4 -14
  19. package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
  20. package/Server/Services/SecurityEventConnectionService.ts +538 -0
  21. package/Server/Services/TraceAggregationService.ts +77 -28
  22. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  23. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
  24. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
  25. package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
  26. package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
  27. package/Server/Utils/AI/Toolbox/Index.ts +38 -0
  28. package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
  30. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
  31. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
  32. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
  33. package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
  34. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
  35. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
  36. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
  37. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
  38. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
  39. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
  40. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
  41. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
  42. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
  43. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
  44. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
  45. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
  46. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
  47. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
  48. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
  49. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
  50. package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
  51. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
  52. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
  53. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
  55. package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
  56. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
  57. package/Server/Utils/StartServer.ts +10 -0
  58. package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
  59. package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
  60. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
  61. package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
  62. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
  63. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
  64. package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
  65. package/Server/Utils/Telemetry.ts +18 -0
  66. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
  67. package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
  68. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
  69. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
  70. package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
  71. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
  72. package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
  73. package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
  74. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
  75. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
  76. package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
  77. package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
  78. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
  79. package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
  80. package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
  81. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
  82. package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
  83. package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
  84. package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
  85. package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
  86. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
  87. package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
  88. package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
  89. package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
  90. package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
  91. package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
  92. package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
  93. package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
  94. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
  95. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
  96. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
  97. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
  98. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
  99. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
  100. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
  101. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
  102. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
  103. package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
  104. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
  105. package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
  106. package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
  107. package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
  108. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
  109. package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
  110. package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
  111. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
  112. package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
  113. package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
  114. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
  115. package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
  116. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
  118. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
  119. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
  120. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
  121. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
  122. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
  123. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
  124. package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
  125. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
  126. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
  127. package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
  128. package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
  129. package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
  130. package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
  131. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
  132. package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
  133. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
  134. package/Tests/Models/InventoryItemNaming.test.ts +132 -16
  135. package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
  136. package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
  137. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
  138. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
  139. package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
  140. package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
  141. package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
  142. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
  143. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
  144. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
  145. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  146. package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
  147. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
  148. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
  149. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
  150. package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
  151. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
  152. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
  153. package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
  154. package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
  155. package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
  156. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
  157. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
  158. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
  159. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  160. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  161. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  178. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  188. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  189. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  190. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  191. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  192. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  193. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +348 -2
  194. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  195. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  196. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  197. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  198. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  199. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  200. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  201. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  202. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  203. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  204. package/Tests/UI/Components/Card.test.tsx +312 -1
  205. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  206. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  207. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  208. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  209. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  210. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  211. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  212. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  213. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  214. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  215. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  216. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  217. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  218. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  219. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  220. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  221. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  222. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  223. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  224. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  225. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  226. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  227. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  228. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  229. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  230. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  231. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  232. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  233. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  234. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  235. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  236. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  237. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  238. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  239. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  240. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  241. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  242. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  243. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  244. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  245. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  246. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  247. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  248. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  249. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  250. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  251. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  252. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  253. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  254. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  255. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  256. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  257. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  258. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  259. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  260. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  261. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  262. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  263. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  264. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  265. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  266. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  267. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  268. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  269. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  270. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  271. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  272. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  273. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  274. package/Types/AI/AIChatTypes.ts +8 -0
  275. package/Types/AI/InvestigationEvidence.ts +96 -0
  276. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  277. package/Types/Permission.ts +2 -2
  278. package/Types/Rum/SessionReplay.ts +109 -0
  279. package/Types/Rum/SessionReplayApi.ts +31 -0
  280. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  281. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  282. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  283. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  284. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  285. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  286. package/Types/Telemetry/EntityType.ts +14 -0
  287. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  288. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  289. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  290. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  291. package/UI/Components/Card/Card.tsx +118 -83
  292. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  293. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  294. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  295. package/UI/Components/Detail/Detail.tsx +137 -22
  296. package/UI/Components/ErrorBoundary.tsx +38 -0
  297. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  298. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  299. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  300. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  301. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  302. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  303. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  304. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  305. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  306. package/UI/Components/LogsViewer/types.ts +6 -0
  307. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  308. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  309. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  310. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  311. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  312. package/UI/Components/ModelList/ModelList.tsx +33 -8
  313. package/UI/Components/Page/ModelPage.tsx +119 -54
  314. package/UI/Components/Table/Table.tsx +8 -0
  315. package/UI/Components/Table/TableRow.tsx +13 -5
  316. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  317. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  318. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  319. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  320. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  321. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  322. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  323. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  324. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  325. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  326. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  327. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  328. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  329. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  330. package/UI/Components/TelemetryViewer/types.ts +19 -0
  331. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  332. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  333. package/UI/Utils/Clipboard.ts +68 -2
  334. package/UI/Utils/Navigation.ts +17 -2
  335. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  336. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  337. package/Utils/AI/InvestigationReport.ts +1789 -0
  338. package/Utils/API.ts +90 -1
  339. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  340. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  341. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  342. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  343. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  344. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  345. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  346. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  347. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  348. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  349. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  350. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  351. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  352. package/Utils/Telemetry/NetworkHost.ts +43 -0
  353. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  354. package/Utils/Traces/CriticalPath.ts +10 -4
  355. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  356. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  357. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  358. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  359. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  360. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  361. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  362. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  363. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  364. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  365. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  366. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  367. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  368. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  369. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  370. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  371. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  372. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  373. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  374. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  375. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  376. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  377. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  378. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  379. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  380. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  381. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  382. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  383. package/build/dist/Server/Services/Index.js +4 -0
  384. package/build/dist/Server/Services/Index.js.map +1 -1
  385. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  386. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  387. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  388. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  389. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  390. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  392. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  393. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  394. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  395. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  396. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  397. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  398. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  399. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  400. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  401. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  402. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  403. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  404. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  405. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  406. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  407. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  408. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  410. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  411. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  412. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  413. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  414. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  415. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  416. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  417. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  418. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  419. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  420. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  421. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  422. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  423. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  424. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  456. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  457. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  458. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  460. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  461. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  462. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  463. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  464. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  465. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  466. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  467. package/build/dist/Server/Utils/StartServer.js +10 -0
  468. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  469. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  470. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  471. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  472. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  473. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  474. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  475. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  476. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  477. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  478. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  480. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  481. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  482. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry.js +11 -5
  484. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  485. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  486. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  487. package/build/dist/Types/Permission.js +2 -2
  488. package/build/dist/Types/Permission.js.map +1 -1
  489. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  490. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  491. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  492. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  493. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  494. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  495. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  496. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  497. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  498. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  499. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  500. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  501. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  502. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  503. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  504. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  505. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  506. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  507. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  508. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  509. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  510. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  511. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  512. package/build/dist/UI/Components/Card/Card.js +34 -17
  513. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  514. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  515. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  516. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  517. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  518. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  519. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  520. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  521. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  522. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  523. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  524. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  525. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  526. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  527. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  528. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  529. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  530. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  531. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  532. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  533. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  534. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  535. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  536. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  537. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  539. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  540. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  541. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  543. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  544. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  545. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  546. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  547. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  548. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  549. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  550. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  551. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  552. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  553. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  554. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  555. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  556. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  557. package/build/dist/UI/Components/Table/Table.js +3 -1
  558. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  559. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  560. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  561. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  562. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  563. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  564. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  565. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  566. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  567. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  568. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  569. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  570. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  571. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  572. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  573. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  574. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  575. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  576. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  578. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  580. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  582. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  583. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  584. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  585. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  586. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  587. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  588. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  589. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  590. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  591. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  592. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  594. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  595. package/build/dist/UI/Utils/Clipboard.js +59 -2
  596. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  597. package/build/dist/UI/Utils/Navigation.js +11 -2
  598. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  599. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  600. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  601. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  602. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  603. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  604. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  605. package/build/dist/Utils/API.js +25 -0
  606. package/build/dist/Utils/API.js.map +1 -1
  607. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  608. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  609. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  610. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  611. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  612. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  613. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  614. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  615. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  616. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  617. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  618. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  619. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  620. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  621. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  622. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  623. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  624. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  626. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  627. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  628. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  629. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  630. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  631. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  632. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  633. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  634. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  635. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  636. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  637. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  638. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  639. package/package.json +1 -1
  640. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  641. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  642. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  643. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  644. package/UI/Components/GanttChart/Index.tsx +0 -90
  645. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  646. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  647. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  648. package/UI/Components/GanttChart/Rows.tsx +0 -59
  649. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  650. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  651. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  652. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  653. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  654. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  655. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  656. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  657. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  658. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  659. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  660. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  661. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  662. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  663. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  664. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  665. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  666. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  667. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  669. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  671. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  673. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -1,15 +1,8 @@
1
- import { ResponseJSON, ResultSet } from "@clickhouse/client";
2
- import TableColumnType from "../../../../Types/AnalyticsDatabase/TableColumnType";
3
- import { SQL, Statement } from "../../AnalyticsDatabase/Statement";
4
- import {
5
- getClickhouseClusterName,
6
- getClickhouseDatabaseName,
7
- getStorageTableName,
8
- } from "../../AnalyticsDatabase/ClusterConfig";
9
- import { getQuerySettings } from "../../AnalyticsDatabase/QuerySettingsHelper";
10
1
  import BadDataException from "../../../../Types/Exception/BadDataException";
11
2
  import {
3
+ GoogleSecOpsCreationLag,
12
4
  GoogleSecOpsDetectionSample,
5
+ GoogleSecOpsDiagnosticCheck,
13
6
  GoogleSecOpsRunOptions,
14
7
  GoogleSecOpsRunResult,
15
8
  } from "../../../../Types/SecurityEvent/GoogleSecOpsDiagnostics";
@@ -19,7 +12,10 @@ import {
19
12
  readString,
20
13
  readValue,
21
14
  } from "../../../../Utils/SecurityEvent/NormalizerHelpers";
22
- import Semaphore, { SemaphoreMutex } from "../../../Infrastructure/Semaphore";
15
+ import Semaphore, {
16
+ SemaphoreLockTimeoutError,
17
+ SemaphoreMutex,
18
+ } from "../../../Infrastructure/Semaphore";
23
19
  import GoogleSecOpsConnection from "../../../../Models/DatabaseModels/GoogleSecOpsConnection";
24
20
  import LIMIT_MAX from "../../../../Types/Database/LimitMax";
25
21
  import OneUptimeDate from "../../../../Types/Date";
@@ -37,20 +33,66 @@ import logger from "../../Logger";
37
33
  import { redactLogString } from "../../LogRedaction";
38
34
  import CaptureSpan from "../../Telemetry/CaptureSpan";
39
35
  import ConnectorErrorMessage from "../ConnectorErrorMessage";
36
+ import SecurityEventDedupe from "../SecurityEventDedupe";
40
37
  import { buildSecurityEventDbRow } from "../SecurityEventRow";
41
38
  import ThreatIntelEnricher from "../ThreatIntel/ThreatIntelEnricher";
42
- import GoogleSecOpsClient, { FetchAlertsResult } from "./GoogleSecOpsClient";
39
+ import GoogleSecOpsClient, {
40
+ FetchAlertsResult,
41
+ GoogleSecOpsListBasis,
42
+ SearchDetectionsResult,
43
+ } from "./GoogleSecOpsClient";
43
44
 
44
45
  const SECOPS_SERVICE_NAME: string = "Google SecOps";
46
+ const SECOPS_VENDOR_NAME: string = "Google";
45
47
 
46
- const DEFAULT_LOOKBACK_IN_MINUTES: number = 15;
47
- const MAX_LOOKBACK_IN_MINUTES: number = 24 * 60;
48
+ /*
49
+ * The first poll of a new connection reads a full day of CREATED detections.
50
+ * Fifteen minutes used to be the default, and on a tenant whose rules run
51
+ * hourly or daily it reliably returned nothing, which read as "the
52
+ * connector is broken" on the very first look.
53
+ */
54
+ const DEFAULT_LOOKBACK_IN_MINUTES: number = 24 * 60;
55
+ /*
56
+ * Adaptive catch-up. A scheduled poll reads at most MAX_CHUNK_MINUTES of
57
+ * created time past its cursor. A poll that cannot read its window in one
58
+ * run halves the next chunk and a complete poll doubles it back, because
59
+ * re-reading an identical window that never fits pinned the cursor forever
60
+ * and nothing new was ever imported. The chunk is measured from the cursor,
61
+ * not from the overlapped window start: a chunk no longer than the overlap
62
+ * would otherwise end at or before the cursor and never move it.
63
+ */
64
+ const MAX_CHUNK_MINUTES: number = 24 * 60;
65
+ const MIN_CHUNK_MINUTES: number = 1;
48
66
  const WINDOW_OVERLAP_IN_MINUTES: number = 1;
49
- const MAX_FETCH_REQUESTS: number = 12;
50
- const MAX_FETCH_DURATION_MS: number = 2 * 60 * 1000;
67
+ /*
68
+ * Separate request budgets per kind of read. The two created-time search
69
+ * passes are the authoritative read of rule detections and share one page
70
+ * budget; the alerts view splits truncated windows and gets its own, so a
71
+ * burst that the alerts view has to split can no longer starve the searches
72
+ * (or the other way round).
73
+ */
74
+ const MAX_SEARCH_PAGES_PER_POLL: number = 20;
75
+ const MAX_ALERTS_VIEW_REQUESTS_PER_POLL: number = 16;
76
+ // The worker job times out at ten minutes; normalizing and importing follow.
77
+ const MAX_FETCH_DURATION_MS: number = 4 * 60 * 1000;
51
78
  const MAX_ALERTS_PER_REQUEST: number = 1000;
79
+ const MAX_SEARCH_PAGE_SIZE: number = 1000;
52
80
  const MAX_DIAGNOSTIC_SAMPLES: number = 25;
53
81
  const MAX_RANGE_MS: number = 7 * 24 * 60 * 60 * 1000;
82
+ const DEFAULT_POLL_INTERVAL_IN_MINUTES: number = 5;
83
+ /*
84
+ * A detection created within one poll interval plus this grace of its
85
+ * detection time would still have been caught by a detection-time cursor;
86
+ * anything later is exactly the record the old basis lost.
87
+ */
88
+ const CREATION_LAG_GRACE_IN_MINUTES: number = 1;
89
+ /*
90
+ * Curated (Google-authored) rule detections are optional: tenants without
91
+ * that entitlement answer 403 (and some 400/404) on the curated route. Those
92
+ * statuses degrade the pass to a warning; 401, 429, 5xx, timeouts and parse
93
+ * failures still fail the run like any other pass.
94
+ */
95
+ const CURATED_OPTIONAL_STATUSES: Array<number> = [400, 403, 404];
54
96
  export const GOOGLE_SECOPS_SOURCE_LOCK_NAMESPACE: string = "GoogleSecOpsSource";
55
97
 
56
98
  interface PollWindow {
@@ -58,6 +100,43 @@ interface PollWindow {
58
100
  endTime: Date;
59
101
  warnings: Array<string>;
60
102
  hasUsableCursor: boolean;
103
+ /*
104
+ * Scheduled polls only: the saved cursor this window reads past, or the
105
+ * window start when there is no usable cursor. chunkMinutes is the
106
+ * distance from there to endTime, rounded up; requestedChunkMinutes is
107
+ * the chunk the previous poll asked for, which is longer whenever this
108
+ * window was cut short by the present.
109
+ */
110
+ chunkBase?: Date | undefined;
111
+ chunkMinutes?: number | undefined;
112
+ requestedChunkMinutes?: number | undefined;
113
+ }
114
+
115
+ // Why a read pass stopped before it had read everything it was asked for.
116
+ type PassStop = "requests" | "time" | null;
117
+
118
+ interface PassOutcome {
119
+ // Records Google returned across every request of the pass.
120
+ returned: number;
121
+ requests: number;
122
+ stoppedBy: PassStop;
123
+ // Google itself left part of the window unread (truncation, unfinished stream).
124
+ leftUnread: boolean;
125
+ }
126
+
127
+ // The per-poll budgets: request counts per kind of read and the wall clock.
128
+ interface PollBudget {
129
+ startedMs: number;
130
+ searchRequests: number;
131
+ alertsViewRequests: number;
132
+ }
133
+
134
+ // How a scheduled poll moves its cursor, decided once the passes are done.
135
+ interface CursorDecision {
136
+ cursor?: string | undefined;
137
+ nextChunkMinutes: number;
138
+ forcedAdvance: boolean;
139
+ warning?: string | undefined;
61
140
  }
62
141
 
63
142
  class RecordedPollFailure extends Error {}
@@ -80,6 +159,8 @@ export default class GoogleSecOpsPoller {
80
159
  pollIntervalInMinutes: true,
81
160
  lastPolledAt: true,
82
161
  cursor: true,
162
+ // Carries nextChunkMinutes, the length of the next catch-up chunk.
163
+ lastPollResult: true,
83
164
  includeNonAlertingDetections: true,
84
165
  },
85
166
  skip: 0,
@@ -197,13 +278,30 @@ export default class GoogleSecOpsPoller {
197
278
  return this.executeUnlocked(connection, options, clientOverride);
198
279
  }
199
280
 
200
- const mutex: SemaphoreMutex = await Semaphore.lock({
201
- namespace: GOOGLE_SECOPS_SOURCE_LOCK_NAMESPACE,
202
- key: connection.projectId!.toString(),
203
- lockTimeout: 30000,
204
- acquireTimeout: 10000,
205
- retryInterval: 100,
206
- });
281
+ let mutex: SemaphoreMutex;
282
+ try {
283
+ mutex = await Semaphore.lock({
284
+ namespace: GOOGLE_SECOPS_SOURCE_LOCK_NAMESPACE,
285
+ key: connection.projectId!.toString(),
286
+ lockTimeout: 30000,
287
+ acquireTimeout: 10000,
288
+ retryInterval: 100,
289
+ });
290
+ } catch (error) {
291
+ /*
292
+ * The library's timeout message names Redis keys and reads like an
293
+ * infrastructure fault. It is the expected steady state when a long
294
+ * import holds the source lock, so it is reworded for the person
295
+ * reading run history, and left as a BadDataException so the run
296
+ * executor records it without retrying.
297
+ */
298
+ if (error instanceof SemaphoreLockTimeoutError) {
299
+ throw new BadDataException(
300
+ "Another poll or import for this source is still running in this project. This run was skipped; polling continues on the next scheduled tick.",
301
+ );
302
+ }
303
+ throw error;
304
+ }
207
305
  try {
208
306
  /*
209
307
  * An injected client also injects the connection snapshot for tests.
@@ -221,6 +319,12 @@ export default class GoogleSecOpsPoller {
221
319
  instanceResourceName: true,
222
320
  serviceAccountJson: true,
223
321
  cursor: true,
322
+ /*
323
+ * Reloaded with the cursor: the previous poll's result decides
324
+ * this poll's chunk and whether it resumes past a forced advance.
325
+ */
326
+ lastPollResult: true,
327
+ pollIntervalInMinutes: true,
224
328
  includeNonAlertingDetections: true,
225
329
  },
226
330
  props: { isRoot: true },
@@ -286,35 +390,124 @@ export default class GoogleSecOpsPoller {
286
390
  "Select a valid past time range of at most seven days.",
287
391
  );
288
392
  }
289
- } else if (options.type === "poll" && connection.cursor) {
290
- const cursorTime: Date = new Date(connection.cursor);
291
- if (!Number.isFinite(cursorTime.getTime())) {
393
+ return { startTime, endTime, warnings, hasUsableCursor };
394
+ }
395
+
396
+ if (options.type !== "poll") {
397
+ return { startTime, endTime, warnings, hasUsableCursor };
398
+ }
399
+
400
+ const chunk: number = this.getChunkMinutes(connection);
401
+ let cursorTime: Date | null = null;
402
+ if (connection.cursor) {
403
+ const parsed: Date = new Date(connection.cursor);
404
+ if (!Number.isFinite(parsed.getTime())) {
292
405
  warnings.push(
293
- "The saved cursor is unreadable; polling the default 15 minute window.",
406
+ "The saved cursor is unreadable; polling the default 24 hour window.",
294
407
  );
295
- } else if (cursorTime >= now) {
408
+ } else if (parsed >= now) {
296
409
  warnings.push(
297
- "The saved cursor is in the future; polling the default 15 minute window.",
410
+ "The saved cursor is in the future; polling the default 24 hour window.",
298
411
  );
299
412
  } else {
300
- hasUsableCursor = true;
301
- startTime = OneUptimeDate.addRemoveMinutes(
302
- cursorTime,
303
- -WINDOW_OVERLAP_IN_MINUTES,
413
+ cursorTime = parsed;
414
+ }
415
+ }
416
+
417
+ if (cursorTime) {
418
+ hasUsableCursor = true;
419
+ startTime = this.resumesFromForcedAdvance(connection, cursorTime)
420
+ ? cursorTime
421
+ : OneUptimeDate.addRemoveMinutes(
422
+ cursorTime,
423
+ -WINDOW_OVERLAP_IN_MINUTES,
424
+ );
425
+ const chunkEnd: Date = OneUptimeDate.addRemoveMinutes(cursorTime, chunk);
426
+ if (chunkEnd < endTime) {
427
+ endTime = chunkEnd;
428
+ warnings.push(
429
+ `Catching up from the saved cursor in ${this.describeChunk(chunk)} windows. Later detections will be fetched by subsequent polls.`,
304
430
  );
305
- const chunkEnd: Date = OneUptimeDate.addRemoveMinutes(
306
- startTime,
307
- MAX_LOOKBACK_IN_MINUTES,
431
+ }
432
+ } else {
433
+ // The first window keeps its 24 hour lookback; a narrowed chunk only shortens its end.
434
+ const chunkEnd: Date = OneUptimeDate.addRemoveMinutes(startTime, chunk);
435
+ if (chunkEnd < endTime) {
436
+ endTime = chunkEnd;
437
+ warnings.push(
438
+ `Reading the first ${chunk} minutes of the 24 hour lookback. Later detections will be fetched by subsequent polls.`,
308
439
  );
309
- if (chunkEnd < endTime) {
310
- endTime = chunkEnd;
311
- warnings.push(
312
- "Catching up from the saved cursor in 24 hour windows. Later detections will be fetched by subsequent polls.",
313
- );
314
- }
315
440
  }
316
441
  }
317
- return { startTime, endTime, warnings, hasUsableCursor };
442
+
443
+ const chunkBase: Date = cursorTime || startTime;
444
+ return {
445
+ startTime,
446
+ endTime,
447
+ warnings,
448
+ hasUsableCursor,
449
+ chunkBase,
450
+ requestedChunkMinutes: chunk,
451
+ chunkMinutes: Math.max(
452
+ MIN_CHUNK_MINUTES,
453
+ Math.ceil((endTime.getTime() - chunkBase.getTime()) / 60000),
454
+ ),
455
+ };
456
+ }
457
+
458
+ /*
459
+ * lastPollResult.nextChunkMinutes when it is a whole number of minutes
460
+ * inside the allowed range. Anything else (no previous poll, a hand-edited
461
+ * row, a result from before adaptive catch-up) reads the full 24 hours.
462
+ */
463
+ private static getChunkMinutes(connection: GoogleSecOpsConnection): number {
464
+ const stored: unknown = connection.lastPollResult
465
+ ? connection.lastPollResult["nextChunkMinutes"]
466
+ : undefined;
467
+ if (
468
+ typeof stored === "number" &&
469
+ Number.isInteger(stored) &&
470
+ stored >= MIN_CHUNK_MINUTES &&
471
+ stored <= MAX_CHUNK_MINUTES
472
+ ) {
473
+ return stored;
474
+ }
475
+ return MAX_CHUNK_MINUTES;
476
+ }
477
+
478
+ /*
479
+ * True when the saved cursor is the end of a minute that a forced advance
480
+ * skipped (or a poll that already started there and did not move the
481
+ * cursor). Such a poll starts at the cursor without the overlap: the
482
+ * overlap minute is exactly the minute that could not be read, and
483
+ * re-reading it would overflow again and force a second advance over a
484
+ * minute that was never the problem.
485
+ */
486
+ private static resumesFromForcedAdvance(
487
+ connection: GoogleSecOpsConnection,
488
+ cursorTime: Date,
489
+ ): boolean {
490
+ const stored: JSONObject | undefined = connection.lastPollResult;
491
+ if (!stored || stored["type"] !== "poll") {
492
+ return false;
493
+ }
494
+ const readTime: (key: string) => number = (key: string): number => {
495
+ const value: unknown = stored[key];
496
+ return typeof value === "string" ? Date.parse(value) : Number.NaN;
497
+ };
498
+ return (
499
+ (stored["forcedAdvance"] === true &&
500
+ readTime("windowEnd") === cursorTime.getTime()) ||
501
+ readTime("windowStart") === cursorTime.getTime()
502
+ );
503
+ }
504
+
505
+ private static describeChunk(minutes: number): string {
506
+ if (minutes % 60 === 0) {
507
+ const hours: number = minutes / 60;
508
+ return `${hours} hour`;
509
+ }
510
+ return `${minutes} minute`;
318
511
  }
319
512
 
320
513
  private static async executeUnlocked(
@@ -324,6 +517,21 @@ export default class GoogleSecOpsPoller {
324
517
  ): Promise<GoogleSecOpsRunResult> {
325
518
  const window: PollWindow = this.getWindow(connection, options);
326
519
  const startedMs: number = Date.now();
520
+ /*
521
+ * Preview and backfill take a detection-time range from the person (it
522
+ * is how the SecOps alerts view frames time), and read created time as
523
+ * well so an import of "yesterday" also carries detections created
524
+ * yesterday about older events. Polls read created time only; the
525
+ * alerts-view pass supplies detection time.
526
+ */
527
+ const isHistorical: boolean =
528
+ options.type === "preview" || options.type === "backfill";
529
+ const searchBases: Array<GoogleSecOpsListBasis> = isHistorical
530
+ ? ["CREATED_TIME", "DETECTION_TIME"]
531
+ : ["CREATED_TIME"];
532
+ const basisLabel: string = isHistorical
533
+ ? "created and detection time"
534
+ : "created time";
327
535
  const result: GoogleSecOpsRunResult = {
328
536
  type: options.type,
329
537
  ...(options.runId ? { runId: options.runId } : {}),
@@ -335,6 +543,8 @@ export default class GoogleSecOpsPoller {
335
543
  windowEnd: window.endTime.toISOString(),
336
544
  includeNonAlertingDetections:
337
545
  connection.includeNonAlertingDetections === true,
546
+ basis: isHistorical ? "detection-time" : "created-time",
547
+ sourceCounts: { ruleDetections: 0, curatedDetections: 0, alertsView: 0 },
338
548
  fetchedCount: 0,
339
549
  ingestedCount: 0,
340
550
  duplicateCount: 0,
@@ -345,6 +555,14 @@ export default class GoogleSecOpsPoller {
345
555
  warnings: window.warnings,
346
556
  samples: [],
347
557
  checks: [],
558
+ ...(window.chunkMinutes !== undefined
559
+ ? { chunkMinutes: window.chunkMinutes }
560
+ : {}),
561
+ };
562
+ const budget: PollBudget = {
563
+ startedMs,
564
+ searchRequests: 0,
565
+ alertsViewRequests: 0,
348
566
  };
349
567
  let phase: string = "Validate configuration";
350
568
  let phaseStartedMs: number = Date.now();
@@ -374,23 +592,101 @@ export default class GoogleSecOpsPoller {
374
592
  message: "Google accepted the service account credentials.",
375
593
  });
376
594
  }
377
- phase = "Read detections from the configured instance";
595
+
596
+ /*
597
+ * Three passes over one window, unioned by Collection.id. The alerts
598
+ * view alone filters on DETECTION time, and a rule that runs hourly
599
+ * creates detections whose detection time is already behind a
600
+ * forward-only cursor, so it can never be the primary read.
601
+ */
602
+ const seen: Map<string, JSONObject> = new Map();
603
+
604
+ phase = `Read rule detections by ${basisLabel}`;
378
605
  phaseStartedMs = Date.now();
379
- const alerts: Array<JSONObject> = await this.fetchWindows(
606
+ const rulePass: PassOutcome = await this.searchPass(
380
607
  client,
381
608
  window,
382
609
  result,
383
- startedMs,
610
+ budget,
611
+ seen,
612
+ { curated: false, bases: searchBases },
384
613
  );
385
- result.fetchedCount = alerts.length;
386
- result.checks.push({
614
+ result.sourceCounts!.ruleDetections = rulePass.returned;
615
+ this.recordPass({
616
+ result,
387
617
  name: phase,
388
- status: "success",
389
- durationMs: Date.now() - phaseStartedMs,
390
- message:
391
- "The configured Google SecOps instance is reachable and allows reading detections.",
618
+ startedMs: phaseStartedMs,
619
+ outcome: rulePass,
620
+ countMessage: `${rulePass.returned} rule detections returned for the window by ${basisLabel}.`,
621
+ });
622
+
623
+ phase = `Read curated rule detections by ${basisLabel}`;
624
+ phaseStartedMs = Date.now();
625
+ try {
626
+ const curatedPass: PassOutcome = await this.searchPass(
627
+ client,
628
+ window,
629
+ result,
630
+ budget,
631
+ seen,
632
+ { curated: true, bases: searchBases },
633
+ );
634
+ result.sourceCounts!.curatedDetections = curatedPass.returned;
635
+ this.recordPass({
636
+ result,
637
+ name: phase,
638
+ startedMs: phaseStartedMs,
639
+ outcome: curatedPass,
640
+ countMessage: `${curatedPass.returned} curated rule detections returned for the window by ${basisLabel}.`,
641
+ });
642
+ } catch (curatedError) {
643
+ /*
644
+ * Named differently from the outer catch on purpose: the guidance
645
+ * accuracy test reads executeUnlocked's first catch clause, by its
646
+ * variable name, as the run-failure path.
647
+ */
648
+ const status: number | null =
649
+ GoogleSecOpsClient.readHttpStatus(curatedError);
650
+ if (status === null || !CURATED_OPTIONAL_STATUSES.includes(status)) {
651
+ throw curatedError;
652
+ }
653
+ const message: string = redactLogString(
654
+ ConnectorErrorMessage.toMessage(curatedError),
655
+ );
656
+ result.warnings.push(
657
+ `Curated rule detections could not be read (HTTP ${status}); this tenant may not have curated rule access. Rule detections and the alerts view were still read.`,
658
+ );
659
+ result.checks.push({
660
+ name: phase,
661
+ status: "warn",
662
+ durationMs: Date.now() - phaseStartedMs,
663
+ message,
664
+ });
665
+ }
666
+
667
+ phase = "Read alerts view by detection time";
668
+ phaseStartedMs = Date.now();
669
+ const alertsPass: PassOutcome = await this.fetchWindows(
670
+ client,
671
+ window,
672
+ result,
673
+ budget,
674
+ seen,
675
+ );
676
+ result.sourceCounts!.alertsView = alertsPass.returned;
677
+ this.recordPass({
678
+ result,
679
+ name: phase,
680
+ startedMs: phaseStartedMs,
681
+ outcome: alertsPass,
682
+ countMessage: `${alertsPass.returned} alerts returned by detection time.`,
683
+ successMessage: `${alertsPass.returned} alerts returned by detection time. The configured Google SecOps instance is reachable and allows reading detections.`,
392
684
  });
393
685
 
686
+ const alerts: Array<JSONObject> = Array.from(seen.values());
687
+ result.fetchedCount = alerts.length;
688
+ result.creationLag = this.measureCreationLag(alerts, connection, result);
689
+
394
690
  phase = "Normalize detections";
395
691
  phaseStartedMs = Date.now();
396
692
  const normalized: Array<NormalizedSecurityEvent> = [];
@@ -411,23 +707,29 @@ export default class GoogleSecOpsPoller {
411
707
  }
412
708
  }
413
709
  if (result.rejectedCount) {
710
+ /*
711
+ * Counted and reported, never retried: an object Google returns
712
+ * that is not a Collection is permanently unrecognizable, so
713
+ * holding the cursor for it would pin the window until the 24 hour
714
+ * chunk could never reach the present.
715
+ */
414
716
  result.warnings.push(
415
- `${result.rejectedCount} returned objects were discarded because they do not look like Google SecOps detections.`,
717
+ `${result.rejectedCount} returned objects were discarded because they do not look like Google SecOps detections. They are counted here and do not hold the poll cursor.`,
416
718
  );
417
719
  }
418
720
  if (result.failedCount) {
419
721
  result.warnings.push(
420
- `${result.failedCount} detections could not be normalized. The poll cursor is held for retry.`,
722
+ `${result.failedCount} detections could not be normalized, so this window is not fully imported.`,
421
723
  );
422
724
  }
423
- result.complete =
424
- result.complete &&
425
- result.failedCount === 0 &&
426
- result.rejectedCount === 0;
725
+ result.complete = result.complete && result.failedCount === 0;
427
726
  result.checks.push({
428
727
  name: phase,
429
- status:
430
- result.failedCount || result.rejectedCount ? "failed" : "success",
728
+ status: result.failedCount
729
+ ? "failed"
730
+ : result.rejectedCount
731
+ ? "warn"
732
+ : "success",
431
733
  durationMs: Date.now() - phaseStartedMs,
432
734
  message: `${normalized.length} detections recognized; ${result.rejectedCount} rejected; ${result.failedCount} failed.`,
433
735
  });
@@ -461,6 +763,25 @@ export default class GoogleSecOpsPoller {
461
763
  message: result.error,
462
764
  });
463
765
  }
766
+ let decision: CursorDecision | null = null;
767
+ if (options.type === "poll") {
768
+ decision = this.decideCursor(window, result);
769
+ result.nextChunkMinutes = decision.nextChunkMinutes;
770
+ if (decision.forcedAdvance) {
771
+ result.forcedAdvance = true;
772
+ }
773
+ if (decision.warning) {
774
+ /*
775
+ * A forced advance leaves a reported gap, so it leads the warnings
776
+ * (and therefore lastError); narrowing follows the reasons for it.
777
+ */
778
+ if (decision.forcedAdvance) {
779
+ result.warnings.unshift(decision.warning);
780
+ } else {
781
+ result.warnings.push(decision.warning);
782
+ }
783
+ }
784
+ }
464
785
  result.completedAt = OneUptimeDate.getCurrentDate().toISOString();
465
786
  result.durationMs = Math.max(0, Date.now() - startedMs);
466
787
  for (const warning of result.warnings) {
@@ -472,19 +793,7 @@ export default class GoogleSecOpsPoller {
472
793
  `GoogleSecOpsPoller: connection ${connection.id!.toString()} fetched ${result.fetchedCount} alerts and ingested ${result.ingestedCount} security events for ${result.windowStart} to ${result.windowEnd}.`,
473
794
  );
474
795
 
475
- if (options.type === "poll") {
476
- /*
477
- * A failed first poll must keep its original start. Otherwise the next
478
- * default lookback would slide past records before we ever imported them.
479
- */
480
- const cursor: string | undefined = result.complete
481
- ? result.windowEnd
482
- : !window.hasUsableCursor
483
- ? OneUptimeDate.addRemoveMinutes(
484
- window.startTime,
485
- WINDOW_OVERLAP_IN_MINUTES,
486
- ).toISOString()
487
- : undefined;
796
+ if (options.type === "poll" && decision) {
488
797
  await GoogleSecOpsConnectionService.updateOneById({
489
798
  id: connection.id!,
490
799
  data: {
@@ -494,8 +803,8 @@ export default class GoogleSecOpsPoller {
494
803
  (result.complete
495
804
  ? null
496
805
  : result.warnings.join(" ") ||
497
- "Poll incomplete; retrying the same window.")) as unknown as string,
498
- ...(cursor ? { cursor } : {}),
806
+ "Poll incomplete.")) as unknown as string,
807
+ ...(decision.cursor ? { cursor: decision.cursor } : {}),
499
808
  ...(result.complete
500
809
  ? { lastSuccessfulPollAt: new Date(result.completedAt) }
501
810
  : {}),
@@ -515,44 +824,180 @@ export default class GoogleSecOpsPoller {
515
824
  return result;
516
825
  }
517
826
 
827
+ /*
828
+ * Where a scheduled poll leaves its cursor and how long the next chunk is.
829
+ * Google's search endpoints return newest first, so a poll that stops
830
+ * early has no "everything before here was read" point to resume from;
831
+ * the only ways forward are a shorter window and, at one minute, moving
832
+ * past the minute and reporting it.
833
+ */
834
+ private static decideCursor(
835
+ window: PollWindow,
836
+ result: GoogleSecOpsRunResult,
837
+ ): CursorDecision {
838
+ const chunkMinutes: number = window.chunkMinutes || MAX_CHUNK_MINUTES;
839
+ const requestedChunkMinutes: number =
840
+ window.requestedChunkMinutes || MAX_CHUNK_MINUTES;
841
+ /*
842
+ * A first poll that does not finish keeps its original start. Otherwise
843
+ * the next default lookback would slide past records before we ever
844
+ * imported them.
845
+ */
846
+ const heldCursor: string | undefined = window.hasUsableCursor
847
+ ? undefined
848
+ : OneUptimeDate.addRemoveMinutes(
849
+ window.startTime,
850
+ WINDOW_OVERLAP_IN_MINUTES,
851
+ ).toISOString();
852
+
853
+ /*
854
+ * Failed and finished polls work from the requested chunk, not from the
855
+ * window read: a caught-up window is only as long as the gap to now, and
856
+ * keeping that as the next chunk would make polls after an outage or a
857
+ * failure fall behind the present for no reason.
858
+ */
859
+ if (result.status === "failed") {
860
+ // A failure says nothing about volume, so the chunk is kept as it was.
861
+ return {
862
+ cursor: heldCursor,
863
+ nextChunkMinutes: requestedChunkMinutes,
864
+ forcedAdvance: false,
865
+ };
866
+ }
867
+
868
+ if (result.complete) {
869
+ // A finished poll never shrinks the chunk; it doubles back after narrowing.
870
+ return {
871
+ cursor: result.windowEnd,
872
+ nextChunkMinutes: Math.min(
873
+ MAX_CHUNK_MINUTES,
874
+ Math.max(requestedChunkMinutes, chunkMinutes * 2),
875
+ ),
876
+ forcedAdvance: false,
877
+ };
878
+ }
879
+
880
+ if (chunkMinutes > MIN_CHUNK_MINUTES) {
881
+ const nextChunkMinutes: number = Math.max(
882
+ MIN_CHUNK_MINUTES,
883
+ Math.floor(chunkMinutes / 2),
884
+ );
885
+ return {
886
+ cursor: heldCursor,
887
+ nextChunkMinutes,
888
+ forcedAdvance: false,
889
+ warning: `This window holds more records than one poll can read; the next poll reads a ${nextChunkMinutes} minute window from the same starting point.`,
890
+ };
891
+ }
892
+
893
+ /*
894
+ * One minute that still cannot be read. Holding here would stall polling
895
+ * for good, so the cursor moves past the minute and the gap is reported
896
+ * loudly with the way to recover it.
897
+ */
898
+ const from: Date = window.chunkBase || window.startTime;
899
+ return {
900
+ cursor: result.windowEnd,
901
+ nextChunkMinutes: MIN_CHUNK_MINUTES,
902
+ forcedAdvance: true,
903
+ warning: `More records were created in the one minute from ${from.toISOString()} to ${result.windowEnd} than one poll can read. Polling moved past this minute so newer records keep arriving; use Import this time range in Diagnostics on this minute to recover what one run can read.`,
904
+ };
905
+ }
906
+
907
+ /*
908
+ * One diagnostic check per read pass. A pass a budget stopped, or never
909
+ * let start, is a warning naming the budget, never a success: a green
910
+ * step that did not read its window hid which data went unread.
911
+ */
912
+ private static recordPass(data: {
913
+ result: GoogleSecOpsRunResult;
914
+ name: string;
915
+ startedMs: number;
916
+ outcome: PassOutcome;
917
+ // What the pass returned; the whole message when it read everything.
918
+ countMessage: string;
919
+ // Replaces countMessage when the pass read everything, if given.
920
+ successMessage?: string | undefined;
921
+ }): void {
922
+ const outcome: PassOutcome = data.outcome;
923
+ let status: GoogleSecOpsDiagnosticCheck["status"] = "success";
924
+ let message: string = data.successMessage || data.countMessage;
925
+ if (outcome.stoppedBy === "time" && outcome.requests === 0) {
926
+ status = "warn";
927
+ message = "Not run: the poll time budget was spent.";
928
+ this.addWarning(
929
+ data.result,
930
+ `${data.name} was not run: the poll time budget was spent.`,
931
+ );
932
+ } else if (outcome.stoppedBy) {
933
+ status = "warn";
934
+ const budgetName: string =
935
+ outcome.stoppedBy === "time" ? "poll time budget" : "request budget";
936
+ const stop: string = `stopped by the ${budgetName} after ${outcome.requests} ${outcome.requests === 1 ? "request" : "requests"}`;
937
+ message =
938
+ outcome.requests === 0
939
+ ? `Not run: ${stop}. The search passes share one page budget and it was spent before this pass started.`
940
+ : `${data.countMessage} The pass was ${stop}.`;
941
+ this.addWarning(data.result, `${data.name} was ${stop}.`);
942
+ } else if (outcome.leftUnread) {
943
+ status = "warn";
944
+ message = `${data.countMessage} Google did not return part of the window.`;
945
+ }
946
+ data.result.checks.push({
947
+ name: data.name,
948
+ status,
949
+ durationMs: Date.now() - data.startedMs,
950
+ message,
951
+ });
952
+ }
953
+
954
+ /*
955
+ * The alerts-view pass (legacyFetchAlertsView, detection time). The
956
+ * endpoint has no pagination, so a truncated window is split in half and
957
+ * both halves are re-read until they fit or the alerts-view budget runs
958
+ * out.
959
+ */
518
960
  private static async fetchWindows(
519
961
  client: GoogleSecOpsClient,
520
962
  window: PollWindow,
521
963
  result: GoogleSecOpsRunResult,
522
- startedMs: number,
523
- ): Promise<Array<JSONObject>> {
524
- const pending: Array<{ startTime: Date; endTime: Date }> = [window];
525
- const seen: Map<string, JSONObject> = new Map();
964
+ budget: PollBudget,
965
+ seen: Map<string, JSONObject>,
966
+ ): Promise<PassOutcome> {
967
+ const outcome: PassOutcome = {
968
+ returned: 0,
969
+ requests: 0,
970
+ stoppedBy: null,
971
+ leftUnread: false,
972
+ };
973
+ const pending: Array<{ startTime: Date; endTime: Date }> = [
974
+ { startTime: window.startTime, endTime: window.endTime },
975
+ ];
976
+ let splits: number = 0;
526
977
  while (pending.length > 0) {
527
- if (
528
- result.requestCount >= MAX_FETCH_REQUESTS ||
529
- Date.now() - startedMs >= MAX_FETCH_DURATION_MS
530
- ) {
978
+ const stop: PassStop = this.budgetStop(
979
+ budget,
980
+ budget.alertsViewRequests,
981
+ MAX_ALERTS_VIEW_REQUESTS_PER_POLL,
982
+ );
983
+ if (stop) {
984
+ outcome.stoppedBy = stop;
531
985
  result.complete = false;
532
- result.warnings.push(
533
- "The recovery request limit was reached. Narrow the time range; the poll cursor has not advanced.",
534
- );
535
986
  break;
536
987
  }
537
988
  const current: { startTime: Date; endTime: Date } = pending.shift()!;
989
+ budget.alertsViewRequests++;
990
+ outcome.requests++;
538
991
  result.requestCount++;
539
992
  const fetched: FetchAlertsResult = await client.fetchDetectionAlerts({
540
- ...current,
993
+ startTime: current.startTime,
994
+ endTime: current.endTime,
541
995
  maxAlerts: result.type === "test" ? 1 : MAX_ALERTS_PER_REQUEST,
542
996
  includeNonAlertingDetections: result.includeNonAlertingDetections,
543
997
  });
998
+ outcome.returned += fetched.alerts.length;
544
999
  for (const alert of fetched.alerts) {
545
- let key: string;
546
- try {
547
- key = readString(alert, "id") || contentHashEventUid(alert);
548
- } catch {
549
- /*
550
- * Keep an uninspectable object in the normalization failure count
551
- * without preventing valid detections in this response from importing.
552
- */
553
- key = `malformed:${result.requestCount}:${seen.size}`;
554
- }
555
- seen.set(key, alert);
1000
+ this.collect(seen, alert, result);
556
1001
  }
557
1002
  result.fetchedCount = seen.size;
558
1003
  const truncated: boolean =
@@ -565,7 +1010,7 @@ export default class GoogleSecOpsPoller {
565
1010
  * A one-record permission probe is deliberately limited; it makes no
566
1011
  * assertion about whether the full detection window can be imported.
567
1012
  */
568
- result.complete = fetched.complete;
1013
+ result.complete = result.complete && fetched.complete;
569
1014
  if (truncated) {
570
1015
  result.warnings.push(
571
1016
  "Connection test reads at most one detection. Use Preview to inspect the full time range.",
@@ -585,23 +1030,211 @@ export default class GoogleSecOpsPoller {
585
1030
  truncated &&
586
1031
  current.endTime.getTime() - current.startTime.getTime() > 1000
587
1032
  ) {
588
- result.warnings.push(
589
- `Google limited the response for ${current.startTime.toISOString()} to ${current.endTime.toISOString()}; retrying smaller windows.`,
590
- );
1033
+ splits++;
591
1034
  pending.unshift(
592
1035
  { startTime: current.startTime, endTime: new Date(midpoint) },
593
1036
  { startTime: new Date(midpoint), endTime: current.endTime },
594
1037
  );
595
1038
  } else if (!fetched.complete || truncated) {
596
1039
  result.complete = false;
597
- result.warnings.push(
1040
+ outcome.leftUnread = true;
1041
+ this.addWarning(
1042
+ result,
598
1043
  truncated
599
- ? "Google still truncated a one-second window. Some detections may be missing; the cursor has not advanced."
600
- : "Google ended the response without confirming it was complete; the cursor has not advanced.",
1044
+ ? "Google still truncated a one-second window of the alerts view, so some alerts in it were not read."
1045
+ : "Google ended an alerts view response without confirming it was complete.",
1046
+ );
1047
+ }
1048
+ }
1049
+ if (splits > 0) {
1050
+ // One line rather than one per split: lastError joins every warning.
1051
+ result.warnings.push(
1052
+ `Google limited the alerts view response for ${window.startTime.toISOString()} to ${window.endTime.toISOString()}, so it was split into smaller windows ${splits} ${splits === 1 ? "time" : "times"}.`,
1053
+ );
1054
+ }
1055
+ return outcome;
1056
+ }
1057
+
1058
+ /*
1059
+ * A detections-search pass (legacySearchDetections or the curated
1060
+ * variant), one basis at a time, following nextPageToken under the page
1061
+ * budget the two search passes share.
1062
+ */
1063
+ private static async searchPass(
1064
+ client: GoogleSecOpsClient,
1065
+ window: PollWindow,
1066
+ result: GoogleSecOpsRunResult,
1067
+ budget: PollBudget,
1068
+ seen: Map<string, JSONObject>,
1069
+ options: { curated: boolean; bases: Array<GoogleSecOpsListBasis> },
1070
+ ): Promise<PassOutcome> {
1071
+ const label: string = options.curated
1072
+ ? "curated rule detections"
1073
+ : "rule detections";
1074
+ const outcome: PassOutcome = {
1075
+ returned: 0,
1076
+ requests: 0,
1077
+ stoppedBy: null,
1078
+ leftUnread: false,
1079
+ };
1080
+ for (const listBasis of options.bases) {
1081
+ let pageToken: string | undefined = undefined;
1082
+ do {
1083
+ const stop: PassStop = this.budgetStop(
1084
+ budget,
1085
+ budget.searchRequests,
1086
+ MAX_SEARCH_PAGES_PER_POLL,
1087
+ );
1088
+ if (stop) {
1089
+ outcome.stoppedBy = stop;
1090
+ result.complete = false;
1091
+ return outcome;
1092
+ }
1093
+ budget.searchRequests++;
1094
+ outcome.requests++;
1095
+ result.requestCount++;
1096
+ const page: SearchDetectionsResult = await client.searchDetections({
1097
+ startTime: window.startTime,
1098
+ endTime: window.endTime,
1099
+ listBasis,
1100
+ alertingOnly: !result.includeNonAlertingDetections,
1101
+ pageSize: result.type === "test" ? 1 : MAX_SEARCH_PAGE_SIZE,
1102
+ pageToken,
1103
+ curated: options.curated,
1104
+ });
1105
+ outcome.returned += page.detections.length;
1106
+ for (const detection of page.detections) {
1107
+ this.collect(seen, detection, result);
1108
+ }
1109
+ result.fetchedCount = seen.size;
1110
+ if (page.truncated) {
1111
+ /*
1112
+ * respTooLargeDetectionsTruncated: Google cut the page by byte
1113
+ * size and the rest is not reachable through a page token, so the
1114
+ * window is not fully read.
1115
+ */
1116
+ result.complete = false;
1117
+ outcome.leftUnread = true;
1118
+ this.addWarning(
1119
+ result,
1120
+ `Google truncated a page of ${label} by size for ${window.startTime.toISOString()} to ${window.endTime.toISOString()}, so part of the window was not read.`,
1121
+ );
1122
+ }
1123
+ if (result.type === "test") {
1124
+ // One record per pass is all a permission probe needs.
1125
+ return outcome;
1126
+ }
1127
+ pageToken = page.nextPageToken || undefined;
1128
+ } while (pageToken);
1129
+ }
1130
+ return outcome;
1131
+ }
1132
+
1133
+ /*
1134
+ * Whether a pass may make another request: each kind of read has its own
1135
+ * request count, and all of them share the poll's wall-clock bound so a
1136
+ * slow endpoint cannot run the worker job into its timeout.
1137
+ */
1138
+ private static budgetStop(
1139
+ budget: PollBudget,
1140
+ used: number,
1141
+ limit: number,
1142
+ ): PassStop {
1143
+ if (used >= limit) {
1144
+ return "requests";
1145
+ }
1146
+ if (Date.now() - budget.startedMs >= MAX_FETCH_DURATION_MS) {
1147
+ return "time";
1148
+ }
1149
+ return null;
1150
+ }
1151
+
1152
+ // Repeated conditions (every one-second window, every page) are said once.
1153
+ private static addWarning(
1154
+ result: GoogleSecOpsRunResult,
1155
+ warning: string,
1156
+ ): void {
1157
+ if (!result.warnings.includes(warning)) {
1158
+ result.warnings.push(warning);
1159
+ }
1160
+ }
1161
+
1162
+ /*
1163
+ * Union by Collection.id across passes and across the alerts view's
1164
+ * chunks; a record with no id falls back to a content hash, and an
1165
+ * object that cannot even be inspected is kept under a positional key so
1166
+ * it is counted as rejected without hiding the valid records beside it.
1167
+ */
1168
+ private static collect(
1169
+ seen: Map<string, JSONObject>,
1170
+ record: JSONObject,
1171
+ result: GoogleSecOpsRunResult,
1172
+ ): void {
1173
+ let key: string;
1174
+ try {
1175
+ key = readString(record, "id") || contentHashEventUid(record);
1176
+ } catch {
1177
+ key = `malformed:${result.requestCount}:${seen.size}`;
1178
+ }
1179
+ seen.set(key, record);
1180
+ }
1181
+
1182
+ /*
1183
+ * createdTime minus detectionTime over what was fetched. A lag beyond one
1184
+ * poll interval is the record a detection-time cursor skips, so the
1185
+ * warning names the reason polling reads created time.
1186
+ */
1187
+ private static measureCreationLag(
1188
+ alerts: Array<JSONObject>,
1189
+ connection: GoogleSecOpsConnection,
1190
+ result: GoogleSecOpsRunResult,
1191
+ ): GoogleSecOpsCreationLag {
1192
+ const intervalInMinutes: number = Math.max(
1193
+ 1,
1194
+ connection.pollIntervalInMinutes || DEFAULT_POLL_INTERVAL_IN_MINUTES,
1195
+ );
1196
+ const thresholdInMinutes: number =
1197
+ intervalInMinutes + CREATION_LAG_GRACE_IN_MINUTES;
1198
+ let measured: number = 0;
1199
+ let lateCount: number = 0;
1200
+ let maxLagMs: number = 0;
1201
+ for (const alert of alerts) {
1202
+ let detectionTime: Date | null = null;
1203
+ let createdTime: Date | null = null;
1204
+ try {
1205
+ detectionTime = parseEventTime(
1206
+ readValue(alert, "detectionTime") ??
1207
+ readValue(alert, "detection_time"),
601
1208
  );
1209
+ createdTime = parseEventTime(
1210
+ readValue(alert, "createdTime") ?? readValue(alert, "created_time"),
1211
+ );
1212
+ } catch {
1213
+ continue;
1214
+ }
1215
+ if (!detectionTime || !createdTime) {
1216
+ continue;
1217
+ }
1218
+ measured++;
1219
+ const lagMs: number = createdTime.getTime() - detectionTime.getTime();
1220
+ if (lagMs > maxLagMs) {
1221
+ maxLagMs = lagMs;
602
1222
  }
1223
+ if (lagMs > thresholdInMinutes * 60 * 1000) {
1224
+ lateCount++;
1225
+ }
1226
+ }
1227
+ const lag: GoogleSecOpsCreationLag = {
1228
+ measured,
1229
+ lateCount,
1230
+ maxLagMinutes: Math.round(maxLagMs / 60000),
1231
+ };
1232
+ if (lateCount > 0) {
1233
+ result.warnings.push(
1234
+ `${lateCount} of ${measured} detections were created more than ${thresholdInMinutes} minutes after their detection time (up to ${lag.maxLagMinutes} minutes). This is why the connector polls by created time: a cursor over detection time would already have moved past them.`,
1235
+ );
603
1236
  }
604
- return Array.from(seen.values());
1237
+ return lag;
605
1238
  }
606
1239
 
607
1240
  private static sample(
@@ -637,54 +1270,20 @@ export default class GoogleSecOpsPoller {
637
1270
  }
638
1271
 
639
1272
  /**
640
- * This is a correctness lookup, not a dashboard query: returning a partial
641
- * result on timeout or reading a lagging replica would create duplicates.
642
- * Read every replica and fail closed on an unavailable shard. The preceding
643
- * source lock plus synchronous inserts make at least one replica contain
644
- * every earlier acknowledged import before this query begins.
1273
+ * Thin delegate kept as a static on this class: existing tests spy on it,
1274
+ * and the query itself now lives in SecurityEventDedupe so the Google
1275
+ * poller and the Security Event Connections poller cannot drift.
645
1276
  */
646
1277
  public static async findExistingEventUids(
647
1278
  projectId: ObjectID,
648
1279
  ids: Array<string>,
649
1280
  ): Promise<Set<string>> {
650
- const existing: Set<string> = new Set();
651
- const startedMs: number = Date.now();
652
- for (
653
- let offset: number = 0;
654
- offset < ids.length;
655
- offset += MAX_ALERTS_PER_REQUEST
656
- ) {
657
- if (Date.now() - startedMs >= 2 * 60 * 1000) {
658
- throw new Error(
659
- "Duplicate lookup exceeded its time limit. Retry a smaller import window.",
660
- );
661
- }
662
- const statement: Statement = SQL`SELECT DISTINCT eventUid FROM clusterAllReplicas(
663
- ${{ type: TableColumnType.Text, value: getClickhouseClusterName() }},
664
- ${{ type: TableColumnType.Text, value: getClickhouseDatabaseName() }},
665
- ${{ type: TableColumnType.Text, value: getStorageTableName(SecurityEventService.model.tableName) }}
666
- ) WHERE projectId = ${{ type: TableColumnType.ObjectID, value: projectId }}
667
- AND vendorName = ${{ type: TableColumnType.Text, value: "Google" }}
668
- AND productName = ${{ type: TableColumnType.Text, value: SECOPS_SERVICE_NAME }}
669
- AND eventUid IN ${{ type: TableColumnType.ArrayText, value: ids.slice(offset, offset + MAX_ALERTS_PER_REQUEST) }}`;
670
- statement.append(
671
- getQuerySettings({
672
- maxExecutionTimeInSeconds: 30,
673
- timeoutOverflowMode: "throw",
674
- boundScanMemory: true,
675
- additionalSettings: { skip_unavailable_shards: 0 },
676
- }),
677
- );
678
- const response: ResultSet<"JSON"> =
679
- await SecurityEventService.executeQuery(statement);
680
- const data: ResponseJSON<JSONObject> = await response.json<JSONObject>();
681
- for (const row of data.data) {
682
- if (typeof row["eventUid"] === "string") {
683
- existing.add(row["eventUid"]);
684
- }
685
- }
686
- }
687
- return existing;
1281
+ return SecurityEventDedupe.findExistingEventUids({
1282
+ projectId,
1283
+ vendorName: SECOPS_VENDOR_NAME,
1284
+ productName: SECOPS_SERVICE_NAME,
1285
+ ids,
1286
+ });
688
1287
  }
689
1288
 
690
1289
  private static async ingest(