@oneuptime/common 13.0.7 → 14.0.0

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 (759) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  6. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  7. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  8. package/Scripts/generate-service-worker.js +65 -4
  9. package/Server/API/AIBillingAPI.ts +1 -0
  10. package/Server/API/AIInsightAPI.ts +1 -4
  11. package/Server/API/AIInvestigationAPI.ts +1 -4
  12. package/Server/API/AIReadinessAPI.ts +4 -5
  13. package/Server/API/AlertAPI.ts +1 -0
  14. package/Server/API/AutoRemediationAPI.ts +1 -4
  15. package/Server/API/BaseAPI.ts +34 -0
  16. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  17. package/Server/API/BillingAPI.ts +2 -0
  18. package/Server/API/BillingInvoiceAPI.ts +1 -0
  19. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  20. package/Server/API/CodeFixRunAPI.ts +1 -3
  21. package/Server/API/CommonAPI.ts +68 -4
  22. package/Server/API/DashboardAPI.ts +12 -12
  23. package/Server/API/GlobalConfigAPI.ts +243 -460
  24. package/Server/API/IncidentAPI.ts +2 -0
  25. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  26. package/Server/API/NotificationAPI.ts +1 -0
  27. package/Server/API/OnCallCalendarAPI.ts +4 -5
  28. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  29. package/Server/API/OnCallReadinessAPI.ts +4 -1
  30. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  31. package/Server/API/ProjectAPI.ts +1 -0
  32. package/Server/API/ProjectOIDC.ts +25 -16
  33. package/Server/API/ProjectSSO.ts +24 -16
  34. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  35. package/Server/API/StatusPageAPI.ts +261 -74
  36. package/Server/API/TeamMemberAPI.ts +13 -1
  37. package/Server/API/UserCallAPI.ts +2 -0
  38. package/Server/API/UserEmailAPI.ts +2 -0
  39. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  40. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  41. package/Server/API/UserSlackAPI.ts +1 -0
  42. package/Server/API/UserSmsAPI.ts +2 -0
  43. package/Server/API/UserTotpAuthAPI.ts +1 -0
  44. package/Server/API/UserWebAuthnAPI.ts +2 -0
  45. package/Server/API/UserWebhookAPI.ts +1 -0
  46. package/Server/API/UserWhatsAppAPI.ts +2 -0
  47. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  48. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  49. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  50. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  51. package/Server/EnvironmentConfig.ts +147 -2
  52. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  54. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  55. package/Server/Middleware/UserAuthorization.ts +117 -21
  56. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  57. package/Server/Services/AuditLogService.ts +84 -1079
  58. package/Server/Services/DatabaseService.ts +245 -0
  59. package/Server/Services/DetectionRuleService.ts +5 -4
  60. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  61. package/Server/Services/GlobalConfigService.ts +80 -1
  62. package/Server/Services/MonitorProbeService.ts +116 -20
  63. package/Server/Services/MonitorService.ts +56 -4
  64. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  65. package/Server/Services/MonitorTemplateService.ts +26 -0
  66. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  67. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  71. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  72. package/Server/Services/ProjectService.ts +302 -0
  73. package/Server/Services/RumSessionPinService.ts +293 -22
  74. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  75. package/Server/Services/StatusPageService.ts +78 -1
  76. package/Server/Services/TeamMemberService.ts +11 -0
  77. package/Server/Services/TeamPermissionService.ts +120 -26
  78. package/Server/Services/TeamService.ts +10 -0
  79. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  80. package/Server/Services/UserService.ts +10 -17
  81. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  82. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  83. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  84. package/Server/Types/Database/Permissions/Index.ts +75 -11
  85. package/Server/Types/Database/Permissions/TablePermission.ts +34 -31
  86. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  87. package/Server/Types/Markdown.ts +41 -0
  88. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  89. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  90. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  91. package/Server/Utils/EditionEnforcement.ts +233 -0
  92. package/Server/Utils/FrontendEnvironment.ts +41 -2
  93. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  94. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  95. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  96. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  97. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  98. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  99. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  100. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  101. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  102. package/Server/Utils/Realtime.ts +163 -45
  103. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  104. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  105. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  106. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  107. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  108. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  109. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  110. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  111. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  112. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  113. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  114. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  115. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  116. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  117. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  118. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  119. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  120. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  121. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  122. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  123. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  124. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  125. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  126. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  127. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  128. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  129. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  130. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  131. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  132. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  133. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  134. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  135. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  136. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  137. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  138. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  139. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  140. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +133 -70
  141. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  142. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  143. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  144. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -58
  145. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  146. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  147. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  148. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  149. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  150. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  151. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  152. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  153. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  154. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  155. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  156. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  157. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  158. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  159. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  160. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  161. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  162. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  163. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  164. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  165. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  166. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  167. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  168. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  169. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  170. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  171. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  172. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  173. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  174. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  175. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  176. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  177. package/Tests/Server/API/GlobalConfigLicense.test.ts +766 -381
  178. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  179. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  180. package/Tests/Server/API/OnCallReadinessAPI.test.ts +196 -912
  181. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  182. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  183. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  184. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  185. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  186. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  187. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  188. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  189. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  190. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  191. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  192. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  193. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  194. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  195. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  196. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  197. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  198. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  199. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  200. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  201. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  202. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  203. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  204. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  205. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  206. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  207. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  208. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  209. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  210. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  211. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  212. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  213. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  214. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  215. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  216. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  217. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  218. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  219. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  220. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  221. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  222. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  223. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  224. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  225. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  226. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  227. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  228. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  229. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  230. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  231. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  232. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  233. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  234. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  235. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  236. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  237. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  238. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  239. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  240. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  241. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  242. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  243. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  244. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  245. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  246. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  247. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  248. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  249. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  250. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  251. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  252. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  253. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  254. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  255. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  256. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  257. package/Tests/Server/Types/Markdown.test.ts +181 -0
  258. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  259. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  260. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  261. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  262. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  263. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  264. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  265. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  266. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  267. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  268. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  269. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  270. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  271. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  272. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  273. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  274. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  275. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  276. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  277. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  278. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  279. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  280. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  281. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  282. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  283. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  284. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  285. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  286. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  287. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  288. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  289. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  290. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  291. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  292. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  293. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  294. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  295. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  296. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  297. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  298. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  299. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  300. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  301. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  302. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  303. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  304. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  305. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  306. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +165 -171
  307. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  308. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  309. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  310. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  311. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  312. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  313. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  314. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  315. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  316. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  317. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  318. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  319. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  320. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  321. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  322. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  323. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  324. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  325. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  326. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  327. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  328. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  329. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  330. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  331. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  332. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  333. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  334. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  335. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  336. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  337. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  338. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  339. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  340. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  341. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  342. package/Types/API/Hostname.ts +63 -16
  343. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  344. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  345. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  346. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  347. package/Types/Database/CanonicalModelInstance.ts +102 -0
  348. package/Types/Database/TableColumn.ts +20 -6
  349. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  350. package/Types/IP/IPv6.ts +6 -1
  351. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  352. package/Types/Monitor/MonitorStep.ts +6 -1
  353. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  354. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  355. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  356. package/Types/Realtime/EventName.ts +11 -0
  357. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  358. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  359. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  360. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  361. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  362. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  363. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  364. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  365. package/UI/Components/EditionLabel/EditionLabel.tsx +808 -1195
  366. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  367. package/UI/Components/EventItem/EventItem.tsx +12 -1
  368. package/UI/Components/Forms/BasicForm.tsx +151 -126
  369. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  370. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  371. package/UI/Components/Forms/FormSummary.tsx +7 -0
  372. package/UI/Components/Forms/Types/Field.ts +10 -0
  373. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  374. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  375. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  376. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  377. package/UI/Components/ModelList/ModelList.tsx +11 -1
  378. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  379. package/UI/Components/ModelTable/TableView.tsx +1 -1
  380. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  381. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  382. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  383. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  384. package/UI/Utils/API/API.ts +173 -12
  385. package/UI/Utils/API/RequestOptions.ts +11 -0
  386. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  387. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  388. package/UI/Utils/Navigation.ts +9 -0
  389. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  390. package/UI/Utils/Realtime.ts +176 -30
  391. package/UI/esbuild-enterprise.js +263 -0
  392. package/Utils/API.ts +14 -5
  393. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  394. package/Utils/HostAddressUtil.ts +94 -0
  395. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  396. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  397. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  398. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  399. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  400. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  401. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  402. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  404. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  406. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  408. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  410. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  412. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  414. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  415. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  416. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  417. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  418. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  419. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  420. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  421. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  422. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  423. package/build/dist/Server/API/AlertAPI.js +1 -1
  424. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  425. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  426. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  427. package/build/dist/Server/API/BaseAPI.js +33 -0
  428. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  429. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  430. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  431. package/build/dist/Server/API/BillingAPI.js +2 -2
  432. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  433. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  434. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  435. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  436. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  437. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  438. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  439. package/build/dist/Server/API/CommonAPI.js +50 -5
  440. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  441. package/build/dist/Server/API/DashboardAPI.js +12 -12
  442. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  443. package/build/dist/Server/API/GlobalConfigAPI.js +158 -342
  444. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  445. package/build/dist/Server/API/IncidentAPI.js +2 -2
  446. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  447. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  448. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  449. package/build/dist/Server/API/NotificationAPI.js +1 -1
  450. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  451. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  452. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  453. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  454. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  455. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  456. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  457. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  458. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  459. package/build/dist/Server/API/ProjectAPI.js +1 -1
  460. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  461. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  462. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  463. package/build/dist/Server/API/ProjectSSO.js +24 -16
  464. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  465. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  466. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  467. package/build/dist/Server/API/StatusPageAPI.js +218 -76
  468. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  469. package/build/dist/Server/API/TeamMemberAPI.js +11 -4
  470. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  471. package/build/dist/Server/API/UserCallAPI.js +2 -2
  472. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  473. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  474. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  475. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  476. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  477. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  478. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  479. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  480. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  481. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  482. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  483. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  484. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  485. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  486. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  487. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  488. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  489. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  490. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  491. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  492. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  493. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  494. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  495. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  496. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  497. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  498. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  499. package/build/dist/Server/EnvironmentConfig.js +84 -2
  500. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  501. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  502. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  503. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  504. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  505. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  506. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  507. package/build/dist/Server/Middleware/UserAuthorization.js +94 -15
  508. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  509. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  510. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  511. package/build/dist/Server/Services/AuditLogService.js +71 -740
  512. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  513. package/build/dist/Server/Services/DatabaseService.js +171 -0
  514. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  515. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  516. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  517. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  518. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  519. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  520. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  521. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  522. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  523. package/build/dist/Server/Services/MonitorService.js +45 -4
  524. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  525. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  526. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  527. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  528. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  529. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  530. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  531. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  532. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  533. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  534. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  535. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  536. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  537. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  538. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  539. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  540. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  541. package/build/dist/Server/Services/ProjectService.js +231 -0
  542. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  543. package/build/dist/Server/Services/RumSessionPinService.js +198 -16
  544. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  545. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  546. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  547. package/build/dist/Server/Services/StatusPageService.js +71 -0
  548. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  549. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  550. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  551. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  552. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  553. package/build/dist/Server/Services/TeamService.js +9 -0
  554. package/build/dist/Server/Services/TeamService.js.map +1 -1
  555. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  556. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  557. package/build/dist/Server/Services/UserService.js +10 -16
  558. package/build/dist/Server/Services/UserService.js.map +1 -1
  559. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  560. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  561. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  562. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  563. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  564. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  565. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  566. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  567. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +24 -16
  568. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  569. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  570. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  571. package/build/dist/Server/Types/Markdown.js +31 -0
  572. package/build/dist/Server/Types/Markdown.js.map +1 -1
  573. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  574. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  575. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  576. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  577. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  578. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  579. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  580. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  581. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  582. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  583. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  584. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  585. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  586. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  587. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  588. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  589. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  590. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  591. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  592. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  593. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  594. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  595. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  596. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  597. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  598. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  599. package/build/dist/Server/Utils/Realtime.js +126 -37
  600. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  601. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  602. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  603. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  604. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  605. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  606. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  607. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  608. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  609. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  610. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  611. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  612. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  613. package/build/dist/Types/API/Hostname.js +59 -11
  614. package/build/dist/Types/API/Hostname.js.map +1 -1
  615. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  616. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  617. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  618. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  619. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  620. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  621. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  622. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  623. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  624. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  625. package/build/dist/Types/Database/TableColumn.js +19 -6
  626. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  627. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  628. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  629. package/build/dist/Types/IP/IPv6.js +6 -1
  630. package/build/dist/Types/IP/IPv6.js.map +1 -1
  631. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  632. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  633. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  634. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  635. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  636. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  637. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  638. package/build/dist/Types/Realtime/EventName.js +10 -0
  639. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  640. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  641. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  642. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  643. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  644. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  645. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  646. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  647. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  648. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  649. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  650. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  651. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  652. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  653. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  654. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  655. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  656. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +425 -585
  657. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  658. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  659. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  660. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  661. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  662. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  663. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  664. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  665. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  666. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  667. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  668. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  669. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  670. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  671. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  672. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  673. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  674. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  675. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  676. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  677. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  678. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  679. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  680. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  681. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  682. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  683. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  684. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  685. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  686. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  687. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  688. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  689. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  690. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  691. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  692. package/build/dist/UI/Utils/API/API.js +143 -19
  693. package/build/dist/UI/Utils/API/API.js.map +1 -1
  694. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  695. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  696. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  697. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  698. package/build/dist/UI/Utils/Navigation.js +8 -0
  699. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  700. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  701. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  702. package/build/dist/UI/Utils/Realtime.js +120 -15
  703. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  704. package/build/dist/Utils/API.js +13 -5
  705. package/build/dist/Utils/API.js.map +1 -1
  706. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  707. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  708. package/build/dist/Utils/HostAddressUtil.js +80 -0
  709. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  710. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  711. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  712. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  713. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  714. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  715. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  716. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  717. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  718. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  719. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  720. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  721. package/jest.config.json +3 -1
  722. package/package.json +1 -1
  723. package/test-setup.sh +103 -34
  724. package/tsconfig.json +13 -0
  725. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  726. package/Server/API/TeamComplianceAPI.ts +0 -158
  727. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  728. package/Server/Services/TeamComplianceService.ts +0 -733
  729. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  730. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  731. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  732. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  733. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  734. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  735. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  736. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  737. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  738. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  739. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  740. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  741. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  742. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  743. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  744. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  745. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  746. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  747. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  748. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  749. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  750. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  751. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  752. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  753. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  754. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  755. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  756. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  757. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  758. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  759. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,1203 @@
