@oneuptime/common 13.0.7 → 13.0.8

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 (463) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  2. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  3. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  4. package/Server/API/AIBillingAPI.ts +1 -0
  5. package/Server/API/AIInsightAPI.ts +1 -4
  6. package/Server/API/AIInvestigationAPI.ts +1 -4
  7. package/Server/API/AIReadinessAPI.ts +4 -5
  8. package/Server/API/AlertAPI.ts +1 -0
  9. package/Server/API/AutoRemediationAPI.ts +1 -4
  10. package/Server/API/BaseAPI.ts +13 -0
  11. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  12. package/Server/API/BillingAPI.ts +2 -0
  13. package/Server/API/BillingInvoiceAPI.ts +1 -0
  14. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  15. package/Server/API/CodeFixRunAPI.ts +1 -3
  16. package/Server/API/CommonAPI.ts +68 -4
  17. package/Server/API/DashboardAPI.ts +12 -12
  18. package/Server/API/GlobalConfigAPI.ts +17 -0
  19. package/Server/API/IncidentAPI.ts +2 -0
  20. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  21. package/Server/API/NotificationAPI.ts +1 -0
  22. package/Server/API/OnCallCalendarAPI.ts +4 -5
  23. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  24. package/Server/API/OnCallReadinessAPI.ts +4 -1
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  26. package/Server/API/ProjectAPI.ts +1 -0
  27. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  28. package/Server/API/StatusPageAPI.ts +161 -32
  29. package/Server/API/TeamMemberAPI.ts +3 -0
  30. package/Server/API/UserCallAPI.ts +2 -0
  31. package/Server/API/UserEmailAPI.ts +2 -0
  32. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  33. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  34. package/Server/API/UserSlackAPI.ts +1 -0
  35. package/Server/API/UserSmsAPI.ts +2 -0
  36. package/Server/API/UserTotpAuthAPI.ts +1 -0
  37. package/Server/API/UserWebAuthnAPI.ts +2 -0
  38. package/Server/API/UserWebhookAPI.ts +1 -0
  39. package/Server/API/UserWhatsAppAPI.ts +2 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  41. package/Server/Middleware/UserAuthorization.ts +79 -5
  42. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  43. package/Server/Services/DatabaseService.ts +245 -0
  44. package/Server/Services/DetectionRuleService.ts +5 -4
  45. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  46. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  47. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  48. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  49. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  50. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  51. package/Server/Services/ProjectService.ts +18 -0
  52. package/Server/Services/RumSessionPinService.ts +274 -22
  53. package/Server/Services/TeamPermissionService.ts +120 -26
  54. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  55. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  56. package/Server/Types/Database/Permissions/Index.ts +75 -11
  57. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  58. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  59. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  60. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  61. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  62. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  63. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  64. package/Server/Utils/Realtime.ts +163 -45
  65. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  66. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  67. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  68. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  69. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  70. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  71. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  72. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  73. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  74. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  75. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  76. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  77. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  78. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  79. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  80. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  81. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  82. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  83. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  84. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  85. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  86. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  87. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  88. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  89. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  90. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  91. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  92. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  93. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  94. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  95. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  96. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  97. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  98. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  99. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  100. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  101. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  102. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  103. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  104. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  105. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  106. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  107. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  108. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  109. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  110. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  111. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  112. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  113. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  114. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  115. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  116. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  117. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  118. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  119. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  120. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  121. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  122. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  123. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  124. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  125. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  126. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  127. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  128. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  129. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  130. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  131. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  132. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  133. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  134. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  135. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  136. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  137. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  138. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  139. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  140. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  141. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  142. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  143. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  144. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  145. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  146. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  147. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  148. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  149. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  150. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  151. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  152. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  153. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  154. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  155. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  156. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  157. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  158. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  159. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  160. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  161. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  162. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  163. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  164. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  165. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  166. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  167. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  168. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  169. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  170. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  171. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  172. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  173. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  174. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  175. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  176. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  178. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  181. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  182. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  183. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  184. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  185. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  186. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  187. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  188. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  189. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  190. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  191. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  192. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  193. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  194. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  195. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  196. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  197. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  198. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  199. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  200. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  201. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  202. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  203. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  204. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  205. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  206. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  207. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  208. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  209. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  210. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  211. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  212. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  213. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  214. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  215. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  216. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  217. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  218. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  219. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  220. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  221. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  222. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  223. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  224. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  225. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  226. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  227. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  228. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  229. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  230. package/Types/Realtime/EventName.ts +11 -0
  231. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  232. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  233. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  234. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  235. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  236. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  237. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  238. package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
  239. package/UI/Components/EventItem/EventItem.tsx +12 -1
  240. package/UI/Components/Forms/BasicForm.tsx +151 -126
  241. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  242. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  243. package/UI/Components/Forms/FormSummary.tsx +7 -0
  244. package/UI/Components/Forms/Types/Field.ts +10 -0
  245. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  246. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  247. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  248. package/UI/Components/ModelTable/TableView.tsx +1 -1
  249. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  250. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  251. package/UI/Utils/API/API.ts +173 -12
  252. package/UI/Utils/API/RequestOptions.ts +11 -0
  253. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  254. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  255. package/UI/Utils/Navigation.ts +9 -0
  256. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  257. package/UI/Utils/Realtime.ts +176 -30
  258. package/Utils/API.ts +14 -5
  259. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  260. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  261. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  262. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  263. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  264. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  265. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  266. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  267. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  268. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  269. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  270. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  271. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  272. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  273. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  274. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  275. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  276. package/build/dist/Server/API/AlertAPI.js +1 -1
  277. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  278. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  279. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  280. package/build/dist/Server/API/BaseAPI.js +12 -0
  281. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  282. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  283. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  284. package/build/dist/Server/API/BillingAPI.js +2 -2
  285. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  286. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  287. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  288. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  289. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  290. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  291. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  292. package/build/dist/Server/API/CommonAPI.js +50 -5
  293. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  294. package/build/dist/Server/API/DashboardAPI.js +12 -12
  295. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  296. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  297. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  298. package/build/dist/Server/API/IncidentAPI.js +2 -2
  299. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  300. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  301. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  302. package/build/dist/Server/API/NotificationAPI.js +1 -1
  303. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  304. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  305. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  306. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  307. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  308. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  309. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  310. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  311. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  312. package/build/dist/Server/API/ProjectAPI.js +1 -1
  313. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  314. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  315. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  316. package/build/dist/Server/API/StatusPageAPI.js +125 -33
  317. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  318. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  319. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  320. package/build/dist/Server/API/UserCallAPI.js +2 -2
  321. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  322. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  323. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  324. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  325. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  326. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  327. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  328. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  329. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  330. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  331. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  332. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  333. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  334. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  335. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  336. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  337. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  338. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  339. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  340. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  341. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  343. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  344. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  345. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  346. package/build/dist/Server/Services/DatabaseService.js +171 -0
  347. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  348. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  349. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  351. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  352. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  353. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  354. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  355. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  356. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  357. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  358. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  359. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  360. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  361. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  362. package/build/dist/Server/Services/ProjectService.js +17 -0
  363. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  364. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  365. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  366. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  367. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  368. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  369. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  370. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  371. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  372. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  373. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  374. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  375. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  376. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  377. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  378. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  379. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  380. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  381. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  382. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  384. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  385. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  386. package/build/dist/Server/Utils/Realtime.js +126 -37
  387. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  388. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  389. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  390. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  391. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  392. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  393. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  394. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  395. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  396. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  397. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  398. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  399. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  400. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  401. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  402. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  403. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  404. package/build/dist/Types/Realtime/EventName.js +10 -0
  405. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  406. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  407. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  408. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  409. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  410. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  411. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  412. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  413. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  414. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  415. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  416. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  417. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  418. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  419. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  420. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
  421. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  422. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  423. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  424. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  425. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  426. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  427. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  428. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  429. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  430. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  431. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  432. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  433. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  434. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  435. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  436. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  437. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  438. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  439. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  440. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  441. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  442. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  443. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  444. package/build/dist/UI/Utils/API/API.js +143 -19
  445. package/build/dist/UI/Utils/API/API.js.map +1 -1
  446. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  447. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  448. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  449. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  450. package/build/dist/UI/Utils/Navigation.js +8 -0
  451. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  452. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  453. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  454. package/build/dist/UI/Utils/Realtime.js +120 -15
  455. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  456. package/build/dist/Utils/API.js +13 -5
  457. package/build/dist/Utils/API.js.map +1 -1
  458. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  459. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  460. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  461. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  462. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  463. package/package.json +1 -1
