@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.
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/RumApplication.ts +1 -1
- package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
- package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
- package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
- package/Server/API/AIInvestigationAPI.ts +484 -24
- package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
- package/Server/API/SecurityEventConnectionAPI.ts +250 -0
- package/Server/API/TelemetryAPI.ts +402 -265
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Middleware/TelemetryIngest.ts +101 -6
- package/Server/Services/ExceptionAggregationService.ts +7 -16
- package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/InventoryItemService.ts +28 -0
- package/Server/Services/LogAggregationService.ts +17 -23
- package/Server/Services/MetricAggregationService.ts +4 -14
- package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
- package/Server/Services/SecurityEventConnectionService.ts +538 -0
- package/Server/Services/TraceAggregationService.ts +77 -28
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
- package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
- package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
- package/Server/Utils/AI/Toolbox/Index.ts +38 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
- package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
- package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
- package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
- package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
- package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
- package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
- package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
- package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
- package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
- package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
- package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
- package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
- package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
- package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
- package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
- package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
- package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
- package/Server/Utils/StartServer.ts +10 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
- package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
- package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
- package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
- package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
- package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
- package/Server/Utils/Telemetry.ts +18 -0
- package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
- package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
- package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
- package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
- package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
- package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
- package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
- package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
- package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
- package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
- package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
- package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
- package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
- package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
- package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
- package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
- package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
- package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
- package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
- package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
- package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
- package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
- package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
- package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
- package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
- package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
- package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
- package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
- package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
- package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
- package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
- package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
- package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
- package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
- package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
- package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
- package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
- package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
- package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
- package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
- package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
- package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
- package/Tests/Models/InventoryItemNaming.test.ts +132 -16
- package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
- package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
- package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
- package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
- package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
- package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
- package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
- package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
- package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
- package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
- package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
- package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
- package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
- package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
- package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
- package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
- package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
- package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +348 -2
- package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
- package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
- package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
- package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
- package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
- package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
- package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
- package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
- package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
- package/Tests/UI/Components/Card.test.tsx +312 -1
- package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
- package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
- package/Tests/UI/Components/Checkbox.test.tsx +79 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
- package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
- package/Tests/UI/Components/FacetSections.test.tsx +581 -0
- package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
- package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
- package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
- package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
- package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
- package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
- package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
- package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
- package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
- package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
- package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
- package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
- package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
- package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
- package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
- package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
- package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
- package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
- package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
- package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
- package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
- package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
- package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
- package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
- package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
- package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
- package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
- package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
- package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
- package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
- package/Tests/UI/Utils/Clipboard.test.ts +154 -0
- package/Tests/UI/Utils/Navigation.test.ts +24 -0
- package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
- package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
- package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
- package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
- package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
- package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
- package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
- package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
- package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
- package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
- package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
- package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
- package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
- package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
- package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
- package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
- package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
- package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
- package/Types/AI/AIChatTypes.ts +8 -0
- package/Types/AI/InvestigationEvidence.ts +96 -0
- package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
- package/Types/Permission.ts +2 -2
- package/Types/Rum/SessionReplay.ts +109 -0
- package/Types/Rum/SessionReplayApi.ts +31 -0
- package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
- package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
- package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
- package/Types/Telemetry/EntityType.ts +14 -0
- package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
- package/Types/Telemetry/LockedFilterDetail.ts +46 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
- package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
- package/UI/Components/Card/Card.tsx +118 -83
- package/UI/Components/Checkbox/Checkbox.tsx +8 -5
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
- package/UI/Components/Detail/Detail.tsx +137 -22
- package/UI/Components/ErrorBoundary.tsx +38 -0
- package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
- package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
- package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
- package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
- package/UI/Components/LogsViewer/types.ts +6 -0
- package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
- package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
- package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
- package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
- package/UI/Components/ModelList/ModelList.tsx +33 -8
- package/UI/Components/Page/ModelPage.tsx +119 -54
- package/UI/Components/Table/Table.tsx +8 -0
- package/UI/Components/Table/TableRow.tsx +13 -5
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
- package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
- package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
- package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
- package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
- package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
- package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
- package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
- package/UI/Components/TelemetryViewer/types.ts +19 -0
- package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
- package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
- package/UI/Utils/Clipboard.ts +68 -2
- package/UI/Utils/Navigation.ts +17 -2
- package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
- package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
- package/Utils/AI/InvestigationReport.ts +1789 -0
- package/Utils/API.ts +90 -1
- package/Utils/Rum/SessionReplayHealth.ts +11 -0
- package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
- package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
- package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
- package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
- package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
- package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
- package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
- package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
- package/Utils/Telemetry/CrossSignalScope.ts +22 -8
- package/Utils/Telemetry/EntityRelationship.ts +8 -0
- package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
- package/Utils/Telemetry/NetworkHost.ts +43 -0
- package/Utils/Telemetry/OriginAllowList.ts +139 -4
- package/Utils/Traces/CriticalPath.ts +10 -4
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
- package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
- package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
- package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
- package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
- package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +248 -170
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
- package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +22 -0
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +8 -17
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +4 -14
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
- package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
- package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
- package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
- package/build/dist/Server/Services/TraceAggregationService.js +51 -25
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
- package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
- package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
- package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
- package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry.js +11 -5
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
- package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
- package/build/dist/Types/Permission.js +2 -2
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +95 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
- package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
- package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
- package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +14 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
- package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
- package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
- package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
- package/build/dist/UI/Components/Card/Card.js +34 -17
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +63 -13
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +34 -0
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
- package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
- package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
- package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +62 -34
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +3 -1
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +8 -1
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
- package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
- package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
- package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
- package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
- package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
- package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
- package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
- package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
- package/build/dist/UI/Utils/Clipboard.js +59 -2
- package/build/dist/UI/Utils/Clipboard.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +11 -2
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
- package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
- package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
- package/build/dist/Utils/API.js +25 -0
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
- package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
- package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
- package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
- package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
- package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
- package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
- package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
- package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
- package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
- package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
- package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
- package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
- package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
- package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
- package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
- package/build/dist/Utils/Traces/CriticalPath.js +9 -4
- package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
- package/package.json +1 -1
- package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
- package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
- package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
- package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
- package/UI/Components/GanttChart/Index.tsx +0 -90
- package/UI/Components/GanttChart/Row/Index.tsx +0 -22
- package/UI/Components/GanttChart/Row/Row.tsx +0 -231
- package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
- package/UI/Components/GanttChart/Rows.tsx +0 -59
- package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
- package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
- package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
- package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
- package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
- package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
- package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Index.js +0 -39
- package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
- package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
- package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
- package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
- package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
- package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
- package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
- package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
- package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
|
@@ -7,13 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import TableColumnType from "../../../../Types/AnalyticsDatabase/TableColumnType";
|
|
11
|
-
import { SQL } from "../../AnalyticsDatabase/Statement";
|
|
12
|
-
import { getClickhouseClusterName, getClickhouseDatabaseName, getStorageTableName, } from "../../AnalyticsDatabase/ClusterConfig";
|
|
13
|
-
import { getQuerySettings } from "../../AnalyticsDatabase/QuerySettingsHelper";
|
|
14
10
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
15
11
|
import { contentHashEventUid, parseEventTime, readString, readValue, } from "../../../../Utils/SecurityEvent/NormalizerHelpers";
|
|
16
|
-
import Semaphore from "../../../Infrastructure/Semaphore";
|
|
12
|
+
import Semaphore, { SemaphoreLockTimeoutError, } from "../../../Infrastructure/Semaphore";
|
|
17
13
|
import GoogleSecOpsConnection from "../../../../Models/DatabaseModels/GoogleSecOpsConnection";
|
|
18
14
|
import LIMIT_MAX from "../../../../Types/Database/LimitMax";
|
|
19
15
|
import OneUptimeDate from "../../../../Types/Date";
|
|
@@ -26,18 +22,60 @@ import logger from "../../Logger";
|
|
|
26
22
|
import { redactLogString } from "../../LogRedaction";
|
|
27
23
|
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
28
24
|
import ConnectorErrorMessage from "../ConnectorErrorMessage";
|
|
25
|
+
import SecurityEventDedupe from "../SecurityEventDedupe";
|
|
29
26
|
import { buildSecurityEventDbRow } from "../SecurityEventRow";
|
|
30
27
|
import ThreatIntelEnricher from "../ThreatIntel/ThreatIntelEnricher";
|
|
31
28
|
import GoogleSecOpsClient from "./GoogleSecOpsClient";
|
|
32
29
|
const SECOPS_SERVICE_NAME = "Google SecOps";
|
|
33
|
-
const
|
|
34
|
-
|
|
30
|
+
const SECOPS_VENDOR_NAME = "Google";
|
|
31
|
+
/*
|
|
32
|
+
* The first poll of a new connection reads a full day of CREATED detections.
|
|
33
|
+
* Fifteen minutes used to be the default, and on a tenant whose rules run
|
|
34
|
+
* hourly or daily it reliably returned nothing, which read as "the
|
|
35
|
+
* connector is broken" on the very first look.
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_LOOKBACK_IN_MINUTES = 24 * 60;
|
|
38
|
+
/*
|
|
39
|
+
* Adaptive catch-up. A scheduled poll reads at most MAX_CHUNK_MINUTES of
|
|
40
|
+
* created time past its cursor. A poll that cannot read its window in one
|
|
41
|
+
* run halves the next chunk and a complete poll doubles it back, because
|
|
42
|
+
* re-reading an identical window that never fits pinned the cursor forever
|
|
43
|
+
* and nothing new was ever imported. The chunk is measured from the cursor,
|
|
44
|
+
* not from the overlapped window start: a chunk no longer than the overlap
|
|
45
|
+
* would otherwise end at or before the cursor and never move it.
|
|
46
|
+
*/
|
|
47
|
+
const MAX_CHUNK_MINUTES = 24 * 60;
|
|
48
|
+
const MIN_CHUNK_MINUTES = 1;
|
|
35
49
|
const WINDOW_OVERLAP_IN_MINUTES = 1;
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
/*
|
|
51
|
+
* Separate request budgets per kind of read. The two created-time search
|
|
52
|
+
* passes are the authoritative read of rule detections and share one page
|
|
53
|
+
* budget; the alerts view splits truncated windows and gets its own, so a
|
|
54
|
+
* burst that the alerts view has to split can no longer starve the searches
|
|
55
|
+
* (or the other way round).
|
|
56
|
+
*/
|
|
57
|
+
const MAX_SEARCH_PAGES_PER_POLL = 20;
|
|
58
|
+
const MAX_ALERTS_VIEW_REQUESTS_PER_POLL = 16;
|
|
59
|
+
// The worker job times out at ten minutes; normalizing and importing follow.
|
|
60
|
+
const MAX_FETCH_DURATION_MS = 4 * 60 * 1000;
|
|
38
61
|
const MAX_ALERTS_PER_REQUEST = 1000;
|
|
62
|
+
const MAX_SEARCH_PAGE_SIZE = 1000;
|
|
39
63
|
const MAX_DIAGNOSTIC_SAMPLES = 25;
|
|
40
64
|
const MAX_RANGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
65
|
+
const DEFAULT_POLL_INTERVAL_IN_MINUTES = 5;
|
|
66
|
+
/*
|
|
67
|
+
* A detection created within one poll interval plus this grace of its
|
|
68
|
+
* detection time would still have been caught by a detection-time cursor;
|
|
69
|
+
* anything later is exactly the record the old basis lost.
|
|
70
|
+
*/
|
|
71
|
+
const CREATION_LAG_GRACE_IN_MINUTES = 1;
|
|
72
|
+
/*
|
|
73
|
+
* Curated (Google-authored) rule detections are optional: tenants without
|
|
74
|
+
* that entitlement answer 403 (and some 400/404) on the curated route. Those
|
|
75
|
+
* statuses degrade the pass to a warning; 401, 429, 5xx, timeouts and parse
|
|
76
|
+
* failures still fail the run like any other pass.
|
|
77
|
+
*/
|
|
78
|
+
const CURATED_OPTIONAL_STATUSES = [400, 403, 404];
|
|
41
79
|
export const GOOGLE_SECOPS_SOURCE_LOCK_NAMESPACE = "GoogleSecOpsSource";
|
|
42
80
|
class RecordedPollFailure extends Error {
|
|
43
81
|
}
|
|
@@ -58,6 +96,8 @@ export default class GoogleSecOpsPoller {
|
|
|
58
96
|
pollIntervalInMinutes: true,
|
|
59
97
|
lastPolledAt: true,
|
|
60
98
|
cursor: true,
|
|
99
|
+
// Carries nextChunkMinutes, the length of the next catch-up chunk.
|
|
100
|
+
lastPollResult: true,
|
|
61
101
|
includeNonAlertingDetections: true,
|
|
62
102
|
},
|
|
63
103
|
skip: 0,
|
|
@@ -138,13 +178,29 @@ export default class GoogleSecOpsPoller {
|
|
|
138
178
|
if (options.type === "test" || options.type === "preview") {
|
|
139
179
|
return this.executeUnlocked(connection, options, clientOverride);
|
|
140
180
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
181
|
+
let mutex;
|
|
182
|
+
try {
|
|
183
|
+
mutex = await Semaphore.lock({
|
|
184
|
+
namespace: GOOGLE_SECOPS_SOURCE_LOCK_NAMESPACE,
|
|
185
|
+
key: connection.projectId.toString(),
|
|
186
|
+
lockTimeout: 30000,
|
|
187
|
+
acquireTimeout: 10000,
|
|
188
|
+
retryInterval: 100,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
/*
|
|
193
|
+
* The library's timeout message names Redis keys and reads like an
|
|
194
|
+
* infrastructure fault. It is the expected steady state when a long
|
|
195
|
+
* import holds the source lock, so it is reworded for the person
|
|
196
|
+
* reading run history, and left as a BadDataException so the run
|
|
197
|
+
* executor records it without retrying.
|
|
198
|
+
*/
|
|
199
|
+
if (error instanceof SemaphoreLockTimeoutError) {
|
|
200
|
+
throw new BadDataException("Another poll or import for this source is still running in this project. This run was skipped; polling continues on the next scheduled tick.");
|
|
201
|
+
}
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
148
204
|
try {
|
|
149
205
|
/*
|
|
150
206
|
* An injected client also injects the connection snapshot for tests.
|
|
@@ -162,6 +218,12 @@ export default class GoogleSecOpsPoller {
|
|
|
162
218
|
instanceResourceName: true,
|
|
163
219
|
serviceAccountJson: true,
|
|
164
220
|
cursor: true,
|
|
221
|
+
/*
|
|
222
|
+
* Reloaded with the cursor: the previous poll's result decides
|
|
223
|
+
* this poll's chunk and whether it resumes past a forced advance.
|
|
224
|
+
*/
|
|
225
|
+
lastPollResult: true,
|
|
226
|
+
pollIntervalInMinutes: true,
|
|
165
227
|
includeNonAlertingDetections: true,
|
|
166
228
|
},
|
|
167
229
|
props: { isRoot: true },
|
|
@@ -206,31 +268,125 @@ export default class GoogleSecOpsPoller {
|
|
|
206
268
|
endTime.getTime() - startTime.getTime() > MAX_RANGE_MS) {
|
|
207
269
|
throw new BadDataException("Select a valid past time range of at most seven days.");
|
|
208
270
|
}
|
|
271
|
+
return { startTime, endTime, warnings, hasUsableCursor };
|
|
272
|
+
}
|
|
273
|
+
if (options.type !== "poll") {
|
|
274
|
+
return { startTime, endTime, warnings, hasUsableCursor };
|
|
209
275
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
276
|
+
const chunk = this.getChunkMinutes(connection);
|
|
277
|
+
let cursorTime = null;
|
|
278
|
+
if (connection.cursor) {
|
|
279
|
+
const parsed = new Date(connection.cursor);
|
|
280
|
+
if (!Number.isFinite(parsed.getTime())) {
|
|
281
|
+
warnings.push("The saved cursor is unreadable; polling the default 24 hour window.");
|
|
214
282
|
}
|
|
215
|
-
else if (
|
|
216
|
-
warnings.push("The saved cursor is in the future; polling the default
|
|
283
|
+
else if (parsed >= now) {
|
|
284
|
+
warnings.push("The saved cursor is in the future; polling the default 24 hour window.");
|
|
217
285
|
}
|
|
218
286
|
else {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
287
|
+
cursorTime = parsed;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (cursorTime) {
|
|
291
|
+
hasUsableCursor = true;
|
|
292
|
+
startTime = this.resumesFromForcedAdvance(connection, cursorTime)
|
|
293
|
+
? cursorTime
|
|
294
|
+
: OneUptimeDate.addRemoveMinutes(cursorTime, -WINDOW_OVERLAP_IN_MINUTES);
|
|
295
|
+
const chunkEnd = OneUptimeDate.addRemoveMinutes(cursorTime, chunk);
|
|
296
|
+
if (chunkEnd < endTime) {
|
|
297
|
+
endTime = chunkEnd;
|
|
298
|
+
warnings.push(`Catching up from the saved cursor in ${this.describeChunk(chunk)} windows. Later detections will be fetched by subsequent polls.`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
// The first window keeps its 24 hour lookback; a narrowed chunk only shortens its end.
|
|
303
|
+
const chunkEnd = OneUptimeDate.addRemoveMinutes(startTime, chunk);
|
|
304
|
+
if (chunkEnd < endTime) {
|
|
305
|
+
endTime = chunkEnd;
|
|
306
|
+
warnings.push(`Reading the first ${chunk} minutes of the 24 hour lookback. Later detections will be fetched by subsequent polls.`);
|
|
226
307
|
}
|
|
227
308
|
}
|
|
228
|
-
|
|
309
|
+
const chunkBase = cursorTime || startTime;
|
|
310
|
+
return {
|
|
311
|
+
startTime,
|
|
312
|
+
endTime,
|
|
313
|
+
warnings,
|
|
314
|
+
hasUsableCursor,
|
|
315
|
+
chunkBase,
|
|
316
|
+
requestedChunkMinutes: chunk,
|
|
317
|
+
chunkMinutes: Math.max(MIN_CHUNK_MINUTES, Math.ceil((endTime.getTime() - chunkBase.getTime()) / 60000)),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/*
|
|
321
|
+
* lastPollResult.nextChunkMinutes when it is a whole number of minutes
|
|
322
|
+
* inside the allowed range. Anything else (no previous poll, a hand-edited
|
|
323
|
+
* row, a result from before adaptive catch-up) reads the full 24 hours.
|
|
324
|
+
*/
|
|
325
|
+
static getChunkMinutes(connection) {
|
|
326
|
+
const stored = connection.lastPollResult
|
|
327
|
+
? connection.lastPollResult["nextChunkMinutes"]
|
|
328
|
+
: undefined;
|
|
329
|
+
if (typeof stored === "number" &&
|
|
330
|
+
Number.isInteger(stored) &&
|
|
331
|
+
stored >= MIN_CHUNK_MINUTES &&
|
|
332
|
+
stored <= MAX_CHUNK_MINUTES) {
|
|
333
|
+
return stored;
|
|
334
|
+
}
|
|
335
|
+
return MAX_CHUNK_MINUTES;
|
|
336
|
+
}
|
|
337
|
+
/*
|
|
338
|
+
* True when the saved cursor is the end of a minute that a forced advance
|
|
339
|
+
* skipped (or a poll that already started there and did not move the
|
|
340
|
+
* cursor). Such a poll starts at the cursor without the overlap: the
|
|
341
|
+
* overlap minute is exactly the minute that could not be read, and
|
|
342
|
+
* re-reading it would overflow again and force a second advance over a
|
|
343
|
+
* minute that was never the problem.
|
|
344
|
+
*/
|
|
345
|
+
static resumesFromForcedAdvance(connection, cursorTime) {
|
|
346
|
+
const stored = connection.lastPollResult;
|
|
347
|
+
if (!stored || stored["type"] !== "poll") {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
const readTime = (key) => {
|
|
351
|
+
const value = stored[key];
|
|
352
|
+
return typeof value === "string" ? Date.parse(value) : Number.NaN;
|
|
353
|
+
};
|
|
354
|
+
return ((stored["forcedAdvance"] === true &&
|
|
355
|
+
readTime("windowEnd") === cursorTime.getTime()) ||
|
|
356
|
+
readTime("windowStart") === cursorTime.getTime());
|
|
357
|
+
}
|
|
358
|
+
static describeChunk(minutes) {
|
|
359
|
+
if (minutes % 60 === 0) {
|
|
360
|
+
const hours = minutes / 60;
|
|
361
|
+
return `${hours} hour`;
|
|
362
|
+
}
|
|
363
|
+
return `${minutes} minute`;
|
|
229
364
|
}
|
|
230
365
|
static async executeUnlocked(connection, options, clientOverride) {
|
|
231
366
|
const window = this.getWindow(connection, options);
|
|
232
367
|
const startedMs = Date.now();
|
|
233
|
-
|
|
368
|
+
/*
|
|
369
|
+
* Preview and backfill take a detection-time range from the person (it
|
|
370
|
+
* is how the SecOps alerts view frames time), and read created time as
|
|
371
|
+
* well so an import of "yesterday" also carries detections created
|
|
372
|
+
* yesterday about older events. Polls read created time only; the
|
|
373
|
+
* alerts-view pass supplies detection time.
|
|
374
|
+
*/
|
|
375
|
+
const isHistorical = options.type === "preview" || options.type === "backfill";
|
|
376
|
+
const searchBases = isHistorical
|
|
377
|
+
? ["CREATED_TIME", "DETECTION_TIME"]
|
|
378
|
+
: ["CREATED_TIME"];
|
|
379
|
+
const basisLabel = isHistorical
|
|
380
|
+
? "created and detection time"
|
|
381
|
+
: "created time";
|
|
382
|
+
const result = Object.assign(Object.assign(Object.assign({ type: options.type }, (options.runId ? { runId: options.runId } : {})), { status: "running", startedAt: new Date(startedMs).toISOString(), completedAt: new Date(startedMs).toISOString(), durationMs: 0, windowStart: window.startTime.toISOString(), windowEnd: window.endTime.toISOString(), includeNonAlertingDetections: connection.includeNonAlertingDetections === true, basis: isHistorical ? "detection-time" : "created-time", sourceCounts: { ruleDetections: 0, curatedDetections: 0, alertsView: 0 }, fetchedCount: 0, ingestedCount: 0, duplicateCount: 0, rejectedCount: 0, failedCount: 0, complete: true, requestCount: 0, warnings: window.warnings, samples: [], checks: [] }), (window.chunkMinutes !== undefined
|
|
383
|
+
? { chunkMinutes: window.chunkMinutes }
|
|
384
|
+
: {}));
|
|
385
|
+
const budget = {
|
|
386
|
+
startedMs,
|
|
387
|
+
searchRequests: 0,
|
|
388
|
+
alertsViewRequests: 0,
|
|
389
|
+
};
|
|
234
390
|
let phase = "Validate configuration";
|
|
235
391
|
let phaseStartedMs = Date.now();
|
|
236
392
|
try {
|
|
@@ -257,16 +413,71 @@ export default class GoogleSecOpsPoller {
|
|
|
257
413
|
message: "Google accepted the service account credentials.",
|
|
258
414
|
});
|
|
259
415
|
}
|
|
260
|
-
|
|
416
|
+
/*
|
|
417
|
+
* Three passes over one window, unioned by Collection.id. The alerts
|
|
418
|
+
* view alone filters on DETECTION time, and a rule that runs hourly
|
|
419
|
+
* creates detections whose detection time is already behind a
|
|
420
|
+
* forward-only cursor, so it can never be the primary read.
|
|
421
|
+
*/
|
|
422
|
+
const seen = new Map();
|
|
423
|
+
phase = `Read rule detections by ${basisLabel}`;
|
|
261
424
|
phaseStartedMs = Date.now();
|
|
262
|
-
const
|
|
263
|
-
result.
|
|
264
|
-
|
|
425
|
+
const rulePass = await this.searchPass(client, window, result, budget, seen, { curated: false, bases: searchBases });
|
|
426
|
+
result.sourceCounts.ruleDetections = rulePass.returned;
|
|
427
|
+
this.recordPass({
|
|
428
|
+
result,
|
|
265
429
|
name: phase,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
430
|
+
startedMs: phaseStartedMs,
|
|
431
|
+
outcome: rulePass,
|
|
432
|
+
countMessage: `${rulePass.returned} rule detections returned for the window by ${basisLabel}.`,
|
|
269
433
|
});
|
|
434
|
+
phase = `Read curated rule detections by ${basisLabel}`;
|
|
435
|
+
phaseStartedMs = Date.now();
|
|
436
|
+
try {
|
|
437
|
+
const curatedPass = await this.searchPass(client, window, result, budget, seen, { curated: true, bases: searchBases });
|
|
438
|
+
result.sourceCounts.curatedDetections = curatedPass.returned;
|
|
439
|
+
this.recordPass({
|
|
440
|
+
result,
|
|
441
|
+
name: phase,
|
|
442
|
+
startedMs: phaseStartedMs,
|
|
443
|
+
outcome: curatedPass,
|
|
444
|
+
countMessage: `${curatedPass.returned} curated rule detections returned for the window by ${basisLabel}.`,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
catch (curatedError) {
|
|
448
|
+
/*
|
|
449
|
+
* Named differently from the outer catch on purpose: the guidance
|
|
450
|
+
* accuracy test reads executeUnlocked's first catch clause, by its
|
|
451
|
+
* variable name, as the run-failure path.
|
|
452
|
+
*/
|
|
453
|
+
const status = GoogleSecOpsClient.readHttpStatus(curatedError);
|
|
454
|
+
if (status === null || !CURATED_OPTIONAL_STATUSES.includes(status)) {
|
|
455
|
+
throw curatedError;
|
|
456
|
+
}
|
|
457
|
+
const message = redactLogString(ConnectorErrorMessage.toMessage(curatedError));
|
|
458
|
+
result.warnings.push(`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.`);
|
|
459
|
+
result.checks.push({
|
|
460
|
+
name: phase,
|
|
461
|
+
status: "warn",
|
|
462
|
+
durationMs: Date.now() - phaseStartedMs,
|
|
463
|
+
message,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
phase = "Read alerts view by detection time";
|
|
467
|
+
phaseStartedMs = Date.now();
|
|
468
|
+
const alertsPass = await this.fetchWindows(client, window, result, budget, seen);
|
|
469
|
+
result.sourceCounts.alertsView = alertsPass.returned;
|
|
470
|
+
this.recordPass({
|
|
471
|
+
result,
|
|
472
|
+
name: phase,
|
|
473
|
+
startedMs: phaseStartedMs,
|
|
474
|
+
outcome: alertsPass,
|
|
475
|
+
countMessage: `${alertsPass.returned} alerts returned by detection time.`,
|
|
476
|
+
successMessage: `${alertsPass.returned} alerts returned by detection time. The configured Google SecOps instance is reachable and allows reading detections.`,
|
|
477
|
+
});
|
|
478
|
+
const alerts = Array.from(seen.values());
|
|
479
|
+
result.fetchedCount = alerts.length;
|
|
480
|
+
result.creationLag = this.measureCreationLag(alerts, connection, result);
|
|
270
481
|
phase = "Normalize detections";
|
|
271
482
|
phaseStartedMs = Date.now();
|
|
272
483
|
const normalized = [];
|
|
@@ -287,18 +498,25 @@ export default class GoogleSecOpsPoller {
|
|
|
287
498
|
}
|
|
288
499
|
}
|
|
289
500
|
if (result.rejectedCount) {
|
|
290
|
-
|
|
501
|
+
/*
|
|
502
|
+
* Counted and reported, never retried: an object Google returns
|
|
503
|
+
* that is not a Collection is permanently unrecognizable, so
|
|
504
|
+
* holding the cursor for it would pin the window until the 24 hour
|
|
505
|
+
* chunk could never reach the present.
|
|
506
|
+
*/
|
|
507
|
+
result.warnings.push(`${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.`);
|
|
291
508
|
}
|
|
292
509
|
if (result.failedCount) {
|
|
293
|
-
result.warnings.push(`${result.failedCount} detections could not be normalized
|
|
510
|
+
result.warnings.push(`${result.failedCount} detections could not be normalized, so this window is not fully imported.`);
|
|
294
511
|
}
|
|
295
|
-
result.complete =
|
|
296
|
-
result.complete &&
|
|
297
|
-
result.failedCount === 0 &&
|
|
298
|
-
result.rejectedCount === 0;
|
|
512
|
+
result.complete = result.complete && result.failedCount === 0;
|
|
299
513
|
result.checks.push({
|
|
300
514
|
name: phase,
|
|
301
|
-
status: result.failedCount
|
|
515
|
+
status: result.failedCount
|
|
516
|
+
? "failed"
|
|
517
|
+
: result.rejectedCount
|
|
518
|
+
? "warn"
|
|
519
|
+
: "success",
|
|
302
520
|
durationMs: Date.now() - phaseStartedMs,
|
|
303
521
|
message: `${normalized.length} detections recognized; ${result.rejectedCount} rejected; ${result.failedCount} failed.`,
|
|
304
522
|
});
|
|
@@ -330,29 +548,40 @@ export default class GoogleSecOpsPoller {
|
|
|
330
548
|
message: result.error,
|
|
331
549
|
});
|
|
332
550
|
}
|
|
551
|
+
let decision = null;
|
|
552
|
+
if (options.type === "poll") {
|
|
553
|
+
decision = this.decideCursor(window, result);
|
|
554
|
+
result.nextChunkMinutes = decision.nextChunkMinutes;
|
|
555
|
+
if (decision.forcedAdvance) {
|
|
556
|
+
result.forcedAdvance = true;
|
|
557
|
+
}
|
|
558
|
+
if (decision.warning) {
|
|
559
|
+
/*
|
|
560
|
+
* A forced advance leaves a reported gap, so it leads the warnings
|
|
561
|
+
* (and therefore lastError); narrowing follows the reasons for it.
|
|
562
|
+
*/
|
|
563
|
+
if (decision.forcedAdvance) {
|
|
564
|
+
result.warnings.unshift(decision.warning);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
result.warnings.push(decision.warning);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
333
571
|
result.completedAt = OneUptimeDate.getCurrentDate().toISOString();
|
|
334
572
|
result.durationMs = Math.max(0, Date.now() - startedMs);
|
|
335
573
|
for (const warning of result.warnings) {
|
|
336
574
|
logger.warn(`GoogleSecOpsPoller: connection ${connection.id.toString()}: ${warning}`);
|
|
337
575
|
}
|
|
338
576
|
logger.debug(`GoogleSecOpsPoller: connection ${connection.id.toString()} fetched ${result.fetchedCount} alerts and ingested ${result.ingestedCount} security events for ${result.windowStart} to ${result.windowEnd}.`);
|
|
339
|
-
if (options.type === "poll") {
|
|
340
|
-
/*
|
|
341
|
-
* A failed first poll must keep its original start. Otherwise the next
|
|
342
|
-
* default lookback would slide past records before we ever imported them.
|
|
343
|
-
*/
|
|
344
|
-
const cursor = result.complete
|
|
345
|
-
? result.windowEnd
|
|
346
|
-
: !window.hasUsableCursor
|
|
347
|
-
? OneUptimeDate.addRemoveMinutes(window.startTime, WINDOW_OVERLAP_IN_MINUTES).toISOString()
|
|
348
|
-
: undefined;
|
|
577
|
+
if (options.type === "poll" && decision) {
|
|
349
578
|
await GoogleSecOpsConnectionService.updateOneById({
|
|
350
579
|
id: connection.id,
|
|
351
580
|
data: Object.assign(Object.assign(Object.assign({ lastPolledAt: new Date(result.completedAt), lastPollResult: result, lastError: (result.error ||
|
|
352
581
|
(result.complete
|
|
353
582
|
? null
|
|
354
583
|
: result.warnings.join(" ") ||
|
|
355
|
-
"Poll incomplete
|
|
584
|
+
"Poll incomplete.")) }, (decision.cursor ? { cursor: decision.cursor } : {})), (result.complete
|
|
356
585
|
? { lastSuccessfulPollAt: new Date(result.completedAt) }
|
|
357
586
|
: {})), (result.ingestedCount
|
|
358
587
|
? { lastEventIngestedAt: new Date(result.completedAt) }
|
|
@@ -369,32 +598,140 @@ export default class GoogleSecOpsPoller {
|
|
|
369
598
|
}
|
|
370
599
|
return result;
|
|
371
600
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
601
|
+
/*
|
|
602
|
+
* Where a scheduled poll leaves its cursor and how long the next chunk is.
|
|
603
|
+
* Google's search endpoints return newest first, so a poll that stops
|
|
604
|
+
* early has no "everything before here was read" point to resume from;
|
|
605
|
+
* the only ways forward are a shorter window and, at one minute, moving
|
|
606
|
+
* past the minute and reporting it.
|
|
607
|
+
*/
|
|
608
|
+
static decideCursor(window, result) {
|
|
609
|
+
const chunkMinutes = window.chunkMinutes || MAX_CHUNK_MINUTES;
|
|
610
|
+
const requestedChunkMinutes = window.requestedChunkMinutes || MAX_CHUNK_MINUTES;
|
|
611
|
+
/*
|
|
612
|
+
* A first poll that does not finish keeps its original start. Otherwise
|
|
613
|
+
* the next default lookback would slide past records before we ever
|
|
614
|
+
* imported them.
|
|
615
|
+
*/
|
|
616
|
+
const heldCursor = window.hasUsableCursor
|
|
617
|
+
? undefined
|
|
618
|
+
: OneUptimeDate.addRemoveMinutes(window.startTime, WINDOW_OVERLAP_IN_MINUTES).toISOString();
|
|
619
|
+
/*
|
|
620
|
+
* Failed and finished polls work from the requested chunk, not from the
|
|
621
|
+
* window read: a caught-up window is only as long as the gap to now, and
|
|
622
|
+
* keeping that as the next chunk would make polls after an outage or a
|
|
623
|
+
* failure fall behind the present for no reason.
|
|
624
|
+
*/
|
|
625
|
+
if (result.status === "failed") {
|
|
626
|
+
// A failure says nothing about volume, so the chunk is kept as it was.
|
|
627
|
+
return {
|
|
628
|
+
cursor: heldCursor,
|
|
629
|
+
nextChunkMinutes: requestedChunkMinutes,
|
|
630
|
+
forcedAdvance: false,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
if (result.complete) {
|
|
634
|
+
// A finished poll never shrinks the chunk; it doubles back after narrowing.
|
|
635
|
+
return {
|
|
636
|
+
cursor: result.windowEnd,
|
|
637
|
+
nextChunkMinutes: Math.min(MAX_CHUNK_MINUTES, Math.max(requestedChunkMinutes, chunkMinutes * 2)),
|
|
638
|
+
forcedAdvance: false,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
if (chunkMinutes > MIN_CHUNK_MINUTES) {
|
|
642
|
+
const nextChunkMinutes = Math.max(MIN_CHUNK_MINUTES, Math.floor(chunkMinutes / 2));
|
|
643
|
+
return {
|
|
644
|
+
cursor: heldCursor,
|
|
645
|
+
nextChunkMinutes,
|
|
646
|
+
forcedAdvance: false,
|
|
647
|
+
warning: `This window holds more records than one poll can read; the next poll reads a ${nextChunkMinutes} minute window from the same starting point.`,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/*
|
|
651
|
+
* One minute that still cannot be read. Holding here would stall polling
|
|
652
|
+
* for good, so the cursor moves past the minute and the gap is reported
|
|
653
|
+
* loudly with the way to recover it.
|
|
654
|
+
*/
|
|
655
|
+
const from = window.chunkBase || window.startTime;
|
|
656
|
+
return {
|
|
657
|
+
cursor: result.windowEnd,
|
|
658
|
+
nextChunkMinutes: MIN_CHUNK_MINUTES,
|
|
659
|
+
forcedAdvance: true,
|
|
660
|
+
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.`,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/*
|
|
664
|
+
* One diagnostic check per read pass. A pass a budget stopped, or never
|
|
665
|
+
* let start, is a warning naming the budget, never a success: a green
|
|
666
|
+
* step that did not read its window hid which data went unread.
|
|
667
|
+
*/
|
|
668
|
+
static recordPass(data) {
|
|
669
|
+
const outcome = data.outcome;
|
|
670
|
+
let status = "success";
|
|
671
|
+
let message = data.successMessage || data.countMessage;
|
|
672
|
+
if (outcome.stoppedBy === "time" && outcome.requests === 0) {
|
|
673
|
+
status = "warn";
|
|
674
|
+
message = "Not run: the poll time budget was spent.";
|
|
675
|
+
this.addWarning(data.result, `${data.name} was not run: the poll time budget was spent.`);
|
|
676
|
+
}
|
|
677
|
+
else if (outcome.stoppedBy) {
|
|
678
|
+
status = "warn";
|
|
679
|
+
const budgetName = outcome.stoppedBy === "time" ? "poll time budget" : "request budget";
|
|
680
|
+
const stop = `stopped by the ${budgetName} after ${outcome.requests} ${outcome.requests === 1 ? "request" : "requests"}`;
|
|
681
|
+
message =
|
|
682
|
+
outcome.requests === 0
|
|
683
|
+
? `Not run: ${stop}. The search passes share one page budget and it was spent before this pass started.`
|
|
684
|
+
: `${data.countMessage} The pass was ${stop}.`;
|
|
685
|
+
this.addWarning(data.result, `${data.name} was ${stop}.`);
|
|
686
|
+
}
|
|
687
|
+
else if (outcome.leftUnread) {
|
|
688
|
+
status = "warn";
|
|
689
|
+
message = `${data.countMessage} Google did not return part of the window.`;
|
|
690
|
+
}
|
|
691
|
+
data.result.checks.push({
|
|
692
|
+
name: data.name,
|
|
693
|
+
status,
|
|
694
|
+
durationMs: Date.now() - data.startedMs,
|
|
695
|
+
message,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
/*
|
|
699
|
+
* The alerts-view pass (legacyFetchAlertsView, detection time). The
|
|
700
|
+
* endpoint has no pagination, so a truncated window is split in half and
|
|
701
|
+
* both halves are re-read until they fit or the alerts-view budget runs
|
|
702
|
+
* out.
|
|
703
|
+
*/
|
|
704
|
+
static async fetchWindows(client, window, result, budget, seen) {
|
|
705
|
+
const outcome = {
|
|
706
|
+
returned: 0,
|
|
707
|
+
requests: 0,
|
|
708
|
+
stoppedBy: null,
|
|
709
|
+
leftUnread: false,
|
|
710
|
+
};
|
|
711
|
+
const pending = [
|
|
712
|
+
{ startTime: window.startTime, endTime: window.endTime },
|
|
713
|
+
];
|
|
714
|
+
let splits = 0;
|
|
375
715
|
while (pending.length > 0) {
|
|
376
|
-
|
|
377
|
-
|
|
716
|
+
const stop = this.budgetStop(budget, budget.alertsViewRequests, MAX_ALERTS_VIEW_REQUESTS_PER_POLL);
|
|
717
|
+
if (stop) {
|
|
718
|
+
outcome.stoppedBy = stop;
|
|
378
719
|
result.complete = false;
|
|
379
|
-
result.warnings.push("The recovery request limit was reached. Narrow the time range; the poll cursor has not advanced.");
|
|
380
720
|
break;
|
|
381
721
|
}
|
|
382
722
|
const current = pending.shift();
|
|
723
|
+
budget.alertsViewRequests++;
|
|
724
|
+
outcome.requests++;
|
|
383
725
|
result.requestCount++;
|
|
384
|
-
const fetched = await client.fetchDetectionAlerts(
|
|
726
|
+
const fetched = await client.fetchDetectionAlerts({
|
|
727
|
+
startTime: current.startTime,
|
|
728
|
+
endTime: current.endTime,
|
|
729
|
+
maxAlerts: result.type === "test" ? 1 : MAX_ALERTS_PER_REQUEST,
|
|
730
|
+
includeNonAlertingDetections: result.includeNonAlertingDetections,
|
|
731
|
+
});
|
|
732
|
+
outcome.returned += fetched.alerts.length;
|
|
385
733
|
for (const alert of fetched.alerts) {
|
|
386
|
-
|
|
387
|
-
try {
|
|
388
|
-
key = readString(alert, "id") || contentHashEventUid(alert);
|
|
389
|
-
}
|
|
390
|
-
catch (_a) {
|
|
391
|
-
/*
|
|
392
|
-
* Keep an uninspectable object in the normalization failure count
|
|
393
|
-
* without preventing valid detections in this response from importing.
|
|
394
|
-
*/
|
|
395
|
-
key = `malformed:${result.requestCount}:${seen.size}`;
|
|
396
|
-
}
|
|
397
|
-
seen.set(key, alert);
|
|
734
|
+
this.collect(seen, alert, result);
|
|
398
735
|
}
|
|
399
736
|
result.fetchedCount = seen.size;
|
|
400
737
|
const truncated = fetched.truncatedByCount ||
|
|
@@ -406,7 +743,7 @@ export default class GoogleSecOpsPoller {
|
|
|
406
743
|
* A one-record permission probe is deliberately limited; it makes no
|
|
407
744
|
* assertion about whether the full detection window can be imported.
|
|
408
745
|
*/
|
|
409
|
-
result.complete = fetched.complete;
|
|
746
|
+
result.complete = result.complete && fetched.complete;
|
|
410
747
|
if (truncated) {
|
|
411
748
|
result.warnings.push("Connection test reads at most one detection. Use Preview to inspect the full time range.");
|
|
412
749
|
}
|
|
@@ -418,17 +755,162 @@ export default class GoogleSecOpsPoller {
|
|
|
418
755
|
const midpoint = Math.floor((current.startTime.getTime() + current.endTime.getTime()) / 2);
|
|
419
756
|
if (truncated &&
|
|
420
757
|
current.endTime.getTime() - current.startTime.getTime() > 1000) {
|
|
421
|
-
|
|
758
|
+
splits++;
|
|
422
759
|
pending.unshift({ startTime: current.startTime, endTime: new Date(midpoint) }, { startTime: new Date(midpoint), endTime: current.endTime });
|
|
423
760
|
}
|
|
424
761
|
else if (!fetched.complete || truncated) {
|
|
425
762
|
result.complete = false;
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
763
|
+
outcome.leftUnread = true;
|
|
764
|
+
this.addWarning(result, truncated
|
|
765
|
+
? "Google still truncated a one-second window of the alerts view, so some alerts in it were not read."
|
|
766
|
+
: "Google ended an alerts view response without confirming it was complete.");
|
|
429
767
|
}
|
|
430
768
|
}
|
|
431
|
-
|
|
769
|
+
if (splits > 0) {
|
|
770
|
+
// One line rather than one per split: lastError joins every warning.
|
|
771
|
+
result.warnings.push(`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"}.`);
|
|
772
|
+
}
|
|
773
|
+
return outcome;
|
|
774
|
+
}
|
|
775
|
+
/*
|
|
776
|
+
* A detections-search pass (legacySearchDetections or the curated
|
|
777
|
+
* variant), one basis at a time, following nextPageToken under the page
|
|
778
|
+
* budget the two search passes share.
|
|
779
|
+
*/
|
|
780
|
+
static async searchPass(client, window, result, budget, seen, options) {
|
|
781
|
+
const label = options.curated
|
|
782
|
+
? "curated rule detections"
|
|
783
|
+
: "rule detections";
|
|
784
|
+
const outcome = {
|
|
785
|
+
returned: 0,
|
|
786
|
+
requests: 0,
|
|
787
|
+
stoppedBy: null,
|
|
788
|
+
leftUnread: false,
|
|
789
|
+
};
|
|
790
|
+
for (const listBasis of options.bases) {
|
|
791
|
+
let pageToken = undefined;
|
|
792
|
+
do {
|
|
793
|
+
const stop = this.budgetStop(budget, budget.searchRequests, MAX_SEARCH_PAGES_PER_POLL);
|
|
794
|
+
if (stop) {
|
|
795
|
+
outcome.stoppedBy = stop;
|
|
796
|
+
result.complete = false;
|
|
797
|
+
return outcome;
|
|
798
|
+
}
|
|
799
|
+
budget.searchRequests++;
|
|
800
|
+
outcome.requests++;
|
|
801
|
+
result.requestCount++;
|
|
802
|
+
const page = await client.searchDetections({
|
|
803
|
+
startTime: window.startTime,
|
|
804
|
+
endTime: window.endTime,
|
|
805
|
+
listBasis,
|
|
806
|
+
alertingOnly: !result.includeNonAlertingDetections,
|
|
807
|
+
pageSize: result.type === "test" ? 1 : MAX_SEARCH_PAGE_SIZE,
|
|
808
|
+
pageToken,
|
|
809
|
+
curated: options.curated,
|
|
810
|
+
});
|
|
811
|
+
outcome.returned += page.detections.length;
|
|
812
|
+
for (const detection of page.detections) {
|
|
813
|
+
this.collect(seen, detection, result);
|
|
814
|
+
}
|
|
815
|
+
result.fetchedCount = seen.size;
|
|
816
|
+
if (page.truncated) {
|
|
817
|
+
/*
|
|
818
|
+
* respTooLargeDetectionsTruncated: Google cut the page by byte
|
|
819
|
+
* size and the rest is not reachable through a page token, so the
|
|
820
|
+
* window is not fully read.
|
|
821
|
+
*/
|
|
822
|
+
result.complete = false;
|
|
823
|
+
outcome.leftUnread = true;
|
|
824
|
+
this.addWarning(result, `Google truncated a page of ${label} by size for ${window.startTime.toISOString()} to ${window.endTime.toISOString()}, so part of the window was not read.`);
|
|
825
|
+
}
|
|
826
|
+
if (result.type === "test") {
|
|
827
|
+
// One record per pass is all a permission probe needs.
|
|
828
|
+
return outcome;
|
|
829
|
+
}
|
|
830
|
+
pageToken = page.nextPageToken || undefined;
|
|
831
|
+
} while (pageToken);
|
|
832
|
+
}
|
|
833
|
+
return outcome;
|
|
834
|
+
}
|
|
835
|
+
/*
|
|
836
|
+
* Whether a pass may make another request: each kind of read has its own
|
|
837
|
+
* request count, and all of them share the poll's wall-clock bound so a
|
|
838
|
+
* slow endpoint cannot run the worker job into its timeout.
|
|
839
|
+
*/
|
|
840
|
+
static budgetStop(budget, used, limit) {
|
|
841
|
+
if (used >= limit) {
|
|
842
|
+
return "requests";
|
|
843
|
+
}
|
|
844
|
+
if (Date.now() - budget.startedMs >= MAX_FETCH_DURATION_MS) {
|
|
845
|
+
return "time";
|
|
846
|
+
}
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
// Repeated conditions (every one-second window, every page) are said once.
|
|
850
|
+
static addWarning(result, warning) {
|
|
851
|
+
if (!result.warnings.includes(warning)) {
|
|
852
|
+
result.warnings.push(warning);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
/*
|
|
856
|
+
* Union by Collection.id across passes and across the alerts view's
|
|
857
|
+
* chunks; a record with no id falls back to a content hash, and an
|
|
858
|
+
* object that cannot even be inspected is kept under a positional key so
|
|
859
|
+
* it is counted as rejected without hiding the valid records beside it.
|
|
860
|
+
*/
|
|
861
|
+
static collect(seen, record, result) {
|
|
862
|
+
let key;
|
|
863
|
+
try {
|
|
864
|
+
key = readString(record, "id") || contentHashEventUid(record);
|
|
865
|
+
}
|
|
866
|
+
catch (_a) {
|
|
867
|
+
key = `malformed:${result.requestCount}:${seen.size}`;
|
|
868
|
+
}
|
|
869
|
+
seen.set(key, record);
|
|
870
|
+
}
|
|
871
|
+
/*
|
|
872
|
+
* createdTime minus detectionTime over what was fetched. A lag beyond one
|
|
873
|
+
* poll interval is the record a detection-time cursor skips, so the
|
|
874
|
+
* warning names the reason polling reads created time.
|
|
875
|
+
*/
|
|
876
|
+
static measureCreationLag(alerts, connection, result) {
|
|
877
|
+
var _a, _b;
|
|
878
|
+
const intervalInMinutes = Math.max(1, connection.pollIntervalInMinutes || DEFAULT_POLL_INTERVAL_IN_MINUTES);
|
|
879
|
+
const thresholdInMinutes = intervalInMinutes + CREATION_LAG_GRACE_IN_MINUTES;
|
|
880
|
+
let measured = 0;
|
|
881
|
+
let lateCount = 0;
|
|
882
|
+
let maxLagMs = 0;
|
|
883
|
+
for (const alert of alerts) {
|
|
884
|
+
let detectionTime = null;
|
|
885
|
+
let createdTime = null;
|
|
886
|
+
try {
|
|
887
|
+
detectionTime = parseEventTime((_a = readValue(alert, "detectionTime")) !== null && _a !== void 0 ? _a : readValue(alert, "detection_time"));
|
|
888
|
+
createdTime = parseEventTime((_b = readValue(alert, "createdTime")) !== null && _b !== void 0 ? _b : readValue(alert, "created_time"));
|
|
889
|
+
}
|
|
890
|
+
catch (_c) {
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (!detectionTime || !createdTime) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
measured++;
|
|
897
|
+
const lagMs = createdTime.getTime() - detectionTime.getTime();
|
|
898
|
+
if (lagMs > maxLagMs) {
|
|
899
|
+
maxLagMs = lagMs;
|
|
900
|
+
}
|
|
901
|
+
if (lagMs > thresholdInMinutes * 60 * 1000) {
|
|
902
|
+
lateCount++;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
const lag = {
|
|
906
|
+
measured,
|
|
907
|
+
lateCount,
|
|
908
|
+
maxLagMinutes: Math.round(maxLagMs / 60000),
|
|
909
|
+
};
|
|
910
|
+
if (lateCount > 0) {
|
|
911
|
+
result.warnings.push(`${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.`);
|
|
912
|
+
}
|
|
913
|
+
return lag;
|
|
432
914
|
}
|
|
433
915
|
static sample(alert, event) {
|
|
434
916
|
var _a, _b, _c, _d;
|
|
@@ -450,42 +932,17 @@ export default class GoogleSecOpsPoller {
|
|
|
450
932
|
: {}));
|
|
451
933
|
}
|
|
452
934
|
/**
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
* source lock plus synchronous inserts make at least one replica contain
|
|
457
|
-
* every earlier acknowledged import before this query begins.
|
|
935
|
+
* Thin delegate kept as a static on this class: existing tests spy on it,
|
|
936
|
+
* and the query itself now lives in SecurityEventDedupe so the Google
|
|
937
|
+
* poller and the Security Event Connections poller cannot drift.
|
|
458
938
|
*/
|
|
459
939
|
static async findExistingEventUids(projectId, ids) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const statement = SQL `SELECT DISTINCT eventUid FROM clusterAllReplicas(
|
|
467
|
-
${{ type: TableColumnType.Text, value: getClickhouseClusterName() }},
|
|
468
|
-
${{ type: TableColumnType.Text, value: getClickhouseDatabaseName() }},
|
|
469
|
-
${{ type: TableColumnType.Text, value: getStorageTableName(SecurityEventService.model.tableName) }}
|
|
470
|
-
) WHERE projectId = ${{ type: TableColumnType.ObjectID, value: projectId }}
|
|
471
|
-
AND vendorName = ${{ type: TableColumnType.Text, value: "Google" }}
|
|
472
|
-
AND productName = ${{ type: TableColumnType.Text, value: SECOPS_SERVICE_NAME }}
|
|
473
|
-
AND eventUid IN ${{ type: TableColumnType.ArrayText, value: ids.slice(offset, offset + MAX_ALERTS_PER_REQUEST) }}`;
|
|
474
|
-
statement.append(getQuerySettings({
|
|
475
|
-
maxExecutionTimeInSeconds: 30,
|
|
476
|
-
timeoutOverflowMode: "throw",
|
|
477
|
-
boundScanMemory: true,
|
|
478
|
-
additionalSettings: { skip_unavailable_shards: 0 },
|
|
479
|
-
}));
|
|
480
|
-
const response = await SecurityEventService.executeQuery(statement);
|
|
481
|
-
const data = await response.json();
|
|
482
|
-
for (const row of data.data) {
|
|
483
|
-
if (typeof row["eventUid"] === "string") {
|
|
484
|
-
existing.add(row["eventUid"]);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
return existing;
|
|
940
|
+
return SecurityEventDedupe.findExistingEventUids({
|
|
941
|
+
projectId,
|
|
942
|
+
vendorName: SECOPS_VENDOR_NAME,
|
|
943
|
+
productName: SECOPS_SERVICE_NAME,
|
|
944
|
+
ids,
|
|
945
|
+
});
|
|
489
946
|
}
|
|
490
947
|
static async ingest(connection, normalized, result) {
|
|
491
948
|
if (!normalized.length) {
|