1
+ /** @timezone UTC */
2
+
3
+ import "@testing-library/jest-dom";
4
+ import {
5
+ afterEach,
6
+ beforeEach,
7
+ describe,
8
+ expect,
9
+ jest,
10
+ test,
11
+ } from "@jest/globals";
12
+ import {
13
+ act,
14
+ cleanup,
15
+ configure,
16
+ fireEvent,
17
+ render,
18
+ screen,
19
+ waitFor,
20
+ within,
21
+ } from "@testing-library/react";
22
+ import React, { ReactElement } from "react";
23
+ import { MemoryRouter } from "react-router-dom";
24
+ import { MockFunction } from "../../MockType";
25
+
26
+ configure({ asyncUtilTimeout: 15000 });
27
+
28
+ /*
29
+ * The Security Events explorer.
30
+ *
31
+ * It is the same shell the traces, metrics and exceptions explorers render —
32
+ * search bar, facet sidebar, severity-stacked volume histogram, dense rows, a
33
+ * detail drawer and pagination — over the SecurityEvent ClickHouse table,
34
+ * replacing the model table this page used to be.
35
+ *
36
+ * What is pinned here is the wiring that only shows once the pieces are
37
+ * together: that the list, the histogram and EVERY facet count are built from
38
+ * one query (so the chart never counts events the list would not show, and a
39
+ * facet count never promises rows a click cannot reach), that every way of
40
+ * narrowing writes itself into the URL and is read back from it, and that an
41
+ * empty list tells "nothing in this window" apart from "nothing ever".
42
+ *
43
+ * The data layer is stubbed at AnalyticsModelAPI / ModelAPI; the shell itself
44
+ * is real, so the assertions are about what a reader sees.
45
+ */
46
+
47
+ interface CapturedPickerProps {
48
+ value: { range: string; startAndEndDate?: unknown };
49
+ onChange: (value: unknown) => void;
50
+ }
51
+
52
+ let pickerProps: CapturedPickerProps | null = null;
53
+
54
+ jest.mock(
55
+ "../../../UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker",
56
+ () => {
57
+ const react: typeof React = jest.requireActual("react") as typeof React;
58
+
59
+ return {
60
+ __esModule: true,
61
+ default: (props: CapturedPickerProps): ReactElement => {
62
+ pickerProps = props;
63
+ return react.createElement(
64
+ "div",
65
+ { "data-testid": "time-range-picker" },
66
+ props.value.range,
67
+ );
68
+ },
69
+ };
70
+ },
71
+ );
72
+
73
+ // One div per bucket, so a drag across the histogram can be driven.
74
+ jest.mock("recharts", () => {
75
+ const react: typeof React = jest.requireActual("react") as typeof React;
76
+
77
+ interface StubRow {
78
+ time: string;
79
+ }
80
+
81
+ interface StubChartProps {
82
+ data: Array<StubRow>;
83
+ children?: React.ReactNode;
84
+ onMouseDown?: (state: { activeLabel: string }) => void;
85
+ onMouseMove?: (state: { activeLabel: string }) => void;
86
+ onMouseUp?: (state: { activeLabel: string }) => void;
87
+ }
88
+
89
+ return {
90
+ __esModule: true,
91
+ ResponsiveContainer: (props: { children: React.ReactNode }) => {
92
+ return react.createElement("div", null, props.children);
93
+ },
94
+ BarChart: (props: StubChartProps) => {
95
+ return react.createElement(
96
+ "div",
97
+ { "data-testid": "bar-chart" },
98
+ props.data.map((row: StubRow) => {
99
+ return react.createElement("div", {
100
+ key: row.time,
101
+ "data-testid": `bucket-${row.time}`,
102
+ onMouseDown: () => {
103
+ props.onMouseDown?.({ activeLabel: row.time });
104
+ },
105
+ onMouseMove: () => {
106
+ props.onMouseMove?.({ activeLabel: row.time });
107
+ },
108
+ onMouseUp: () => {
109
+ props.onMouseUp?.({ activeLabel: row.time });
110
+ },
111
+ });
112
+ }),
113
+ props.children,
114
+ );
115
+ },
116
+ Bar: () => {
117
+ return null;
118
+ },
119
+ XAxis: () => {
120
+ return null;
121
+ },
122
+ YAxis: () => {
123
+ return null;
124
+ },
125
+ Tooltip: () => {
126
+ return null;
127
+ },
128
+ ReferenceArea: () => {
129
+ return null;
130
+ },
131
+ };
132
+ });
133
+
134
+ import SecurityEventsViewer, {
135
+ SECURITY_EVENTS_VOLUME_TOTAL_TEST_ID,
136
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventsViewer";
137
+ import SecurityEventAttributeUtil from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventAttributeUtil";
138
+ import { SECURITY_EVENT_ROW_TEST_ID } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventListRow";
139
+ import { SECURITY_EVENT_DETAIL_PANEL_TEST_ID } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventDetailPanel";
140
+ import { SECURITY_EVENTS_EMPTY_STATE_ID } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventsEmptyState";
141
+ import { SECURITY_EVENTS_NO_RESULTS_ID } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventsNoResults";
142
+ import {
143
+ SECURITY_EVENT_FACET_KEYS,
144
+ SECURITY_EVENT_SOURCE_FACET_KEY,
145
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventsFacets";
146
+ import AnalyticsModelAPI from "../../../UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
147
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
148
+ import ProjectUtil from "../../../UI/Utils/Project";
149
+ import AggregateBy from "../../../Types/BaseDatabase/AggregateBy";
150
+ import AggregatedResult from "../../../Types/BaseDatabase/AggregatedResult";
151
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
152
+ import Includes from "../../../Types/BaseDatabase/Includes";
153
+ import NotEqual from "../../../Types/BaseDatabase/NotEqual";
154
+ import ListResult from "../../../Types/BaseDatabase/ListResult";
155
+ import Search from "../../../Types/BaseDatabase/Search";
156
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
157
+ import OneUptimeDate from "../../../Types/Date";
158
+ import ObjectID from "../../../Types/ObjectID";
159
+ import OcsfSeverity from "../../../Types/SecurityEvent/OcsfSeverity";
160
+ import TimeRange from "../../../Types/Time/TimeRange";
161
+ import SecurityEvent from "../../../Models/AnalyticsModels/SecurityEvent";
162
+ import Service from "../../../Models/DatabaseModels/Service";
163
+
164
+ const PROJECT_ID: ObjectID = new ObjectID(
165
+ "11111111-1111-4111-8111-111111111111",
166
+ );
167
+ const SOURCE_ID: ObjectID = new ObjectID(
168
+ "22222222-2222-4222-8222-222222222222",
169
+ );
170
+
171
+ const NOW: Date = new Date("2026-09-18T12:00:00.000Z");
172
+ const DAY_MS: number = 24 * 60 * 60 * 1000;
173
+
174
+ let now: Date = NOW;
175
+
176
+ let aggregateMock: MockFunction;
177
+ let getListMock: MockFunction;
178
+ let modelListMock: MockFunction;
179
+ let attributeKeysMock: MockFunction;
180
+
181
+ function event(fields: Partial<SecurityEvent> = {}): SecurityEvent {
182
+ return Object.assign(new SecurityEvent(), {
183
+ time: new Date("2026-09-18T10:00:00.000Z"),
184
+ eventUid: "uid-1",
185
+ severityName: OcsfSeverity.Critical,
186
+ className: "Authentication",
187
+ message: "Failed logon for alice",
188
+ principalUser: "alice",
189
+ ...fields,
190
+ });
191
+ }
192
+
193
+ const EVENTS: Array<SecurityEvent> = [
194
+ event(),
195
+ event({
196
+ eventUid: "uid-2",
197
+ severityName: OcsfSeverity.Low,
198
+ className: "DNS Activity",
199
+ message: "Resolved internal.example.com",
200
+ principalUser: "bob",
201
+ }),
202
+ ];
203
+
204
+ /*
205
+ * The aggregate stub answers facet GROUP BYs with values keyed by the facet
206
+ * column, and the volume aggregate with time-bucketed severity counts. Which
207
+ * one it is asked for is read off the aggregateBy, exactly as the server
208
+ * would.
209
+ */
210
+ function stubAggregates(): void {
211
+ aggregateMock.mockImplementation(
212
+ async (data: {
213
+ aggregateBy: AggregateBy<SecurityEvent>;
214
+ }): Promise<AggregatedResult> => {
215
+ const groupBy: Record<string, boolean> =
216
+ (data.aggregateBy.groupBy as unknown as Record<string, boolean>) || {};
217
+ const facetKey: string | undefined = Object.keys(groupBy)[0];
218
+
219
+ if (data.aggregateBy.aggregationInterval === "Total") {
220
+ if (facetKey === "severityName") {
221
+ return {
222
+ data: [
223
+ { timestamp: NOW, value: 4, severityName: OcsfSeverity.Critical },
224
+ { timestamp: NOW, value: 9, severityName: OcsfSeverity.Low },
225
+ ],
226
+ } as unknown as AggregatedResult;
227
+ }
228
+
229
+ if (facetKey === SECURITY_EVENT_SOURCE_FACET_KEY) {
230
+ return {
231
+ data: [
232
+ {
233
+ timestamp: NOW,
234
+ value: 13,
235
+ [SECURITY_EVENT_SOURCE_FACET_KEY]: SOURCE_ID.toString(),
236
+ },
237
+ ],
238
+ } as unknown as AggregatedResult;
239
+ }
240
+
241
+ return { data: [] } as unknown as AggregatedResult;
242
+ }
243
+
244
+ return {
245
+ data: [
246
+ {
247
+ timestamp: "2026-09-18T10:00:00.000Z",
248
+ value: 4,
249
+ severityName: OcsfSeverity.Critical,
250
+ },
251
+ {
252
+ timestamp: "2026-09-18T10:15:00.000Z",
253
+ value: 9,
254
+ severityName: OcsfSeverity.Low,
255
+ },
256
+ ],
257
+ } as unknown as AggregatedResult;
258
+ },
259
+ );
260
+ }
261
+
262
+ function stubList(
263
+ events: Array<SecurityEvent> = EVENTS,
264
+ count: number = events.length,
265
+ ): void {
266
+ getListMock.mockImplementation(
267
+ async (data: { limit: number }): Promise<ListResult<SecurityEvent>> => {
268
+ /*
269
+ * The "has this project EVER had an event" probe asks for one row with
270
+ * only `time` selected; every other call is the list.
271
+ */
272
+ if (data.limit === 1) {
273
+ return {
274
+ data: events.length > 0 ? [events[0]!] : [],
275
+ count: events.length,
276
+ skip: 0,
277
+ limit: 1,
278
+ };
279
+ }
280
+
281
+ /*
282
+ * Fresh objects every call, as the real API hands back: a viewer that
283
+ * tracked the open row by reference would lose its highlight on the
284
+ * first refresh.
285
+ */
286
+ return {
287
+ data: events.map((listed: SecurityEvent): SecurityEvent => {
288
+ return Object.assign(new SecurityEvent(), listed);
289
+ }),
290
+ count,
291
+ skip: 0,
292
+ limit: data.limit,
293
+ };
294
+ },
295
+ );
296
+ }
297
+
298
+ function listCalls(): Array<Record<string, unknown>> {
299
+ return getListMock.mock.calls
300
+ .map((call: Array<unknown>): Record<string, unknown> => {
301
+ return call[0] as Record<string, unknown>;
302
+ })
303
+ .filter((call: Record<string, unknown>): boolean => {
304
+ return call["limit"] !== 1;
305
+ });
306
+ }
307
+
308
+ function lastListCall(): Record<string, unknown> {
309
+ const calls: Array<Record<string, unknown>> = listCalls();
310
+ return calls[calls.length - 1]!;
311
+ }
312
+
313
+ function listQuery(): Record<string, unknown> {
314
+ return lastListCall()["query"] as Record<string, unknown>;
315
+ }
316
+
317
+ function aggregateCalls(): Array<AggregateBy<SecurityEvent>> {
318
+ return aggregateMock.mock.calls.map(
319
+ (call: Array<unknown>): AggregateBy<SecurityEvent> => {
320
+ return (call[0] as { aggregateBy: AggregateBy<SecurityEvent> })
321
+ .aggregateBy;
322
+ },
323
+ );
324
+ }
325
+
326
+ function facetAggregates(): Array<AggregateBy<SecurityEvent>> {
327
+ return aggregateCalls().filter(
328
+ (aggregate: AggregateBy<SecurityEvent>): boolean => {
329
+ return String(aggregate.aggregationInterval) === "Total";
330
+ },
331
+ );
332
+ }
333
+
334
+ function volumeAggregates(): Array<AggregateBy<SecurityEvent>> {
335
+ return aggregateCalls().filter(
336
+ (aggregate: AggregateBy<SecurityEvent>): boolean => {
337
+ return String(aggregate.aggregationInterval) !== "Total";
338
+ },
339
+ );
340
+ }
341
+
342
+ function urlParams(): URLSearchParams {
343
+ return new URLSearchParams(window.location.search);
344
+ }
345
+
346
+ function renderViewer(): void {
347
+ render(
348
+ <MemoryRouter>
349
+ <SecurityEventsViewer />
350
+ </MemoryRouter>,
351
+ );
352
+ }
353
+
354
+ async function waitForLoad(): Promise<void> {
355
+ await waitFor(() => {
356
+ expect(listCalls().length).toBeGreaterThan(0);
357
+ });
358
+ await waitFor(() => {
359
+ expect(facetAggregates().length).toBe(SECURITY_EVENT_FACET_KEYS.length);
360
+ });
361
+ // The volume total stops saying "Counting..." once the chart has its data.
362
+ await waitFor(() => {
363
+ expect(
364
+ screen.getByTestId(SECURITY_EVENTS_VOLUME_TOTAL_TEST_ID),
365
+ ).not.toHaveTextContent("Counting");
366
+ });
367
+ // And the list has either rows or one of its two empty states.
368
+ await waitFor(() => {
369
+ expect(
370
+ rows().length > 0 ||
371
+ document.getElementById(SECURITY_EVENTS_EMPTY_STATE_ID) !== null ||
372
+ document.getElementById(SECURITY_EVENTS_NO_RESULTS_ID) !== null,
373
+ ).toBe(true);
374
+ });
375
+ }
376
+
377
+ function rows(): Array<HTMLElement> {
378
+ return screen.queryAllByTestId(SECURITY_EVENT_ROW_TEST_ID);
379
+ }
380
+
381
+ function searchInput(): HTMLInputElement {
382
+ return screen.getByPlaceholderText(
383
+ /Search security events/i,
384
+ ) as HTMLInputElement;
385
+ }
386
+
387
+ async function submitSearch(text: string): Promise<void> {
388
+ fireEvent.change(searchInput(), { target: { value: text } });
389
+ fireEvent.keyDown(searchInput(), { key: "Enter", code: "Enter" });
390
+
391
+ await waitFor(() => {
392
+ expect(String((listQuery()["message"] as Search<string>) ?? "")).toContain(
393
+ "",
394
+ );
395
+ });
396
+ }
397
+
398
+ /*
399
+ * A facet section by its header title. Sections whose facet has no values in
400
+ * the window fold away entirely (hideWhenEmpty), so asking for one is also an
401
+ * assertion that it has something to show.
402
+ */
403
+ function facetSection(title: string): HTMLElement {
404
+ const label: Element | undefined = Array.from(
405
+ document.querySelectorAll("span.uppercase"),
406
+ ).find((node: Element): boolean => {
407
+ return (node.textContent || "").trim() === title;
408
+ });
409
+
410
+ if (!label) {
411
+ throw new Error(
412
+ `No facet section titled "${title}". Sections rendered: ${Array.from(
413
+ document.querySelectorAll("span.uppercase"),
414
+ )
415
+ .map((node: Element): string => {
416
+ return (node.textContent || "").trim();
417
+ })
418
+ .join(", ")}`,
419
+ );
420
+ }
421
+
422
+ const section: HTMLElement | null = label.closest("div.border-b");
423
+
424
+ if (!section) {
425
+ throw new Error(`Facet section "${title}" has no container.`);
426
+ }
427
+
428
+ return section as HTMLElement;
429
+ }
430
+
431
+ beforeEach(() => {
432
+ pickerProps = null;
433
+ now = NOW;
434
+ window.history.replaceState(null, "", "/dashboard/project/security-events");
435
+
436
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(PROJECT_ID);
437
+ jest.spyOn(OneUptimeDate, "getCurrentDate").mockImplementation(() => {
438
+ return new Date(now.getTime());
439
+ });
440
+
441
+ aggregateMock = jest.spyOn(
442
+ AnalyticsModelAPI,
443
+ "aggregate",
444
+ ) as unknown as MockFunction;
445
+ getListMock = jest.spyOn(
446
+ AnalyticsModelAPI,
447
+ "getList",
448
+ ) as unknown as MockFunction;
449
+ modelListMock = jest.spyOn(ModelAPI, "getList") as unknown as MockFunction;
450
+ attributeKeysMock = jest.spyOn(
451
+ SecurityEventAttributeUtil,
452
+ "getAttributeKeys",
453
+ ) as unknown as MockFunction;
454
+
455
+ stubAggregates();
456
+ stubList();
457
+
458
+ modelListMock.mockImplementation(async (): Promise<ListResult<Service>> => {
459
+ const service: Service = new Service();
460
+ service.id = SOURCE_ID;
461
+ service.name = "Google SecOps";
462
+
463
+ return { data: [service], count: 1, skip: 0, limit: 100 };
464
+ });
465
+
466
+ attributeKeysMock.mockResolvedValue([
467
+ "threat.matched",
468
+ "device.hostname",
469
+ ] as never);
470
+
471
+ jest
472
+ .spyOn(SecurityEventAttributeUtil, "getAttributeValues")
473
+ .mockResolvedValue([] as never);
474
+ });
475
+
476
+ afterEach(() => {
477
+ cleanup();
478
+ jest.restoreAllMocks();
479
+ });
480
+
481
+ describe("the explorer renders the shared telemetry shell", () => {
482
+ test("a search bar, a time picker, a histogram, a facet sidebar, rows and pagination", async () => {
483
+ renderViewer();
484
+ await waitForLoad();
485
+
486
+ expect(searchInput()).toBeInTheDocument();
487
+ expect(screen.getByTestId("time-range-picker")).toBeInTheDocument();
488
+ expect(screen.getByTestId("bar-chart")).toBeInTheDocument();
489
+ expect(screen.getByTestId("telemetry-pagination")).toBeInTheDocument();
490
+ expect(rows()).toHaveLength(2);
491
+ expect(rows()[0]).toHaveTextContent("Failed logon for alice");
492
+ });
493
+
494
+ test("the list is newest first, and carries every field the drawer needs", async () => {
495
+ renderViewer();
496
+ await waitForLoad();
497
+
498
+ expect(lastListCall()["sort"]).toEqual({ time: SortOrder.Descending });
499
+
500
+ const select: Record<string, boolean> = lastListCall()["select"] as Record<
501
+ string,
502
+ boolean
503
+ >;
504
+
505
+ for (const field of [
506
+ "attributes",
507
+ "observables",
508
+ "mitreTactics",
509
+ "targetPort",
510
+ "principalProcess",
511
+ "eventUid",
512
+ ]) {
513
+ expect(select[field]).toBe(true);
514
+ }
515
+ });
516
+
517
+ test("opens on the past day, scoped to the current project", async () => {
518
+ renderViewer();
519
+ await waitForLoad();
520
+
521
+ expect(listQuery()["projectId"]).toBe(PROJECT_ID);
522
+
523
+ const window_: InBetween<Date> = listQuery()["time"] as InBetween<Date>;
524
+ expect(window_).toBeInstanceOf(InBetween);
525
+ expect(new Date(window_.endValue).toISOString()).toBe(NOW.toISOString());
526
+ expect(new Date(window_.startValue).toISOString()).toBe(
527
+ new Date(NOW.getTime() - DAY_MS).toISOString(),
528
+ );
529
+ });
530
+
531
+ test("the volume total is reported beside the chart", async () => {
532
+ renderViewer();
533
+ await waitForLoad();
534
+
535
+ await waitFor(() => {
536
+ expect(
537
+ screen.getByTestId(SECURITY_EVENTS_VOLUME_TOTAL_TEST_ID),
538
+ ).toHaveTextContent("13 events");
539
+ });
540
+ });
541
+ });
542
+
543
+ describe("one query behind everything", () => {
544
+ test("the histogram and every facet count exactly the rows the list shows", async () => {
545
+ renderViewer();
546
+ await waitForLoad();
547
+
548
+ const expected: Record<string, unknown> = listQuery();
549
+
550
+ expect(volumeAggregates().length).toBeGreaterThan(0);
551
+
552
+ for (const aggregate of [...volumeAggregates(), ...facetAggregates()]) {
553
+ expect(aggregate.query).toEqual(expected);
554
+ }
555
+ });
556
+
557
+ test("one aggregate per facet, each grouped by its own column", async () => {
558
+ renderViewer();
559
+ await waitForLoad();
560
+
561
+ expect(
562
+ facetAggregates().map((aggregate: AggregateBy<SecurityEvent>): string => {
563
+ return Object.keys(
564
+ aggregate.groupBy as unknown as Record<string, boolean>,
565
+ )[0]!;
566
+ }),
567
+ ).toEqual([...SECURITY_EVENT_FACET_KEYS]);
568
+ });
569
+
570
+ test("a facet that errors leaves the others intact", async () => {
571
+ aggregateMock.mockImplementation(
572
+ async (data: {
573
+ aggregateBy: AggregateBy<SecurityEvent>;
574
+ }): Promise<AggregatedResult> => {
575
+ const facetKey: string | undefined = Object.keys(
576
+ (data.aggregateBy.groupBy as unknown as Record<string, boolean>) ||
577
+ {},
578
+ )[0];
579
+
580
+ if (facetKey === "className") {
581
+ throw new Error("boom");
582
+ }
583
+
584
+ if (
585
+ facetKey === "severityName" &&
586
+ String(data.aggregateBy.aggregationInterval) === "Total"
587
+ ) {
588
+ return {
589
+ data: [
590
+ { timestamp: NOW, value: 4, severityName: OcsfSeverity.Critical },
591
+ ],
592
+ } as unknown as AggregatedResult;
593
+ }
594
+
595
+ return { data: [] } as unknown as AggregatedResult;
596
+ },
597
+ );
598
+
599
+ renderViewer();
600
+ await waitForLoad();
601
+
602
+ await waitFor(() => {
603
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
604
+ });
605
+ });
606
+
607
+ /*
608
+ * A failed count draws no bars rather than an error banner over a list that
609
+ * loaded fine — the chart is a second read of rows the reader can already
610
+ * see.
611
+ */
612
+ test("a failed histogram does not take the list down with it", async () => {
613
+ aggregateMock.mockRejectedValue(new Error("boom") as never);
614
+
615
+ renderViewer();
616
+
617
+ await waitFor(() => {
618
+ expect(rows()).toHaveLength(2);
619
+ });
620
+
621
+ expect(screen.queryByText(/boom/)).toBeNull();
622
+ });
623
+
624
+ test("a failed list is reported, and the rows are cleared", async () => {
625
+ getListMock.mockRejectedValue(new Error("clickhouse is down") as never);
626
+
627
+ renderViewer();
628
+
629
+ await waitFor(() => {
630
+ expect(screen.getByText(/clickhouse is down/)).toBeInTheDocument();
631
+ });
632
+
633
+ expect(rows()).toHaveLength(0);
634
+ });
635
+ });
636
+
637
+ describe("the facet sidebar", () => {
638
+ test("lists the counted values, Source named from Postgres rather than by id", async () => {
639
+ renderViewer();
640
+ await waitForLoad();
641
+
642
+ await waitFor(() => {
643
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
644
+ });
645
+
646
+ expect(facetSection("Severity")).toHaveTextContent("Low");
647
+ expect(facetSection("Source")).toHaveTextContent("Google SecOps");
648
+ expect(facetSection("Source")).not.toHaveTextContent(SOURCE_ID.toString());
649
+ });
650
+
651
+ test("clicking a value narrows the list, the chart and the counts alike", async () => {
652
+ renderViewer();
653
+ await waitForLoad();
654
+
655
+ await waitFor(() => {
656
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
657
+ });
658
+
659
+ fireEvent.click(
660
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
661
+ );
662
+
663
+ await waitFor(() => {
664
+ expect(listQuery()["severityName"]).toBe(OcsfSeverity.Critical);
665
+ });
666
+
667
+ const expected: Record<string, unknown> = listQuery();
668
+
669
+ for (const aggregate of [
670
+ ...volumeAggregates().slice(-1),
671
+ ...facetAggregates().slice(-SECURITY_EVENT_FACET_KEYS.length),
672
+ ]) {
673
+ expect(aggregate.query).toEqual(expected);
674
+ }
675
+
676
+ expect(JSON.parse(urlParams().get("filters") as string)).toEqual([
677
+ ["severityName", OcsfSeverity.Critical],
678
+ ]);
679
+ });
680
+
681
+ test("a second value on the same facet is an IN, and a second click clears it", async () => {
682
+ renderViewer();
683
+ await waitForLoad();
684
+
685
+ await waitFor(() => {
686
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
687
+ });
688
+
689
+ fireEvent.click(
690
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
691
+ );
692
+ await waitFor(() => {
693
+ expect(listQuery()["severityName"]).toBe(OcsfSeverity.Critical);
694
+ });
695
+
696
+ fireEvent.click(
697
+ within(facetSection("Severity")).getByText(OcsfSeverity.Low),
698
+ );
699
+ await waitFor(() => {
700
+ expect(listQuery()["severityName"]).toBeInstanceOf(Includes);
701
+ });
702
+
703
+ fireEvent.click(
704
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
705
+ );
706
+ await waitFor(() => {
707
+ expect(listQuery()["severityName"]).toBe(OcsfSeverity.Low);
708
+ });
709
+ });
710
+
711
+ /*
712
+ * The sidebar has always drawn an exclude action beside every value. This
713
+ * pins that it does something.
714
+ */
715
+ test("excluding a value compiles to a not-equal, and says so on the chip", async () => {
716
+ renderViewer();
717
+ await waitForLoad();
718
+
719
+ await waitFor(() => {
720
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
721
+ });
722
+
723
+ fireEvent.click(
724
+ within(facetSection("Severity")).getByRole("button", {
725
+ name: `Exclude ${OcsfSeverity.Low}`,
726
+ }),
727
+ );
728
+
729
+ await waitFor(() => {
730
+ expect(listQuery()["severityName"]).toBeInstanceOf(NotEqual);
731
+ });
732
+
733
+ expect(screen.getByText(/Severity is not/)).toBeInTheDocument();
734
+ expect(JSON.parse(urlParams().get("filters") as string)).toEqual([
735
+ [`!severityName`, OcsfSeverity.Low],
736
+ ]);
737
+ });
738
+ });
739
+
740
+ describe("the search bar", () => {
741
+ test("free text searches the message and lands in the URL", async () => {
742
+ renderViewer();
743
+ await waitForLoad();
744
+
745
+ await submitSearch("failed logon");
746
+
747
+ await waitFor(() => {
748
+ expect(listQuery()["message"]).toBeInstanceOf(Search);
749
+ });
750
+
751
+ expect(String(listQuery()["message"])).toContain("failed logon");
752
+ expect(urlParams().get("search")).toBe("failed logon");
753
+ });
754
+
755
+ test("a field filter reaches its column", async () => {
756
+ renderViewer();
757
+ await waitForLoad();
758
+
759
+ await submitSearch("severity:Critical");
760
+
761
+ await waitFor(() => {
762
+ expect(listQuery()["severityName"]).toBe("Critical");
763
+ });
764
+ });
765
+
766
+ test("an attribute filter reaches the attributes map", async () => {
767
+ renderViewer();
768
+ await waitForLoad();
769
+
770
+ await submitSearch("@threat.matched:true");
771
+
772
+ await waitFor(() => {
773
+ expect(listQuery()["attributes"]).toEqual({ "threat.matched": "true" });
774
+ });
775
+ });
776
+
777
+ test("typing alone changes nothing until it is submitted", async () => {
778
+ renderViewer();
779
+ await waitForLoad();
780
+
781
+ const before: number = listCalls().length;
782
+
783
+ fireEvent.change(searchInput(), { target: { value: "severity:High" } });
784
+
785
+ expect(listCalls()).toHaveLength(before);
786
+ expect(listQuery()["severityName"]).toBeUndefined();
787
+ });
788
+
789
+ test("the attribute keys of this project back the bar's completion", async () => {
790
+ renderViewer();
791
+ await waitForLoad();
792
+
793
+ await waitFor(() => {
794
+ expect(attributeKeysMock).toHaveBeenCalled();
795
+ });
796
+ });
797
+ });
798
+
799
+ describe("chips", () => {
800
+ test("clearing one widens the query and drops it from the URL", async () => {
801
+ renderViewer();
802
+ await waitForLoad();
803
+
804
+ await waitFor(() => {
805
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
806
+ });
807
+
808
+ fireEvent.click(
809
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
810
+ );
811
+ await waitFor(() => {
812
+ expect(listQuery()["severityName"]).toBe(OcsfSeverity.Critical);
813
+ });
814
+
815
+ fireEvent.click(screen.getByRole("button", { name: /Remove .*Critical/i }));
816
+
817
+ await waitFor(() => {
818
+ expect(listQuery()["severityName"]).toBeUndefined();
819
+ });
820
+
821
+ expect(urlParams().get("filters")).toBeNull();
822
+ });
823
+
824
+ test("Clear all drops the chips AND the search text", async () => {
825
+ renderViewer();
826
+ await waitForLoad();
827
+
828
+ await submitSearch("failed logon");
829
+
830
+ await waitFor(() => {
831
+ expect(listQuery()["message"]).toBeInstanceOf(Search);
832
+ });
833
+
834
+ await waitFor(() => {
835
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
836
+ });
837
+
838
+ // Two chips, which is when the shell offers "Clear all" at all.
839
+ fireEvent.click(
840
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
841
+ );
842
+ fireEvent.click(within(facetSection("Source")).getByText("Google SecOps"));
843
+
844
+ await waitFor(() => {
845
+ expect(listQuery()[SECURITY_EVENT_SOURCE_FACET_KEY]).toBe(
846
+ SOURCE_ID.toString(),
847
+ );
848
+ });
849
+
850
+ fireEvent.click(screen.getByText(/Clear all/i));
851
+
852
+ await waitFor(() => {
853
+ expect(listQuery()[SECURITY_EVENT_SOURCE_FACET_KEY]).toBeUndefined();
854
+ });
855
+
856
+ expect(listQuery()["severityName"]).toBeUndefined();
857
+ expect(listQuery()["message"]).toBeUndefined();
858
+ expect(searchInput().value).toBe("");
859
+ expect(urlParams().get("search")).toBeNull();
860
+ });
861
+ });
862
+
863
+ describe("the window", () => {
864
+ test("picking a range moves the list, the chart and the URL together", async () => {
865
+ renderViewer();
866
+ await waitForLoad();
867
+
868
+ act(() => {
869
+ pickerProps!.onChange({ range: TimeRange.PAST_ONE_HOUR });
870
+ });
871
+
872
+ await waitFor(() => {
873
+ const window_: InBetween<Date> = listQuery()["time"] as InBetween<Date>;
874
+ expect(new Date(window_.startValue).toISOString()).toBe(
875
+ new Date(NOW.getTime() - 60 * 60 * 1000).toISOString(),
876
+ );
877
+ });
878
+
879
+ expect(urlParams().get("range")).toBe(TimeRange.PAST_ONE_HOUR);
880
+ expect(volumeAggregates().slice(-1)[0]!.query).toEqual(listQuery());
881
+ });
882
+
883
+ test("dragging the histogram zooms into a custom window", async () => {
884
+ renderViewer();
885
+ await waitForLoad();
886
+
887
+ await waitFor(() => {
888
+ expect(
889
+ screen.queryByTestId("bucket-2026-09-18T10:00:00.000Z"),
890
+ ).toBeInTheDocument();
891
+ });
892
+
893
+ const first: HTMLElement = screen.getByTestId(
894
+ "bucket-2026-09-18T10:00:00.000Z",
895
+ );
896
+ const second: HTMLElement = screen.getByTestId(
897
+ "bucket-2026-09-18T10:15:00.000Z",
898
+ );
899
+
900
+ fireEvent.mouseDown(first);
901
+ fireEvent.mouseMove(second);
902
+ fireEvent.mouseUp(second);
903
+
904
+ await waitFor(() => {
905
+ expect(urlParams().get("range")).toBe(TimeRange.CUSTOM);
906
+ });
907
+
908
+ expect(urlParams().get("start")).toBe("2026-09-18T10:00:00.000Z");
909
+ });
910
+
911
+ test("Refresh re-reads a relative window from now", async () => {
912
+ renderViewer();
913
+ await waitForLoad();
914
+
915
+ const before: number = listCalls().length;
916
+
917
+ now = new Date(NOW.getTime() + 60 * 60 * 1000);
918
+
919
+ fireEvent.click(screen.getByTitle("Refresh"));
920
+
921
+ await waitFor(() => {
922
+ expect(listCalls().length).toBeGreaterThan(before);
923
+ });
924
+
925
+ const window_: InBetween<Date> = listQuery()["time"] as InBetween<Date>;
926
+ expect(new Date(window_.endValue).toISOString()).toBe(now.toISOString());
927
+ });
928
+ });
929
+
930
+ describe("pagination", () => {
931
+ test("moving to page 2 skips a page and records it in the URL", async () => {
932
+ stubList(EVENTS, 500);
933
+
934
+ renderViewer();
935
+ await waitForLoad();
936
+
937
+ fireEvent.click(screen.getByRole("button", { name: "Go to next page" }));
938
+
939
+ await waitFor(() => {
940
+ expect(lastListCall()["skip"]).toBe(50);
941
+ });
942
+
943
+ expect(urlParams().get("page")).toBe("2");
944
+ });
945
+
946
+ test("narrowing the list returns to page 1", async () => {
947
+ stubList(EVENTS, 500);
948
+
949
+ renderViewer();
950
+ await waitForLoad();
951
+
952
+ fireEvent.click(screen.getByRole("button", { name: "Go to next page" }));
953
+ await waitFor(() => {
954
+ expect(lastListCall()["skip"]).toBe(50);
955
+ });
956
+
957
+ await waitFor(() => {
958
+ expect(facetSection("Severity")).toHaveTextContent("Critical");
959
+ });
960
+
961
+ fireEvent.click(
962
+ within(facetSection("Severity")).getByText(OcsfSeverity.Critical),
963
+ );
964
+
965
+ await waitFor(() => {
966
+ expect(lastListCall()["skip"]).toBe(0);
967
+ });
968
+
969
+ expect(urlParams().get("page")).toBeNull();
970
+ });
971
+ });
972
+
973
+ describe("opening an event", () => {
974
+ test("clicking a row opens the drawer on that event", async () => {
975
+ renderViewer();
976
+ await waitForLoad();
977
+
978
+ fireEvent.click(rows()[0]!);
979
+
980
+ expect(
981
+ screen.getByTestId(SECURITY_EVENT_DETAIL_PANEL_TEST_ID),
982
+ ).toHaveTextContent("Failed logon for alice");
983
+ });
984
+
985
+ test("a filter added from the drawer becomes a chip on the list", async () => {
986
+ renderViewer();
987
+ await waitForLoad();
988
+
989
+ fireEvent.click(rows()[0]!);
990
+
991
+ fireEvent.click(
992
+ screen.getByRole("button", { name: "Filter by Event Class" }),
993
+ );
994
+
995
+ await waitFor(() => {
996
+ expect(listQuery()["className"]).toBe("Authentication");
997
+ });
998
+ });
999
+
1000
+ test("the highlight follows the event across a refresh, not the row object", async () => {
1001
+ renderViewer();
1002
+ await waitForLoad();
1003
+
1004
+ fireEvent.click(rows()[0]!);
1005
+ expect(rows()[0]).toHaveAttribute("aria-pressed", "true");
1006
+
1007
+ const before: number = listCalls().length;
1008
+ fireEvent.click(screen.getByTitle("Refresh"));
1009
+
1010
+ await waitFor(() => {
1011
+ expect(listCalls().length).toBeGreaterThan(before);
1012
+ });
1013
+
1014
+ expect(rows()[0]).toHaveAttribute("aria-pressed", "true");
1015
+ expect(rows()[1]).not.toHaveAttribute("aria-pressed");
1016
+ });
1017
+
1018
+ test("closing the drawer leaves the list where it was", async () => {
1019
+ renderViewer();
1020
+ await waitForLoad();
1021
+
1022
+ fireEvent.click(rows()[0]!);
1023
+ fireEvent.click(screen.getByRole("button", { name: /close/i }));
1024
+
1025
+ expect(
1026
+ screen.queryByTestId(SECURITY_EVENT_DETAIL_PANEL_TEST_ID),
1027
+ ).toBeNull();
1028
+ expect(rows()).toHaveLength(2);
1029
+ });
1030
+ });
1031
+
1032
+ describe("an empty list", () => {
1033
+ test("a project that has never received an event gets the setup guide", async () => {
1034
+ stubList([], 0);
1035
+
1036
+ renderViewer();
1037
+
1038
+ await waitFor(() => {
1039
+ expect(
1040
+ document.getElementById(SECURITY_EVENTS_EMPTY_STATE_ID),
1041
+ ).not.toBeNull();
1042
+ });
1043
+
1044
+ expect(screen.getByText("No security events yet")).toBeInTheDocument();
1045
+ });
1046
+
1047
+ /*
1048
+ * Offering the setup guide to someone who is already ingesting would send
1049
+ * them off to set up ingest they already have.
1050
+ */
1051
+ test("a project that has events gets a no-results message instead", async () => {
1052
+ getListMock.mockImplementation(
1053
+ async (data: { limit: number }): Promise<ListResult<SecurityEvent>> => {
1054
+ if (data.limit === 1) {
1055
+ return {
1056
+ data: [event({ time: new Date("2026-09-01T00:00:00.000Z") })],
1057
+ count: 1,
1058
+ skip: 0,
1059
+ limit: 1,
1060
+ };
1061
+ }
1062
+
1063
+ return { data: [], count: 0, skip: 0, limit: data.limit };
1064
+ },
1065
+ );
1066
+
1067
+ renderViewer();
1068
+
1069
+ await waitFor(() => {
1070
+ expect(
1071
+ document.getElementById(SECURITY_EVENTS_NO_RESULTS_ID),
1072
+ ).not.toBeNull();
1073
+ });
1074
+
1075
+ expect(document.getElementById(SECURITY_EVENTS_EMPTY_STATE_ID)).toBeNull();
1076
+ });
1077
+
1078
+ test("a narrowed list never offers the setup guide, whatever the project has", async () => {
1079
+ stubList([], 0);
1080
+
1081
+ renderViewer();
1082
+
1083
+ await waitFor(() => {
1084
+ expect(
1085
+ document.getElementById(SECURITY_EVENTS_EMPTY_STATE_ID),
1086
+ ).not.toBeNull();
1087
+ });
1088
+
1089
+ await submitSearch("severity:Critical");
1090
+
1091
+ await waitFor(() => {
1092
+ expect(
1093
+ document.getElementById(SECURITY_EVENTS_NO_RESULTS_ID),
1094
+ ).not.toBeNull();
1095
+ });
1096
+
1097
+ expect(document.getElementById(SECURITY_EVENTS_EMPTY_STATE_ID)).toBeNull();
1098
+ });
1099
+ });
1100
+
1101
+ describe("a link into the explorer", () => {
1102
+ test("opens on the window, chips, search text and page it names", async () => {
1103
+ window.history.replaceState(
1104
+ null,
1105
+ "",
1106
+ `/dashboard/project/security-events?range=${TimeRange.PAST_ONE_HOUR}` +
1107
+ `&filters=${encodeURIComponent(
1108
+ JSON.stringify([["severityName", "Critical"]]),
1109
+ )}&search=${encodeURIComponent("failed logon")}&page=2&pageSize=25`,
1110
+ );
1111
+
1112
+ renderViewer();
1113
+ await waitForLoad();
1114
+
1115
+ expect(listQuery()["severityName"]).toBe("Critical");
1116
+ expect(String(listQuery()["message"])).toContain("failed logon");
1117
+ expect(lastListCall()["limit"]).toBe(25);
1118
+ expect(lastListCall()["skip"]).toBe(25);
1119
+ expect(searchInput().value).toBe("failed logon");
1120
+ expect(screen.getByTestId("time-range-picker")).toHaveTextContent(
1121
+ TimeRange.PAST_ONE_HOUR,
1122
+ );
1123
+ });
1124
+
1125
+ /*
1126
+ * The connection diagnostics' "view these events" links, and anything else
1127
+ * written for the model table this explorer replaced, name their attribute
1128
+ * filter in the table's own param. Reading it back is what keeps an old
1129
+ * link from quietly opening an unfiltered list that looks like the right
1130
+ * answer.
1131
+ */
1132
+ test("a legacy model-table filter link is read back as a chip", async () => {
1133
+ const legacyFilter: string = encodeURIComponent(
1134
+ JSON.stringify({
1135
+ attributes: { "oneuptime.security_connection.id": "conn-1" },
1136
+ }),
1137
+ );
1138
+
1139
+ window.history.replaceState(
1140
+ null,
1141
+ "",
1142
+ `/dashboard/project/security-events?security-events-table-filter=${legacyFilter}`,
1143
+ );
1144
+
1145
+ renderViewer();
1146
+ await waitForLoad();
1147
+
1148
+ expect(listQuery()["attributes"]).toEqual({
1149
+ "oneuptime.security_connection.id": "conn-1",
1150
+ });
1151
+ // And the chip says so, rather than narrowing the list silently.
1152
+ expect(
1153
+ screen.getByText(/oneuptime\.security_connection\.id/),
1154
+ ).toBeInTheDocument();
1155
+ expect(screen.getByText("conn-1")).toBeInTheDocument();
1156
+ });
1157
+
1158
+ test("a malformed filters payload opens an unfiltered list rather than a blank page", async () => {
1159
+ window.history.replaceState(
1160
+ null,
1161
+ "",
1162
+ "/dashboard/project/security-events?filters=%7Bnot-json",
1163
+ );
1164
+
1165
+ renderViewer();
1166
+ await waitForLoad();
1167
+
1168
+ expect(rows()).toHaveLength(2);
1169
+ expect(listQuery()["severityName"]).toBeUndefined();
1170
+ });
1171
+ });
1172
+
1173
+ describe("live updates", () => {
1174
+ test("are off until switched on, and then poll", async () => {
1175
+ jest.useFakeTimers();
1176
+
1177
+ try {
1178
+ renderViewer();
1179
+
1180
+ await act(async () => {
1181
+ await Promise.resolve();
1182
+ });
1183
+
1184
+ const toggle: HTMLElement = screen.getByTitle("Enable live updates");
1185
+ expect(toggle).toHaveTextContent("Paused");
1186
+
1187
+ fireEvent.click(toggle);
1188
+
1189
+ expect(screen.getByTitle("Pause live updates")).toHaveTextContent("Live");
1190
+
1191
+ const before: number = listCalls().length;
1192
+
1193
+ await act(async () => {
1194
+ jest.advanceTimersByTime(20000);
1195
+ await Promise.resolve();
1196
+ });
1197
+
1198
+ expect(listCalls().length).toBeGreaterThan(before);
1199
+ } finally {
1200
+ jest.useRealTimers();
1201
+ }
1202
+ });
1203
+ });