@@ -0,0 +1,729 @@
1
+ import "../TestingUtils/Init";
2
+ import BaseAnalyticsAPI from "../../../Server/API/BaseAnalyticsAPI";
3
+ import GlobalCache from "../../../Server/Infrastructure/GlobalCache";
4
+ import AnalyticsDatabaseService from "../../../Server/Services/AnalyticsDatabaseService";
5
+ import ProjectService from "../../../Server/Services/ProjectService";
6
+ import ModelPermission from "../../../Server/Types/AnalyticsDatabase/ModelPermission";
7
+ import {
8
+ ExpressRequest,
9
+ ExpressResponse,
10
+ NextFunction,
11
+ } from "../../../Server/Utils/Express";
12
+ import Response from "../../../Server/Utils/Response";
13
+ import Log from "../../../Models/AnalyticsModels/Log";
14
+ import MonitorLog from "../../../Models/AnalyticsModels/MonitorLog";
15
+ import { mockRouter } from "./Helpers";
16
+ import { getJestSpyOn } from "../../Spy";
17
+ import AggregationType from "../../../Types/BaseDatabase/AggregationType";
18
+ import { PlanType } from "../../../Types/Billing/SubscriptionPlan";
19
+ import PermissionScope from "../../../Types/Database/AccessControl/PermissionScope";
20
+ import Dictionary from "../../../Types/Dictionary";
21
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
22
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
23
+ import PaymentRequiredException from "../../../Types/Exception/PaymentRequiredException";
24
+ import { JSONObject } from "../../../Types/JSON";
25
+ import JSONFunctions from "../../../Types/JSONFunctions";
26
+ import ObjectID from "../../../Types/ObjectID";
27
+ import Permission, {
28
+ UserGlobalAccessPermission,
29
+ UserPermission,
30
+ UserTenantAccessPermission,
31
+ } from "../../../Types/Permission";
32
+ import UserType from "../../../Types/UserType";
33
+ import { beforeEach, describe, expect, jest, test } from "@jest/globals";
34
+
35
+ /*
36
+ * POST /<analytics-model>/aggregate keeps a short-lived cache in front of
37
+ * AnalyticsDatabaseService.aggregateBy — and aggregateBy is where the read
38
+ * check lives. A hit used to be answered before that check ever ran, keyed
39
+ * only on the caller-supplied tenant plus the query. getUserMiddleware
40
+ * accepts the `tenantid` header even from an anonymous caller, so for the
41
+ * cache TTL anyone could read another user's Log / Span / Metric / AuditLog
42
+ * aggregate for any project whose id they knew, and a member whose read was
43
+ * narrower (no read on the model, Owned- or label-scoped) got the wider
44
+ * result someone else had just cached.
45
+ *
46
+ * These tests pin the gate in front of the cache: the caller must pass the
47
+ * same read check the live path runs before the cache is even consulted,
48
+ * and the slot is keyed on the raw request plus the permission-scoped
49
+ * query, so callers with different access never share one.
50
+ */
51
+
52
+ jest.mock("../../../Server/Utils/Express", () => {
53
+ return {
54
+ getRouter: () => {
55
+ return mockRouter;
56
+ },
57
+ };
58
+ });
59
+
60
+ /*
61
+ * Billing on, so the unpaid-subscription gate in the read check is live.
62
+ * It only fires when props.currentPlan is set, and every test except the
63
+ * unpaid one gets plan: null from getCurrentPlan, which leaves it unset.
64
+ */
65
+ jest.mock("../../../Server/EnvironmentConfig", () => {
66
+ return {
67
+ ...(jest.requireActual("../../../Server/EnvironmentConfig") as Record<
68
+ string,
69
+ unknown
70
+ >),
71
+ IsBillingEnabled: true,
72
+ };
73
+ });
74
+
75
+ jest.mock("../../../Server/Utils/Response", () => {
76
+ return {
77
+ sendEmptySuccessResponse: jest.fn(),
78
+ sendEntityResponse: jest.fn(),
79
+ sendEntityArrayResponse: jest.fn(),
80
+ sendJsonObjectResponse: jest.fn(),
81
+ sendErrorResponse: jest.fn(),
82
+ };
83
+ });
84
+
85
+ const PROJECT_ID: ObjectID = new ObjectID(
86
+ "5f8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
87
+ );
88
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
89
+ "6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d",
90
+ );
91
+ const OWNED_SERVICE_ID: ObjectID = new ObjectID(
92
+ "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
93
+ );
94
+
95
+ /*
96
+ * What another user's earlier request left in the cache. The tests that
97
+ * must not see it assert that neither this body nor anything else from the
98
+ * cache reaches the response.
99
+ */
100
+ const CACHED_BODY: JSONObject = {
101
+ data: [{ timestamp: "2026-09-18T00:00:00.000Z", value: 4242 }],
102
+ truncated: false,
103
+ };
104
+
105
+ const LIVE_BODY: JSONObject = {
106
+ data: [{ timestamp: "2026-09-18T00:00:00.000Z", value: 7 }],
107
+ truncated: false,
108
+ };
109
+
110
+ type SpyInstance = ReturnType<typeof getJestSpyOn>;
111
+
112
+ type CallResult = {
113
+ thrownToNext: unknown;
114
+ sentBody: JSONObject | undefined;
115
+ };
116
+
117
+ function buildAggregateBody(data: {
118
+ aggregateColumnName: string;
119
+ query?: JSONObject | undefined;
120
+ }): JSONObject {
121
+ return {
122
+ aggregateBy: JSONFunctions.serialize({
123
+ aggregateColumnName: data.aggregateColumnName,
124
+ aggregationType: AggregationType.Count,
125
+ aggregationTimestampColumnName: "time",
126
+ startTimestamp: new Date("2026-09-17T00:00:00.000Z"),
127
+ endTimestamp: new Date("2026-09-18T00:00:00.000Z"),
128
+ query: data.query || {},
129
+ limit: 10,
130
+ skip: 0,
131
+ } as JSONObject),
132
+ };
133
+ }
134
+
135
+ function buildTenantPermissions(
136
+ projectId: ObjectID,
137
+ permissions: Array<UserPermission>,
138
+ ): UserTenantAccessPermission {
139
+ return {
140
+ _type: "UserTenantAccessPermission",
141
+ projectId,
142
+ permissions,
143
+ };
144
+ }
145
+
146
+ function grant(
147
+ permission: Permission,
148
+ scope?: PermissionScope | undefined,
149
+ ): UserPermission {
150
+ return {
151
+ _type: "UserPermission",
152
+ permission,
153
+ labelIds: [],
154
+ isBlockPermission: false,
155
+ ...(scope ? { scope } : {}),
156
+ };
157
+ }
158
+
159
+ /*
160
+ * The request as getUserMiddleware leaves it for a logged-in user: the
161
+ * tenant from the header, the decoded token, and the permissions it loaded
162
+ * from Postgres. Driving the real middleware here would need a signed JWT
163
+ * and a database, and it is not what is under test.
164
+ */
165
+ function buildMemberRequest(data: {
166
+ body: JSONObject;
167
+ tenantPermissions: Dictionary<UserTenantAccessPermission>;
168
+ projectIds?: Array<ObjectID> | undefined;
169
+ headers?: Dictionary<string> | undefined;
170
+ }): ExpressRequest {
171
+ const globalPermission: UserGlobalAccessPermission = {
172
+ _type: "UserGlobalAccessPermission",
173
+ projectIds: data.projectIds || [PROJECT_ID],
174
+ globalPermissions: [Permission.Public, Permission.User],
175
+ };
176
+
177
+ return {
178
+ params: {},
179
+ query: {},
180
+ headers: { tenantid: PROJECT_ID.toString(), ...(data.headers || {}) },
181
+ body: data.body,
182
+ tenantId: PROJECT_ID,
183
+ userType: UserType.User,
184
+ userAuthorization: {
185
+ userId: ObjectID.generate(),
186
+ email: "member@example.com",
187
+ name: "Member",
188
+ isMasterAdmin: false,
189
+ },
190
+ userGlobalAccessPermission: globalPermission,
191
+ userTenantAccessPermission: data.tenantPermissions,
192
+ userTeamIds: [],
193
+ } as unknown as ExpressRequest;
194
+ }
195
+
196
+ /*
197
+ * The request as ProjectMiddleware leaves it for a project API key: the
198
+ * tenant comes from the key itself, there is no user id, and the tenant
199
+ * permissions are the grants configured on the key.
200
+ */
201
+ function buildApiKeyRequest(data: {
202
+ body: JSONObject;
203
+ keyPermissions: Array<UserPermission>;
204
+ }): ExpressRequest {
205
+ return {
206
+ params: {},
207
+ query: {},
208
+ headers: { tenantid: PROJECT_ID.toString() },
209
+ body: data.body,
210
+ tenantId: PROJECT_ID,
211
+ userType: UserType.API,
212
+ userGlobalAccessPermission: {
213
+ _type: "UserGlobalAccessPermission",
214
+ projectIds: [PROJECT_ID],
215
+ globalPermissions: [
216
+ Permission.Public,
217
+ Permission.User,
218
+ Permission.CurrentUser,
219
+ Permission.AuthenticatedRequest,
220
+ ],
221
+ },
222
+ userTenantAccessPermission: {
223
+ [PROJECT_ID.toString()]: buildTenantPermissions(
224
+ PROJECT_ID,
225
+ data.keyPermissions,
226
+ ),
227
+ },
228
+ } as unknown as ExpressRequest;
229
+ }
230
+
231
+ /*
232
+ * A master admin: no membership in the project at all, which the read check
233
+ * admits through its root branch.
234
+ */
235
+ function buildMasterAdminRequest(data: { body: JSONObject }): ExpressRequest {
236
+ return {
237
+ params: {},
238
+ query: {},
239
+ headers: { tenantid: PROJECT_ID.toString() },
240
+ body: data.body,
241
+ tenantId: PROJECT_ID,
242
+ userType: UserType.MasterAdmin,
243
+ userAuthorization: {
244
+ userId: ObjectID.generate(),
245
+ email: "admin@example.com",
246
+ name: "Admin",
247
+ isMasterAdmin: true,
248
+ },
249
+ } as unknown as ExpressRequest;
250
+ }
251
+
252
+ async function callAggregateRoute(data: {
253
+ uri: string;
254
+ req: ExpressRequest;
255
+ runUserMiddleware?: boolean | undefined;
256
+ }): Promise<CallResult> {
257
+ const res: ExpressResponse = {
258
+ status: jest.fn().mockReturnThis(),
259
+ json: jest.fn().mockReturnThis(),
260
+ send: jest.fn().mockReturnThis(),
261
+ set: jest.fn().mockReturnThis(),
262
+ } as unknown as ExpressResponse;
263
+
264
+ const route: ReturnType<typeof mockRouter.match> = mockRouter.match(
265
+ "POST",
266
+ data.uri,
267
+ );
268
+
269
+ let thrownToNext: unknown = undefined;
270
+ const handlerNext: NextFunction = ((err?: unknown) => {
271
+ thrownToNext = err;
272
+ }) as NextFunction;
273
+
274
+ if (data.runUserMiddleware) {
275
+ let reachedHandler: boolean = false;
276
+ await route.middlewares[0]!(data.req, res, (() => {
277
+ reachedHandler = true;
278
+ }) as NextFunction);
279
+ expect(reachedHandler).toBe(true);
280
+ }
281
+
282
+ await route.handlerFunction(data.req, res, handlerNext);
283
+
284
+ const sendJsonObjectResponse: jest.Mock =
285
+ Response.sendJsonObjectResponse as unknown as jest.Mock;
286
+ const lastCall: Array<unknown> | undefined =
287
+ sendJsonObjectResponse.mock.calls[
288
+ sendJsonObjectResponse.mock.calls.length - 1
289
+ ];
290
+
291
+ return {
292
+ thrownToNext,
293
+ sentBody: lastCall ? (lastCall[2] as JSONObject) : undefined,
294
+ };
295
+ }
296
+
297
+ describe("BaseAnalyticsAPI aggregate cache authorization", () => {
298
+ let monitorLogService: AnalyticsDatabaseService<MonitorLog>;
299
+ let logService: AnalyticsDatabaseService<Log>;
300
+ let cacheRead: SpyInstance;
301
+ let cacheWrite: SpyInstance;
302
+
303
+ beforeEach(() => {
304
+ jest.restoreAllMocks();
305
+ jest.clearAllMocks();
306
+ mockRouter.routes = [];
307
+
308
+ monitorLogService = new AnalyticsDatabaseService<MonitorLog>({
309
+ modelType: MonitorLog,
310
+ });
311
+ logService = new AnalyticsDatabaseService<Log>({ modelType: Log });
312
+
313
+ new BaseAnalyticsAPI<MonitorLog, AnalyticsDatabaseService<MonitorLog>>(
314
+ MonitorLog,
315
+ monitorLogService,
316
+ );
317
+ new BaseAnalyticsAPI<Log, AnalyticsDatabaseService<Log>>(Log, logService);
318
+
319
+ // Never ClickHouse: a call here means the request got past the gate.
320
+ getJestSpyOn(monitorLogService, "aggregateBy").mockResolvedValue(LIVE_BODY);
321
+ getJestSpyOn(logService, "aggregateBy").mockResolvedValue(LIVE_BODY);
322
+
323
+ getJestSpyOn(ProjectService, "getCurrentPlan").mockResolvedValue({
324
+ plan: null,
325
+ isSubscriptionUnpaid: false,
326
+ });
327
+ getJestSpyOn(ProjectService, "updateLastActive").mockResolvedValue(
328
+ undefined,
329
+ );
330
+
331
+ // A warm cache: every slot holds another user's result.
332
+ cacheRead = getJestSpyOn(GlobalCache, "getJSONObject").mockResolvedValue(
333
+ CACHED_BODY,
334
+ );
335
+ cacheWrite = getJestSpyOn(GlobalCache, "setJSON").mockResolvedValue(
336
+ undefined,
337
+ );
338
+ });
339
+
340
+ test("an anonymous caller with only a tenantid header gets 401 and the cache is never read", async () => {
341
+ const req: ExpressRequest = {
342
+ params: {},
343
+ query: {},
344
+ headers: { tenantid: PROJECT_ID.toString() },
345
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
346
+ } as unknown as ExpressRequest;
347
+
348
+ const result: CallResult = await callAggregateRoute({
349
+ uri: "/monitor-log/aggregate",
350
+ req,
351
+ runUserMiddleware: true,
352
+ });
353
+
354
+ // The middleware really did take the tenant from the header.
355
+ expect((req as unknown as { tenantId: ObjectID }).tenantId.toString()).toBe(
356
+ PROJECT_ID.toString(),
357
+ );
358
+
359
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthenticatedException);
360
+ expect((result.thrownToNext as NotAuthenticatedException).code).toBe(401);
361
+ expect(result.sentBody).toBeUndefined();
362
+ expect(cacheRead).not.toHaveBeenCalled();
363
+ expect(cacheWrite).not.toHaveBeenCalled();
364
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
365
+ });
366
+
367
+ test("a project member without read permission on the model gets 422 and the cache is never read", async () => {
368
+ const tenantPermissions: Dictionary<UserTenantAccessPermission> = {
369
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
370
+ grant(Permission.ReadProjectIncident),
371
+ ]),
372
+ };
373
+
374
+ const result: CallResult = await callAggregateRoute({
375
+ uri: "/monitor-log/aggregate",
376
+ req: buildMemberRequest({
377
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
378
+ tenantPermissions,
379
+ }),
380
+ });
381
+
382
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
383
+ expect((result.thrownToNext as NotAuthorizedException).code).toBe(422);
384
+ expect(result.sentBody).toBeUndefined();
385
+ expect(cacheRead).not.toHaveBeenCalled();
386
+ expect(cacheWrite).not.toHaveBeenCalled();
387
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
388
+ });
389
+
390
+ test("a member with read permission is still served from the cache", async () => {
391
+ const tenantPermissions: Dictionary<UserTenantAccessPermission> = {
392
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
393
+ grant(Permission.ProjectMember),
394
+ ]),
395
+ };
396
+
397
+ const result: CallResult = await callAggregateRoute({
398
+ uri: "/monitor-log/aggregate",
399
+ req: buildMemberRequest({
400
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
401
+ tenantPermissions,
402
+ }),
403
+ });
404
+
405
+ expect(result.thrownToNext).toBeUndefined();
406
+ expect(result.sentBody).toEqual(CACHED_BODY);
407
+ expect(cacheRead).toHaveBeenCalledTimes(1);
408
+ expect(cacheRead.mock.calls[0]![0]).toBe("MonitorLog-aggregate");
409
+ expect(
410
+ cacheRead.mock.calls[0]![1].startsWith(`${PROJECT_ID.toString()}:`),
411
+ ).toBe(true);
412
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
413
+ });
414
+
415
+ test("two members with the same unrestricted read share one cache slot", async () => {
416
+ const readers: Array<Permission> = [
417
+ Permission.ProjectMember,
418
+ Permission.ReadProjectMonitor,
419
+ ];
420
+
421
+ for (const permission of readers) {
422
+ await callAggregateRoute({
423
+ uri: "/monitor-log/aggregate",
424
+ req: buildMemberRequest({
425
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
426
+ tenantPermissions: {
427
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
428
+ grant(permission),
429
+ ]),
430
+ },
431
+ }),
432
+ });
433
+ }
434
+
435
+ expect(cacheRead).toHaveBeenCalledTimes(2);
436
+ expect(cacheRead.mock.calls[0]![1]).toBe(cacheRead.mock.calls[1]![1]);
437
+ });
438
+
439
+ test("an Owned-scoped member does not get the slot an unrestricted member filled", async () => {
440
+ /*
441
+ * resolveOwnedParentIds reads ServiceOwnerUser / ServiceOwnerTeam from
442
+ * Postgres; here the member owns exactly one service.
443
+ */
444
+ const resolveOwned: SpyInstance = getJestSpyOn(
445
+ ModelPermission,
446
+ "resolveOwnedParentIds",
447
+ ).mockResolvedValue(new Set<string>([OWNED_SERVICE_ID.toString()]));
448
+
449
+ // The unrestricted member misses, runs live, and fills their slot.
450
+ cacheRead.mockResolvedValue(null);
451
+
452
+ await callAggregateRoute({
453
+ uri: "/logs/aggregate",
454
+ req: buildMemberRequest({
455
+ body: buildAggregateBody({ aggregateColumnName: "severityNumber" }),
456
+ tenantPermissions: {
457
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
458
+ grant(Permission.ProjectMember),
459
+ ]),
460
+ },
461
+ }),
462
+ });
463
+
464
+ expect(cacheWrite).toHaveBeenCalledTimes(1);
465
+ const unrestrictedKey: string = cacheWrite.mock.calls[0]![1];
466
+ expect(resolveOwned).not.toHaveBeenCalled();
467
+
468
+ // From now on only that slot is warm.
469
+ cacheRead.mockImplementation(
470
+ async (_namespace: string, key: string): Promise<JSONObject | null> => {
471
+ return key === unrestrictedKey ? CACHED_BODY : null;
472
+ },
473
+ );
474
+
475
+ const result: CallResult = await callAggregateRoute({
476
+ uri: "/logs/aggregate",
477
+ req: buildMemberRequest({
478
+ body: buildAggregateBody({ aggregateColumnName: "severityNumber" }),
479
+ tenantPermissions: {
480
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
481
+ grant(Permission.ReadTelemetryServiceLog, PermissionScope.Owned),
482
+ ]),
483
+ },
484
+ }),
485
+ });
486
+
487
+ expect(resolveOwned).toHaveBeenCalled();
488
+ const ownedKey: string = cacheRead.mock.calls[1]![1];
489
+ expect(ownedKey).not.toBe(unrestrictedKey);
490
+ // The slot is keyed on what the member may actually see.
491
+ expect(ownedKey).toContain(OWNED_SERVICE_ID.toString());
492
+
493
+ expect(result.thrownToNext).toBeUndefined();
494
+ expect(result.sentBody).toEqual(LIVE_BODY);
495
+ expect(logService.aggregateBy).toHaveBeenCalledTimes(2);
496
+ });
497
+
498
+ test("a multi-tenant request does not share the slot of a single-project request for the same tenant", async () => {
499
+ const tenantPermissions: Dictionary<UserTenantAccessPermission> = {
500
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
501
+ grant(Permission.ProjectMember),
502
+ ]),
503
+ [OTHER_PROJECT_ID.toString()]: buildTenantPermissions(OTHER_PROJECT_ID, [
504
+ grant(Permission.ProjectMember),
505
+ ]),
506
+ };
507
+
508
+ await callAggregateRoute({
509
+ uri: "/monitor-log/aggregate",
510
+ req: buildMemberRequest({
511
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
512
+ tenantPermissions,
513
+ projectIds: [PROJECT_ID, OTHER_PROJECT_ID],
514
+ headers: { "is-multi-tenant-query": "true" },
515
+ }),
516
+ });
517
+
518
+ await callAggregateRoute({
519
+ uri: "/monitor-log/aggregate",
520
+ req: buildMemberRequest({
521
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
522
+ tenantPermissions: {
523
+ [PROJECT_ID.toString()]: tenantPermissions[PROJECT_ID.toString()]!,
524
+ },
525
+ }),
526
+ });
527
+
528
+ expect(cacheRead).toHaveBeenCalledTimes(2);
529
+ const multiTenantKey: string = cacheRead.mock.calls[0]![1];
530
+ const singleProjectKey: string = cacheRead.mock.calls[1]![1];
531
+
532
+ expect(multiTenantKey).toContain(OTHER_PROJECT_ID.toString());
533
+ expect(singleProjectKey).not.toContain(OTHER_PROJECT_ID.toString());
534
+ expect(multiTenantKey).not.toBe(singleProjectKey);
535
+ });
536
+
537
+ test("a member who can read the model but not the aggregated column gets 422 and the cache is never read", async () => {
538
+ // retentionDate carries no read grants at all: only root may read it.
539
+ const result: CallResult = await callAggregateRoute({
540
+ uri: "/monitor-log/aggregate",
541
+ req: buildMemberRequest({
542
+ body: buildAggregateBody({ aggregateColumnName: "retentionDate" }),
543
+ tenantPermissions: {
544
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
545
+ grant(Permission.ProjectMember),
546
+ ]),
547
+ },
548
+ }),
549
+ });
550
+
551
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
552
+ expect((result.thrownToNext as NotAuthorizedException).code).toBe(422);
553
+ expect(result.sentBody).toBeUndefined();
554
+ expect(cacheRead).not.toHaveBeenCalled();
555
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
556
+ });
557
+
558
+ test("a member filtering on a column they cannot read gets 422 and the cache is never read", async () => {
559
+ const result: CallResult = await callAggregateRoute({
560
+ uri: "/monitor-log/aggregate",
561
+ req: buildMemberRequest({
562
+ body: buildAggregateBody({
563
+ aggregateColumnName: "time",
564
+ query: { retentionDate: new Date("2026-10-01T00:00:00.000Z") },
565
+ }),
566
+ tenantPermissions: {
567
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
568
+ grant(Permission.ProjectMember),
569
+ ]),
570
+ },
571
+ }),
572
+ });
573
+
574
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
575
+ expect((result.thrownToNext as NotAuthorizedException).code).toBe(422);
576
+ expect(cacheRead).not.toHaveBeenCalled();
577
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
578
+ });
579
+
580
+ test("a member of a project with an unpaid subscription gets 402 and the cache is never read", async () => {
581
+ getJestSpyOn(ProjectService, "getCurrentPlan").mockResolvedValue({
582
+ plan: PlanType.Growth,
583
+ isSubscriptionUnpaid: true,
584
+ });
585
+
586
+ const result: CallResult = await callAggregateRoute({
587
+ uri: "/monitor-log/aggregate",
588
+ req: buildMemberRequest({
589
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
590
+ tenantPermissions: {
591
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
592
+ grant(Permission.ProjectMember),
593
+ ]),
594
+ },
595
+ }),
596
+ });
597
+
598
+ expect(result.thrownToNext).toBeInstanceOf(PaymentRequiredException);
599
+ expect((result.thrownToNext as PaymentRequiredException).code).toBe(402);
600
+ expect(cacheRead).not.toHaveBeenCalled();
601
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
602
+ });
603
+
604
+ test("an API key with read permission is served from the same slot as a member", async () => {
605
+ const body: JSONObject = buildAggregateBody({
606
+ aggregateColumnName: "time",
607
+ });
608
+
609
+ await callAggregateRoute({
610
+ uri: "/monitor-log/aggregate",
611
+ req: buildMemberRequest({
612
+ body,
613
+ tenantPermissions: {
614
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
615
+ grant(Permission.ProjectMember),
616
+ ]),
617
+ },
618
+ }),
619
+ });
620
+
621
+ const result: CallResult = await callAggregateRoute({
622
+ uri: "/monitor-log/aggregate",
623
+ req: buildApiKeyRequest({
624
+ body,
625
+ keyPermissions: [grant(Permission.ReadProjectMonitor)],
626
+ }),
627
+ });
628
+
629
+ expect(result.thrownToNext).toBeUndefined();
630
+ expect(result.sentBody).toEqual(CACHED_BODY);
631
+ expect(cacheRead).toHaveBeenCalledTimes(2);
632
+ expect(cacheRead.mock.calls[1]![1]).toBe(cacheRead.mock.calls[0]![1]);
633
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
634
+ });
635
+
636
+ test("an API key without read permission on the model gets 422 and the cache is never read", async () => {
637
+ const result: CallResult = await callAggregateRoute({
638
+ uri: "/monitor-log/aggregate",
639
+ req: buildApiKeyRequest({
640
+ body: buildAggregateBody({ aggregateColumnName: "time" }),
641
+ keyPermissions: [grant(Permission.ReadProjectIncident)],
642
+ }),
643
+ });
644
+
645
+ expect(result.thrownToNext).toBeInstanceOf(NotAuthorizedException);
646
+ expect((result.thrownToNext as NotAuthorizedException).code).toBe(422);
647
+ expect(cacheRead).not.toHaveBeenCalled();
648
+ expect(monitorLogService.aggregateBy).not.toHaveBeenCalled();
649
+ });
650
+
651
+ test("a master admin who is not a project member is served from the same slot as an unrestricted member", async () => {
652
+ const body: JSONObject = buildAggregateBody({
653
+ aggregateColumnName: "time",
654
+ });
655
+
656
+ await callAggregateRoute({
657
+ uri: "/monitor-log/aggregate",
658
+ req: buildMemberRequest({
659
+ body,
660
+ tenantPermissions: {
661
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
662
+ grant(Permission.ProjectMember),
663
+ ]),
664
+ },
665
+ }),
666
+ });
667
+
668
+ const result: CallResult = await callAggregateRoute({
669
+ uri: "/monitor-log/aggregate",
670
+ req: buildMasterAdminRequest({ body }),
671
+ });
672
+
673
+ expect(result.thrownToNext).toBeUndefined();
674
+ expect(result.sentBody).toEqual(CACHED_BODY);
675
+ expect(cacheRead).toHaveBeenCalledTimes(2);
676
+ expect(cacheRead.mock.calls[1]![1]).toBe(cacheRead.mock.calls[0]![1]);
677
+ });
678
+
679
+ test("requests that scope to the same query but differ in the raw query never share a slot", async () => {
680
+ /*
681
+ * Scoping overwrites projectId, so both requests below scope to the
682
+ * same query. MetricService still plans from the raw projectId (point
683
+ * type, rollup routing), so they can compute different results and
684
+ * must not share a slot.
685
+ */
686
+ const tenantPermissions: Dictionary<UserTenantAccessPermission> = {
687
+ [PROJECT_ID.toString()]: buildTenantPermissions(PROJECT_ID, [
688
+ grant(Permission.ProjectMember),
689
+ ]),
690
+ };
691
+
692
+ cacheRead.mockResolvedValue(null);
693
+
694
+ await callAggregateRoute({
695
+ uri: "/monitor-log/aggregate",
696
+ req: buildMemberRequest({
697
+ body: buildAggregateBody({
698
+ aggregateColumnName: "time",
699
+ query: { projectId: PROJECT_ID },
700
+ }),
701
+ tenantPermissions,
702
+ }),
703
+ });
704
+
705
+ expect(cacheWrite).toHaveBeenCalledTimes(1);
706
+ const firstKey: string = cacheWrite.mock.calls[0]![1];
707
+
708
+ cacheRead.mockImplementation(
709
+ async (_namespace: string, key: string): Promise<JSONObject | null> => {
710
+ return key === firstKey ? CACHED_BODY : null;
711
+ },
712
+ );
713
+
714
+ const result: CallResult = await callAggregateRoute({
715
+ uri: "/monitor-log/aggregate",
716
+ req: buildMemberRequest({
717
+ body: buildAggregateBody({
718
+ aggregateColumnName: "time",
719
+ query: { projectId: OTHER_PROJECT_ID },
720
+ }),
721
+ tenantPermissions,
722
+ }),
723
+ });
724
+
725
+ expect(cacheRead.mock.calls[1]![1]).not.toBe(firstKey);
726
+ expect(result.sentBody).toEqual(LIVE_BODY);
727
+ expect(monitorLogService.aggregateBy).toHaveBeenCalledTimes(2);
728
+ });
729
+ });