@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,664 @@
1
+ import StatusPageAPI from "../../../Server/API/StatusPageAPI";
2
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
3
+ import AccessTokenService from "../../../Server/Services/AccessTokenService";
4
+ import ProjectService from "../../../Server/Services/ProjectService";
5
+ import StatusPageOwnerTeamService from "../../../Server/Services/StatusPageOwnerTeamService";
6
+ import StatusPageOwnerUserService from "../../../Server/Services/StatusPageOwnerUserService";
7
+ import StatusPageService from "../../../Server/Services/StatusPageService";
8
+ import TeamMemberService from "../../../Server/Services/TeamMemberService";
9
+ import UserService from "../../../Server/Services/UserService";
10
+ import JSONWebToken from "../../../Server/Utils/JsonWebToken";
11
+ import logger from "../../../Server/Utils/Logger";
12
+ import { expressErrorHandler } from "../../../Server/Utils/StartServer";
13
+ import Label from "../../../Models/DatabaseModels/Label";
14
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
15
+ import StatusPageOwnerUser from "../../../Models/DatabaseModels/StatusPageOwnerUser";
16
+ import PermissionScope from "../../../Types/Database/AccessControl/PermissionScope";
17
+ import Dictionary from "../../../Types/Dictionary";
18
+ import Email from "../../../Types/Email";
19
+ import JSONWebTokenData from "../../../Types/JsonWebTokenData";
20
+ import ObjectID from "../../../Types/ObjectID";
21
+ import Permission, {
22
+ UserGlobalAccessPermission,
23
+ UserTenantAccessPermission,
24
+ } from "../../../Types/Permission";
25
+ import {
26
+ afterAll,
27
+ beforeAll,
28
+ beforeEach,
29
+ describe,
30
+ expect,
31
+ it,
32
+ } from "@jest/globals";
33
+ import express from "express";
34
+ import http from "http";
35
+ import { AddressInfo } from "net";
36
+ import { FindOperator } from "typeorm";
37
+
38
+ /*
39
+ * POST /status-page/test-email-report sends a status page's report to an
40
+ * address named in the body, so only someone who could update that page may
41
+ * call it. These tests drive the route through a real Express app with the
42
+ * production middleware chain (getUserMiddleware, requireUserAuthentication),
43
+ * the real model permission layer and the production error handler, so the
44
+ * statuses asserted are the ones a client actually receives. Only the data
45
+ * layer is stubbed: token decoding, permission and ownership lookups, the
46
+ * status page reads and the email send.
47
+ */
48
+
49
+ const PROJECT_A: ObjectID = new ObjectID(
50
+ "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
51
+ );
52
+ const PROJECT_B: ObjectID = new ObjectID(
53
+ "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
54
+ );
55
+ const STATUS_PAGE_IN_A: ObjectID = new ObjectID(
56
+ "11111111-1111-4111-8111-111111111111",
57
+ );
58
+ const MISSING_STATUS_PAGE: ObjectID = new ObjectID(
59
+ "99999999-9999-4999-8999-999999999999",
60
+ );
61
+
62
+ // The label the status page carries, and one it does not.
63
+ const PAGE_LABEL: ObjectID = new ObjectID(
64
+ "1abe1000-0000-4000-8000-000000000001",
65
+ );
66
+ const OTHER_LABEL: ObjectID = new ObjectID(
67
+ "1abe1000-0000-4000-8000-000000000002",
68
+ );
69
+
70
+ function user(suffix: string): ObjectID {
71
+ return new ObjectID(`00000000-0000-4000-8000-0000000000${suffix}`);
72
+ }
73
+
74
+ const EDITOR_IN_A: ObjectID = user("0a");
75
+ const VIEWER_IN_A: ObjectID = user("0b");
76
+ const BLOCK_ONLY_IN_A: ObjectID = user("0c");
77
+ const OWNER_IN_B: ObjectID = user("0d");
78
+ const MEMBER_WITH_BLOCK_IN_A: ObjectID = user("0e");
79
+ const MEMBER_WITH_LABELLED_BLOCK_IN_A: ObjectID = user("0f");
80
+ const LABEL_SCOPED_ELSEWHERE_IN_A: ObjectID = user("10");
81
+ const LABEL_SCOPED_TO_PAGE_IN_A: ObjectID = user("11");
82
+ const OWNED_SCOPE_NOT_OWNER_IN_A: ObjectID = user("12");
83
+ const OWNED_SCOPE_OWNER_IN_A: ObjectID = user("13");
84
+
85
+ // Users recorded as owners of STATUS_PAGE_IN_A.
86
+ const PAGE_OWNERS: Array<string> = [OWNED_SCOPE_OWNER_IN_A.toString()];
87
+
88
+ const TARGET_EMAIL: string = "someone@example.com";
89
+
90
+ type Grant = {
91
+ permission: Permission;
92
+ isBlockPermission?: boolean;
93
+ labelIds?: Array<ObjectID>;
94
+ scope?: PermissionScope;
95
+ };
96
+
97
+ // userId -> projectId -> grants. A missing entry means "not a member".
98
+ const MEMBERSHIPS: Dictionary<Dictionary<Array<Grant>>> = {
99
+ [EDITOR_IN_A.toString()]: {
100
+ [PROJECT_A.toString()]: [{ permission: Permission.StatusPageAdmin }],
101
+ },
102
+ [VIEWER_IN_A.toString()]: {
103
+ [PROJECT_A.toString()]: [
104
+ { permission: Permission.Viewer },
105
+ { permission: Permission.StatusPageViewer },
106
+ { permission: Permission.ReadProjectStatusPage },
107
+ ],
108
+ },
109
+ [BLOCK_ONLY_IN_A.toString()]: {
110
+ [PROJECT_A.toString()]: [
111
+ { permission: Permission.ReadProjectStatusPage },
112
+ {
113
+ permission: Permission.EditProjectStatusPage,
114
+ isBlockPermission: true,
115
+ },
116
+ ],
117
+ },
118
+ [OWNER_IN_B.toString()]: {
119
+ [PROJECT_B.toString()]: [{ permission: Permission.ProjectOwner }],
120
+ },
121
+ // A broad role, with status page editing taken away by a team block row.
122
+ [MEMBER_WITH_BLOCK_IN_A.toString()]: {
123
+ [PROJECT_A.toString()]: [
124
+ { permission: Permission.ProjectMember },
125
+ {
126
+ permission: Permission.EditProjectStatusPage,
127
+ isBlockPermission: true,
128
+ },
129
+ ],
130
+ },
131
+ // The same, but the block only covers pages carrying PAGE_LABEL.
132
+ [MEMBER_WITH_LABELLED_BLOCK_IN_A.toString()]: {
133
+ [PROJECT_A.toString()]: [
134
+ { permission: Permission.ProjectMember },
135
+ {
136
+ permission: Permission.EditProjectStatusPage,
137
+ isBlockPermission: true,
138
+ labelIds: [PAGE_LABEL],
139
+ },
140
+ ],
141
+ },
142
+ [LABEL_SCOPED_ELSEWHERE_IN_A.toString()]: {
143
+ [PROJECT_A.toString()]: [
144
+ {
145
+ permission: Permission.StatusPageMember,
146
+ labelIds: [OTHER_LABEL],
147
+ scope: PermissionScope.Labels,
148
+ },
149
+ ],
150
+ },
151
+ [LABEL_SCOPED_TO_PAGE_IN_A.toString()]: {
152
+ [PROJECT_A.toString()]: [
153
+ {
154
+ permission: Permission.StatusPageMember,
155
+ labelIds: [PAGE_LABEL],
156
+ scope: PermissionScope.Labels,
157
+ },
158
+ ],
159
+ },
160
+ [OWNED_SCOPE_NOT_OWNER_IN_A.toString()]: {
161
+ [PROJECT_A.toString()]: [
162
+ { permission: Permission.StatusPageMember, scope: PermissionScope.Owned },
163
+ ],
164
+ },
165
+ [OWNED_SCOPE_OWNER_IN_A.toString()]: {
166
+ [PROJECT_A.toString()]: [
167
+ { permission: Permission.StatusPageMember, scope: PermissionScope.Owned },
168
+ ],
169
+ },
170
+ };
171
+
172
+ function tenantPermissionFor(
173
+ userId: ObjectID,
174
+ projectId: ObjectID,
175
+ ): UserTenantAccessPermission | null {
176
+ const grants: Array<Grant> | undefined =
177
+ MEMBERSHIPS[userId.toString()]?.[projectId.toString()];
178
+
179
+ if (!grants) {
180
+ return null;
181
+ }
182
+
183
+ return {
184
+ _type: "UserTenantAccessPermission",
185
+ projectId,
186
+ permissions: grants.map((grant: Grant) => {
187
+ return {
188
+ _type: "UserPermission",
189
+ permission: grant.permission,
190
+ labelIds: grant.labelIds || [],
191
+ isBlockPermission: grant.isBlockPermission || false,
192
+ scope: grant.scope,
193
+ };
194
+ }),
195
+ };
196
+ }
197
+
198
+ function tokenFor(userId: ObjectID): string {
199
+ return `token-${userId.toString()}`;
200
+ }
201
+
202
+ /*
203
+ * Whether a serialized query condition admits `value`. Understands exactly the
204
+ * shapes the permission layer produces for this route (plain ids, Equal, In,
205
+ * And, and Raw operators whose bound parameters hold the permitted values) and
206
+ * throws on anything else, so a test cannot pass because the fake silently
207
+ * ignored a narrowing it did not recognise.
208
+ */
209
+ function conditionAdmits(condition: unknown, value: string): boolean {
210
+ if (condition === undefined) {
211
+ return true;
212
+ }
213
+
214
+ if (typeof condition === "string" || condition instanceof ObjectID) {
215
+ return condition.toString() === value;
216
+ }
217
+
218
+ if (condition instanceof FindOperator) {
219
+ const operator: FindOperator<unknown> = condition as FindOperator<unknown>;
220
+
221
+ switch (operator.type) {
222
+ case "and":
223
+ return (operator.value as unknown as Array<unknown>).every(
224
+ (child: unknown) => {
225
+ return conditionAdmits(child, value);
226
+ },
227
+ );
228
+ case "equal":
229
+ return String(operator.value) === value;
230
+ case "in":
231
+ return (operator.value as unknown as Array<unknown>)
232
+ .map(String)
233
+ .includes(value);
234
+ case "raw":
235
+ return Object.values(operator.objectLiteralParameters || {})
236
+ .flat()
237
+ .map(String)
238
+ .includes(value);
239
+ default:
240
+ break;
241
+ }
242
+ }
243
+
244
+ throw new Error(
245
+ `Unexpected query condition in test fake: ${JSON.stringify(condition)}`,
246
+ );
247
+ }
248
+
249
+ function queryMatchesPage(
250
+ query: Dictionary<unknown>,
251
+ page: StatusPage,
252
+ ): boolean {
253
+ for (const key of Object.keys(query)) {
254
+ if (key !== "_id" && key !== "projectId" && key !== "labels") {
255
+ throw new Error(`Unexpected query key in test fake: ${key}`);
256
+ }
257
+ }
258
+
259
+ if (!conditionAdmits(query["_id"], page.id!.toString())) {
260
+ return false;
261
+ }
262
+
263
+ if (!conditionAdmits(query["projectId"], page.projectId!.toString())) {
264
+ return false;
265
+ }
266
+
267
+ const labelCondition: unknown = query["labels"];
268
+
269
+ if (labelCondition === undefined) {
270
+ return true;
271
+ }
272
+
273
+ const pageLabelIds: Array<string> = (page.labels || []).map(
274
+ (label: Label) => {
275
+ return label.id!.toString();
276
+ },
277
+ );
278
+
279
+ if (Array.isArray(labelCondition)) {
280
+ return labelCondition.some((labelId: unknown) => {
281
+ return pageLabelIds.includes(String(labelId));
282
+ });
283
+ }
284
+
285
+ const labelIdCondition: unknown =
286
+ labelCondition instanceof FindOperator
287
+ ? labelCondition
288
+ : (labelCondition as Dictionary<unknown>)["_id"];
289
+
290
+ return pageLabelIds.some((labelId: string) => {
291
+ return conditionAdmits(labelIdCondition, labelId);
292
+ });
293
+ }
294
+
295
+ type HttpResult = { status: number; body: unknown };
296
+
297
+ function post(data: {
298
+ port: number;
299
+ headers: http.OutgoingHttpHeaders;
300
+ body: unknown;
301
+ }): Promise<HttpResult> {
302
+ return new Promise<HttpResult>(
303
+ (resolve: (result: HttpResult) => void, reject: (e: Error) => void) => {
304
+ const payload: string = JSON.stringify(data.body);
305
+ const request: http.ClientRequest = http.request(
306
+ {
307
+ host: "127.0.0.1",
308
+ port: data.port,
309
+ path: "/api/status-page/test-email-report",
310
+ method: "POST",
311
+ headers: {
312
+ "content-type": "application/json",
313
+ "content-length": Buffer.byteLength(payload),
314
+ ...data.headers,
315
+ },
316
+ },
317
+ (response: http.IncomingMessage) => {
318
+ const chunks: Array<Buffer> = [];
319
+ response.on("data", (chunk: Buffer) => {
320
+ chunks.push(chunk);
321
+ });
322
+ response.on("end", () => {
323
+ const raw: string = Buffer.concat(chunks).toString("utf8");
324
+ let body: unknown = raw;
325
+ try {
326
+ body = raw ? JSON.parse(raw) : null;
327
+ } catch {
328
+ // keep the raw text
329
+ }
330
+ resolve({ status: response.statusCode || 0, body });
331
+ });
332
+ },
333
+ );
334
+ request.on("error", reject);
335
+ request.write(payload);
336
+ request.end();
337
+ },
338
+ );
339
+ }
340
+
341
+ describe("POST /status-page/test-email-report authorization", () => {
342
+ let server: http.Server;
343
+ let port: number;
344
+ let sendEmailReport: jest.SpyInstance;
345
+
346
+ function send(data: {
347
+ userId?: ObjectID | undefined;
348
+ tenantId?: ObjectID | undefined;
349
+ statusPageId?: ObjectID | undefined;
350
+ headers?: http.OutgoingHttpHeaders | undefined;
351
+ }): Promise<HttpResult> {
352
+ const headers: http.OutgoingHttpHeaders = { ...(data.headers || {}) };
353
+
354
+ if (data.userId) {
355
+ headers["authorization"] = `Bearer ${tokenFor(data.userId)}`;
356
+ }
357
+
358
+ if (data.tenantId) {
359
+ headers["tenantid"] = data.tenantId.toString();
360
+ }
361
+
362
+ return post({
363
+ port,
364
+ headers,
365
+ body: {
366
+ statusPageId: (data.statusPageId || STATUS_PAGE_IN_A).toString(),
367
+ email: TARGET_EMAIL,
368
+ },
369
+ });
370
+ }
371
+
372
+ function expectRejected(result: HttpResult, status: number): void {
373
+ expect(result.status).toBe(status);
374
+ expect(sendEmailReport).not.toHaveBeenCalled();
375
+ }
376
+
377
+ function expectSent(result: HttpResult): void {
378
+ expect(result.status).toBe(200);
379
+ expect(sendEmailReport).toHaveBeenCalledTimes(1);
380
+
381
+ const args: { email?: Email | undefined; statusPageId: ObjectID } =
382
+ sendEmailReport.mock.calls[0]![0];
383
+ expect(args.statusPageId.toString()).toBe(STATUS_PAGE_IN_A.toString());
384
+ expect(args.email?.toString()).toBe(TARGET_EMAIL);
385
+ }
386
+
387
+ beforeAll(async () => {
388
+ const app: express.Express = express();
389
+ app.use(express.json());
390
+ app.use("/api", new StatusPageAPI().getRouter());
391
+ app.use(expressErrorHandler);
392
+
393
+ server = http.createServer(app);
394
+ await new Promise<void>((resolve: () => void) => {
395
+ server.listen(0, "127.0.0.1", resolve);
396
+ });
397
+ port = (server.address() as AddressInfo).port;
398
+ });
399
+
400
+ afterAll(async () => {
401
+ jest.restoreAllMocks();
402
+ await new Promise<void>((resolve: () => void) => {
403
+ server.close(() => {
404
+ resolve();
405
+ });
406
+ });
407
+ });
408
+
409
+ beforeEach(() => {
410
+ jest.restoreAllMocks();
411
+ jest.spyOn(logger, "error").mockImplementation((): void => {});
412
+
413
+ jest
414
+ .spyOn(JSONWebToken, "decode")
415
+ .mockImplementation((token: string): JSONWebTokenData => {
416
+ const userId: string = token.replace(/^token-/, "");
417
+
418
+ if (!MEMBERSHIPS[userId]) {
419
+ throw new Error("invalid token");
420
+ }
421
+
422
+ return {
423
+ userId: new ObjectID(userId),
424
+ email: new Email(`${userId}@example.com`),
425
+ isMasterAdmin: false,
426
+ isGlobalLogin: true,
427
+ };
428
+ });
429
+
430
+ jest
431
+ .spyOn(AccessTokenService, "getUserGlobalAccessPermission")
432
+ .mockImplementation(
433
+ async (userId: ObjectID): Promise<UserGlobalAccessPermission> => {
434
+ return {
435
+ _type: "UserGlobalAccessPermission",
436
+ projectIds: Object.keys(MEMBERSHIPS[userId.toString()] || {}).map(
437
+ (id: string) => {
438
+ return new ObjectID(id);
439
+ },
440
+ ),
441
+ // What every signed-in user carries; none of it is a project grant.
442
+ globalPermissions: [
443
+ Permission.Public,
444
+ Permission.User,
445
+ Permission.CurrentUser,
446
+ ],
447
+ };
448
+ },
449
+ );
450
+
451
+ jest
452
+ .spyOn(UserMiddleware, "getUserTenantAccessPermissionWithTenantId")
453
+ .mockImplementation(
454
+ async (data: {
455
+ tenantId: ObjectID;
456
+ userId: ObjectID;
457
+ }): Promise<UserTenantAccessPermission | null> => {
458
+ return tenantPermissionFor(data.userId, data.tenantId);
459
+ },
460
+ );
461
+
462
+ jest
463
+ .spyOn(UserMiddleware, "getUserTenantAccessPermissionForMultiTenant")
464
+ .mockImplementation(
465
+ async (
466
+ _req: unknown,
467
+ userId: ObjectID,
468
+ projectIds: Array<ObjectID>,
469
+ ): Promise<Dictionary<UserTenantAccessPermission> | null> => {
470
+ const result: Dictionary<UserTenantAccessPermission> = {};
471
+
472
+ for (const projectId of projectIds) {
473
+ const permission: UserTenantAccessPermission | null =
474
+ tenantPermissionFor(userId, projectId);
475
+
476
+ if (permission) {
477
+ result[projectId.toString()] = permission;
478
+ }
479
+ }
480
+
481
+ return result;
482
+ },
483
+ );
484
+
485
+ jest.spyOn(TeamMemberService, "getTeamIdsForUser").mockResolvedValue([]);
486
+ jest
487
+ .spyOn(UserService, "updateLastActive")
488
+ .mockResolvedValue(undefined as never);
489
+ jest
490
+ .spyOn(ProjectService, "updateLastActive")
491
+ .mockResolvedValue(undefined as never);
492
+ jest
493
+ .spyOn(ProjectService, "getCurrentPlan")
494
+ .mockResolvedValue({ plan: null, isSubscriptionUnpaid: false });
495
+
496
+ // Ownership lookups made by the Owned permission scope.
497
+ jest
498
+ .spyOn(StatusPageOwnerUserService, "findBy")
499
+ .mockImplementation(async (findBy: any): Promise<Array<any>> => {
500
+ const userId: string = findBy.query.userId.toString();
501
+
502
+ if (!PAGE_OWNERS.includes(userId)) {
503
+ return [];
504
+ }
505
+
506
+ const owner: StatusPageOwnerUser = new StatusPageOwnerUser();
507
+ owner.statusPageId = STATUS_PAGE_IN_A;
508
+ return [owner];
509
+ });
510
+ jest.spyOn(StatusPageOwnerTeamService, "findBy").mockResolvedValue([]);
511
+
512
+ const buildPage: () => StatusPage = (): StatusPage => {
513
+ const statusPage: StatusPage = new StatusPage();
514
+ statusPage.id = STATUS_PAGE_IN_A;
515
+ statusPage.projectId = PROJECT_A;
516
+ const label: Label = new Label();
517
+ label.id = PAGE_LABEL;
518
+ statusPage.labels = [label];
519
+ return statusPage;
520
+ };
521
+
522
+ jest
523
+ .spyOn(StatusPageService, "findOneById")
524
+ .mockImplementation(async (findBy: any): Promise<StatusPage | null> => {
525
+ if (findBy.id.toString() !== STATUS_PAGE_IN_A.toString()) {
526
+ return null;
527
+ }
528
+
529
+ return buildPage();
530
+ });
531
+
532
+ // The permission-narrowed existence check.
533
+ jest
534
+ .spyOn(StatusPageService, "findOneBy")
535
+ .mockImplementation(async (findBy: any): Promise<StatusPage | null> => {
536
+ const page: StatusPage = buildPage();
537
+ return queryMatchesPage(findBy.query, page) ? page : null;
538
+ });
539
+
540
+ sendEmailReport = jest
541
+ .spyOn(StatusPageService, "sendEmailReport")
542
+ .mockResolvedValue(undefined);
543
+ });
544
+
545
+ describe("authentication", () => {
546
+ it("rejects an anonymous caller with 401 and sends no email", async () => {
547
+ expectRejected(await send({ tenantId: PROJECT_A }), 401);
548
+ expect(StatusPageService.findOneById).not.toHaveBeenCalled();
549
+ });
550
+
551
+ it("rejects an anonymous caller with no tenant header with 401", async () => {
552
+ expectRejected(await send({}), 401);
553
+ });
554
+ });
555
+
556
+ describe("project membership", () => {
557
+ it("rejects a member of another project with 422 and sends no email", async () => {
558
+ expectRejected(
559
+ await send({ userId: OWNER_IN_B, tenantId: PROJECT_B }),
560
+ 422,
561
+ );
562
+ });
563
+
564
+ it("rejects a member of another project who names the owning project in the tenant header", async () => {
565
+ expectRejected(
566
+ await send({ userId: OWNER_IN_B, tenantId: PROJECT_A }),
567
+ 422,
568
+ );
569
+ });
570
+
571
+ it("rejects a status page that does not exist the same way as a foreign one", async () => {
572
+ expectRejected(
573
+ await send({
574
+ userId: EDITOR_IN_A,
575
+ tenantId: PROJECT_A,
576
+ statusPageId: MISSING_STATUS_PAGE,
577
+ }),
578
+ 422,
579
+ );
580
+ });
581
+ });
582
+
583
+ describe("status page edit permission", () => {
584
+ it("rejects a member of the project who cannot edit status pages with 422", async () => {
585
+ expectRejected(
586
+ await send({ userId: VIEWER_IN_A, tenantId: PROJECT_A }),
587
+ 422,
588
+ );
589
+ });
590
+
591
+ it("does not count a blocked edit permission as a grant", async () => {
592
+ expectRejected(
593
+ await send({ userId: BLOCK_ONLY_IN_A, tenantId: PROJECT_A }),
594
+ 422,
595
+ );
596
+ });
597
+
598
+ it("honours a team block on status page editing over a broader role", async () => {
599
+ expectRejected(
600
+ await send({ userId: MEMBER_WITH_BLOCK_IN_A, tenantId: PROJECT_A }),
601
+ 422,
602
+ );
603
+ });
604
+
605
+ it("honours a label-restricted block that covers this page", async () => {
606
+ expectRejected(
607
+ await send({
608
+ userId: MEMBER_WITH_LABELLED_BLOCK_IN_A,
609
+ tenantId: PROJECT_A,
610
+ }),
611
+ 422,
612
+ );
613
+ });
614
+
615
+ it("rejects an edit grant restricted to labels this page does not carry", async () => {
616
+ expectRejected(
617
+ await send({
618
+ userId: LABEL_SCOPED_ELSEWHERE_IN_A,
619
+ tenantId: PROJECT_A,
620
+ }),
621
+ 422,
622
+ );
623
+ });
624
+
625
+ it("rejects an Owned-scoped edit grant for a page the caller does not own", async () => {
626
+ expectRejected(
627
+ await send({
628
+ userId: OWNED_SCOPE_NOT_OWNER_IN_A,
629
+ tenantId: PROJECT_A,
630
+ }),
631
+ 422,
632
+ );
633
+ });
634
+
635
+ it("applies the same checks when the multi-tenant header is sent", async () => {
636
+ expectRejected(
637
+ await send({
638
+ userId: OWNED_SCOPE_NOT_OWNER_IN_A,
639
+ tenantId: PROJECT_A,
640
+ headers: { "is-multi-tenant-query": "true" },
641
+ }),
642
+ 422,
643
+ );
644
+ });
645
+ });
646
+
647
+ describe("permitted callers", () => {
648
+ it("sends the report once for a member permitted to edit the status page", async () => {
649
+ expectSent(await send({ userId: EDITOR_IN_A, tenantId: PROJECT_A }));
650
+ });
651
+
652
+ it("sends the report for an edit grant restricted to a label this page carries", async () => {
653
+ expectSent(
654
+ await send({ userId: LABEL_SCOPED_TO_PAGE_IN_A, tenantId: PROJECT_A }),
655
+ );
656
+ });
657
+
658
+ it("sends the report for an Owned-scoped edit grant on a page the caller owns", async () => {
659
+ expectSent(
660
+ await send({ userId: OWNED_SCOPE_OWNER_IN_A, tenantId: PROJECT_A }),
661
+ );
662
+ });
663
+ });
664
+ });
@@ -242,6 +242,13 @@ describe("POST /team-member/remove-user-from-project", () => {
242
242
  );
243
243
  });
244
244
 
245
+ /*
246
+ * The handler's own check, called directly. On the router an anonymous
247
+ * request (an expired session) never gets this far any more:
248
+ * requireUserAuthentication answers it with a 401 first, which is pinned
249
+ * in ExpiredSessionRouteGuards.test.ts. This stays as defence in depth for
250
+ * a credentialed caller with no user id, such as a project API key.
251
+ */
245
252
  test("rejects an unauthenticated caller without deleting anything", async () => {
246
253
  await callRoute({ body: { userId: targetUserId.toString() } });
247
254
 
@@ -157,6 +157,12 @@ describe("UserIncomingCallNumberAPI", () => {
157
157
  expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
158
158
  });
159
159
 
160
+ /*
161
+ * The handler's own check, reached directly. Through the router, a
162
+ * request with no session is answered 401 by requireUserAuthentication
163
+ * before the limiter or this handler runs (see
164
+ * ExpiredSessionRouteGuards.test.ts); this pins the defence in depth.
165
+ */
160
166
  it("should reject when the request carries no user authorization", async () => {
161
167
  mockRequest.body = { itemId: ITEM_ID };
162
168
  UserIncomingCallNumberService.findOneById = jest