@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,615 @@
1
+ jest.mock("../../../Server/Infrastructure/Redis", () => {
2
+ return {
3
+ __esModule: true,
4
+ default: {
5
+ getClient: jest.fn().mockReturnValue(null),
6
+ isConnected: jest.fn().mockReturnValue(false),
7
+ },
8
+ };
9
+ });
10
+
11
+ jest.mock("../../../Server/Utils/Logger", () => {
12
+ return {
13
+ __esModule: true,
14
+ default: {
15
+ debug: jest.fn(),
16
+ info: jest.fn(),
17
+ warn: jest.fn(),
18
+ error: jest.fn(),
19
+ },
20
+ getLogAttributesFromRequest: jest.fn().mockReturnValue({}),
21
+ };
22
+ });
23
+
24
+ import BaseAPI from "../../../Server/API/BaseAPI";
25
+ import DashboardAPI from "../../../Server/API/DashboardAPI";
26
+ import StatusPageAPI from "../../../Server/API/StatusPageAPI";
27
+ import { EncryptionSecret } from "../../../Server/EnvironmentConfig";
28
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
29
+ import DashboardService from "../../../Server/Services/DashboardService";
30
+ import StatusPageResourceService from "../../../Server/Services/StatusPageResourceService";
31
+ import StatusPageService from "../../../Server/Services/StatusPageService";
32
+ import CookieUtil from "../../../Server/Utils/Cookie";
33
+ import { ExpressRouter } from "../../../Server/Utils/Express";
34
+ import JSONWebToken from "../../../Server/Utils/JsonWebToken";
35
+ import { expressErrorHandler } from "../../../Server/Utils/StartServer";
36
+ import Dashboard from "../../../Models/DatabaseModels/Dashboard";
37
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
38
+ import {
39
+ DASHBOARD_MASTER_PASSWORD_COOKIE_IDENTIFIER,
40
+ DASHBOARD_MASTER_PASSWORD_REQUIRED_MESSAGE,
41
+ } from "../../../Types/Dashboard/MasterPassword";
42
+ import Dictionary from "../../../Types/Dictionary";
43
+ import HashedString from "../../../Types/HashedString";
44
+ import { JSONObject } from "../../../Types/JSON";
45
+ import ObjectID from "../../../Types/ObjectID";
46
+ import { afterAll, beforeAll, describe, expect, it } from "@jest/globals";
47
+ import CookieParser from "cookie-parser";
48
+ import express from "express";
49
+ import http from "http";
50
+ import jwt from "jsonwebtoken";
51
+ import { AddressInfo } from "net";
52
+
53
+ /*
54
+ * The public dashboard and public status page routes, driven through a real
55
+ * Express app with cookie-parser and the production error handler, carrying
56
+ * the host-wide dashboard access-token cookie after it has stopped decoding.
57
+ *
58
+ * That cookie reaches these routes whenever the page is served from the
59
+ * dashboard's host, and it outlives its usefulness whenever the encryption
60
+ * secret is rotated inside its Max-Age. getUserMiddleware answered it with
61
+ * 401 "AccessToken is invalid or expired" before the route asked whether the
62
+ * dashboard was public at all; the public clients cannot refresh a dashboard
63
+ * session, so that 401 became a login redirect - a reload loop on the
64
+ * preview route, and a fresh master-password prompt for a viewer who had
65
+ * already entered it.
66
+ *
67
+ * Here those routes must answer the stale cookie exactly as they answer no
68
+ * cookie, and the authenticated routes mounted beside them must still 401.
69
+ * Only the data layer is stubbed; tokens are real JWTs.
70
+ */
71
+
72
+ const INVALID_ACCESS_TOKEN_MESSAGE: string =
73
+ "AccessToken is invalid or expired. Please refresh your token.";
74
+
75
+ const PUBLIC_DASHBOARD_ID: ObjectID = new ObjectID(
76
+ "d0000000-0000-4000-8000-000000000001",
77
+ );
78
+ const PROTECTED_DASHBOARD_ID: ObjectID = new ObjectID(
79
+ "d0000000-0000-4000-8000-000000000002",
80
+ );
81
+ const PRIVATE_DASHBOARD_ID: ObjectID = new ObjectID(
82
+ "d0000000-0000-4000-8000-000000000003",
83
+ );
84
+ const PUBLIC_STATUS_PAGE_ID: ObjectID = new ObjectID(
85
+ "50000000-0000-4000-8000-000000000001",
86
+ );
87
+
88
+ /*
89
+ * Well-formed and inside its lifetime, but signed with a secret this instance
90
+ * no longer uses: what every signed-in browser holds right after a rotation.
91
+ */
92
+ const STALE_ACCESS_TOKEN: string = jwt.sign(
93
+ {
94
+ userId: "33333333-3333-4333-8333-333333333333",
95
+ email: "viewer@example.com",
96
+ name: "Viewer",
97
+ isMasterAdmin: false,
98
+ isGlobalLogin: true,
99
+ sessionId: "44444444-4444-4444-8444-444444444444",
100
+ },
101
+ `${EncryptionSecret.toString()}-before-rotation`,
102
+ { expiresIn: 15 * 60 },
103
+ );
104
+
105
+ const STALE_SESSION_COOKIE: Dictionary<string> = {
106
+ [CookieUtil.getUserTokenKey()]: STALE_ACCESS_TOKEN,
107
+ };
108
+
109
+ const masterPasswordCookie: (dashboardId: ObjectID) => Dictionary<string> = (
110
+ dashboardId: ObjectID,
111
+ ): Dictionary<string> => {
112
+ return {
113
+ [CookieUtil.getDashboardMasterPasswordKey(dashboardId)]:
114
+ JSONWebToken.signJsonPayload(
115
+ {
116
+ dashboardId: dashboardId.toString(),
117
+ type: DASHBOARD_MASTER_PASSWORD_COOKIE_IDENTIFIER,
118
+ },
119
+ 60 * 60,
120
+ ),
121
+ };
122
+ };
123
+
124
+ const buildDashboard: (data: {
125
+ id: ObjectID;
126
+ isPublicDashboard: boolean;
127
+ enableMasterPassword?: boolean;
128
+ }) => Dashboard = (data: {
129
+ id: ObjectID;
130
+ isPublicDashboard: boolean;
131
+ enableMasterPassword?: boolean;
132
+ }): Dashboard => {
133
+ const dashboard: Dashboard = new Dashboard();
134
+ dashboard.id = data.id;
135
+ dashboard.name = `Dashboard ${data.id.toString()}`;
136
+ dashboard.isPublicDashboard = data.isPublicDashboard;
137
+ dashboard.enableMasterPassword = Boolean(data.enableMasterPassword);
138
+
139
+ if (data.enableMasterPassword) {
140
+ dashboard.masterPassword = new HashedString("stored-hash", true);
141
+ }
142
+
143
+ return dashboard;
144
+ };
145
+
146
+ const DASHBOARDS: Dictionary<Dashboard> = {
147
+ [PUBLIC_DASHBOARD_ID.toString()]: buildDashboard({
148
+ id: PUBLIC_DASHBOARD_ID,
149
+ isPublicDashboard: true,
150
+ }),
151
+ [PROTECTED_DASHBOARD_ID.toString()]: buildDashboard({
152
+ id: PROTECTED_DASHBOARD_ID,
153
+ isPublicDashboard: true,
154
+ enableMasterPassword: true,
155
+ }),
156
+ [PRIVATE_DASHBOARD_ID.toString()]: buildDashboard({
157
+ id: PRIVATE_DASHBOARD_ID,
158
+ isPublicDashboard: false,
159
+ }),
160
+ };
161
+
162
+ type HttpResult = { status: number; body: JSONObject | null };
163
+
164
+ function send(data: {
165
+ port: number;
166
+ method: "GET" | "POST";
167
+ path: string;
168
+ cookies?: Dictionary<string> | undefined;
169
+ body?: JSONObject | undefined;
170
+ }): Promise<HttpResult> {
171
+ return new Promise<HttpResult>(
172
+ (resolve: (result: HttpResult) => void, reject: (e: Error) => void) => {
173
+ const payload: string =
174
+ data.method === "POST" ? JSON.stringify(data.body || {}) : "";
175
+
176
+ const cookieHeader: string = Object.entries(data.cookies || {})
177
+ .map(([name, value]: [string, string]) => {
178
+ return `${name}=${value}`;
179
+ })
180
+ .join("; ");
181
+
182
+ const headers: http.OutgoingHttpHeaders = {
183
+ // What the public dashboard and status page clients send.
184
+ tenantid: "",
185
+ };
186
+
187
+ if (cookieHeader) {
188
+ headers["cookie"] = cookieHeader;
189
+ }
190
+
191
+ if (data.method === "POST") {
192
+ headers["content-type"] = "application/json";
193
+ headers["content-length"] = Buffer.byteLength(payload);
194
+ }
195
+
196
+ const request: http.ClientRequest = http.request(
197
+ {
198
+ host: "127.0.0.1",
199
+ port: data.port,
200
+ path: data.path,
201
+ method: data.method,
202
+ headers,
203
+ },
204
+ (response: http.IncomingMessage) => {
205
+ const chunks: Array<Buffer> = [];
206
+
207
+ response.on("data", (chunk: Buffer) => {
208
+ chunks.push(chunk);
209
+ });
210
+
211
+ response.on("end", () => {
212
+ const raw: string = Buffer.concat(chunks).toString("utf8");
213
+ let body: JSONObject | null = null;
214
+
215
+ try {
216
+ body = raw ? (JSON.parse(raw) as JSONObject) : null;
217
+ } catch {
218
+ body = { raw };
219
+ }
220
+
221
+ resolve({ status: response.statusCode || 0, body });
222
+ });
223
+ },
224
+ );
225
+
226
+ request.on("error", reject);
227
+
228
+ if (payload) {
229
+ request.write(payload);
230
+ }
231
+
232
+ request.end();
233
+ },
234
+ );
235
+ }
236
+
237
+ /*
238
+ * Response.sendErrorResponse (the middleware) writes { message }; the
239
+ * production error handler (a handler's next(err)) writes { error }.
240
+ */
241
+ const errorMessageOf: (result: HttpResult) => unknown = (
242
+ result: HttpResult,
243
+ ): unknown => {
244
+ return result.body?.["message"] ?? result.body?.["error"];
245
+ };
246
+
247
+ type RouteHandler = (...args: Array<unknown>) => unknown;
248
+
249
+ type RouterLayer = {
250
+ route?: {
251
+ path: string;
252
+ methods: Dictionary<boolean>;
253
+ stack: Array<{ handle: RouteHandler }>;
254
+ };
255
+ };
256
+
257
+ type RegisteredRoute = {
258
+ key: string;
259
+ handlers: Array<RouteHandler>;
260
+ };
261
+
262
+ // The routes an Express router has registered, as "METHOD path" + handlers.
263
+ const listRoutes: (router: ExpressRouter) => Array<RegisteredRoute> = (
264
+ router: ExpressRouter,
265
+ ): Array<RegisteredRoute> => {
266
+ const layers: Array<RouterLayer> = (
267
+ router as unknown as { stack: Array<RouterLayer> }
268
+ ).stack;
269
+
270
+ const routes: Array<RegisteredRoute> = [];
271
+
272
+ for (const layer of layers) {
273
+ if (!layer.route) {
274
+ continue;
275
+ }
276
+
277
+ for (const method of Object.keys(layer.route.methods)) {
278
+ routes.push({
279
+ key: `${method.toUpperCase()} ${layer.route.path}`,
280
+ handlers: layer.route.stack.map((entry: { handle: RouteHandler }) => {
281
+ return entry.handle;
282
+ }),
283
+ });
284
+ }
285
+ }
286
+
287
+ return routes;
288
+ };
289
+
290
+ describe("public routes with an access-token cookie that no longer decodes", () => {
291
+ let server: http.Server;
292
+ let port: number;
293
+
294
+ beforeAll(async () => {
295
+ jest
296
+ .spyOn(DashboardService, "findOneById")
297
+ .mockImplementation(async (data: { id: ObjectID }) => {
298
+ return DASHBOARDS[data.id.toString()] || null;
299
+ });
300
+
301
+ jest
302
+ .spyOn(StatusPageService, "findOneById")
303
+ .mockImplementation(async (data: { id: ObjectID }) => {
304
+ if (data.id.toString() !== PUBLIC_STATUS_PAGE_ID.toString()) {
305
+ return null;
306
+ }
307
+
308
+ const statusPage: StatusPage = new StatusPage();
309
+ statusPage.id = PUBLIC_STATUS_PAGE_ID;
310
+ statusPage.isPublicStatusPage = true;
311
+ return statusPage;
312
+ });
313
+
314
+ jest.spyOn(StatusPageResourceService, "findBy").mockResolvedValue([]);
315
+
316
+ const app: express.Express = express();
317
+ app.use(CookieParser());
318
+ app.use(express.json());
319
+ app.use("/api", new DashboardAPI().getRouter());
320
+ app.use("/api", new StatusPageAPI().getRouter());
321
+ app.use(expressErrorHandler);
322
+
323
+ server = http.createServer(app);
324
+
325
+ await new Promise<void>((resolve: () => void) => {
326
+ server.listen(0, "127.0.0.1", resolve);
327
+ });
328
+
329
+ port = (server.address() as AddressInfo).port;
330
+ });
331
+
332
+ afterAll(async () => {
333
+ await new Promise<void>((resolve: () => void) => {
334
+ server.close(() => {
335
+ resolve();
336
+ });
337
+ });
338
+
339
+ jest.restoreAllMocks();
340
+ });
341
+
342
+ describe("public dashboard routes", () => {
343
+ it("serves a public dashboard's overview", async () => {
344
+ const result: HttpResult = await send({
345
+ port,
346
+ method: "GET",
347
+ path: `/api/dashboard/overview/${PUBLIC_DASHBOARD_ID.toString()}`,
348
+ cookies: STALE_SESSION_COOKIE,
349
+ });
350
+
351
+ expect(result.status).toBe(200);
352
+ expect(result.body?.["_id"]).toBe(PUBLIC_DASHBOARD_ID.toString());
353
+ expect(result.body?.["name"]).toBe(
354
+ `Dashboard ${PUBLIC_DASHBOARD_ID.toString()}`,
355
+ );
356
+ });
357
+
358
+ it("serves a public dashboard's metadata", async () => {
359
+ const result: HttpResult = await send({
360
+ port,
361
+ method: "POST",
362
+ path: `/api/dashboard/metadata/${PUBLIC_DASHBOARD_ID.toString()}`,
363
+ cookies: STALE_SESSION_COOKIE,
364
+ });
365
+
366
+ expect(result.status).toBe(200);
367
+ expect(result.body?.["_id"]).toBe(PUBLIC_DASHBOARD_ID.toString());
368
+ expect(result.body?.["isPublicDashboard"]).toBe(true);
369
+ });
370
+
371
+ /*
372
+ * The master-password case from the report: the viewer already entered
373
+ * the password, and the stale session cookie must not send them back to
374
+ * the prompt.
375
+ */
376
+ it("serves a protected dashboard to a viewer holding its master-password cookie", async () => {
377
+ const result: HttpResult = await send({
378
+ port,
379
+ method: "GET",
380
+ path: `/api/dashboard/overview/${PROTECTED_DASHBOARD_ID.toString()}`,
381
+ cookies: {
382
+ ...STALE_SESSION_COOKIE,
383
+ ...masterPasswordCookie(PROTECTED_DASHBOARD_ID),
384
+ },
385
+ });
386
+
387
+ expect(result.status).toBe(200);
388
+ expect(result.body?.["_id"]).toBe(PROTECTED_DASHBOARD_ID.toString());
389
+ });
390
+
391
+ /*
392
+ * Anonymous is not "let in". Access is still the dashboard's own
393
+ * decision, and a refusal names the dashboard's reason rather than the
394
+ * stale session's.
395
+ */
396
+ it.each([
397
+ [
398
+ "asks for the master password on a protected dashboard",
399
+ PROTECTED_DASHBOARD_ID,
400
+ DASHBOARD_MASTER_PASSWORD_REQUIRED_MESSAGE,
401
+ ],
402
+ [
403
+ "refuses a dashboard that is not public",
404
+ PRIVATE_DASHBOARD_ID,
405
+ "This dashboard is not available.",
406
+ ],
407
+ ])(
408
+ "%s, for the dashboard's reason",
409
+ async (_label: string, dashboardId: ObjectID, message: string) => {
410
+ const result: HttpResult = await send({
411
+ port,
412
+ method: "GET",
413
+ path: `/api/dashboard/overview/${dashboardId.toString()}`,
414
+ cookies: STALE_SESSION_COOKIE,
415
+ });
416
+
417
+ expect(result.status).toBe(401);
418
+ expect(errorMessageOf(result)).toBe(message);
419
+ },
420
+ );
421
+
422
+ /*
423
+ * The whole contract in one assertion: to a public route, a session
424
+ * cookie that no longer decodes is the same as no session cookie.
425
+ */
426
+ it.each([
427
+ ["GET", `/api/dashboard/overview/${PUBLIC_DASHBOARD_ID.toString()}`],
428
+ ["POST", `/api/dashboard/metadata/${PUBLIC_DASHBOARD_ID.toString()}`],
429
+ ["GET", `/api/dashboard/overview/${PROTECTED_DASHBOARD_ID.toString()}`],
430
+ ["GET", `/api/dashboard/overview/${PRIVATE_DASHBOARD_ID.toString()}`],
431
+ ] as Array<["GET" | "POST", string]>)(
432
+ "answers %s %s exactly as it answers a request with no session cookie",
433
+ async (method: "GET" | "POST", path: string) => {
434
+ const withStaleCookie: HttpResult = await send({
435
+ port,
436
+ method,
437
+ path,
438
+ cookies: STALE_SESSION_COOKIE,
439
+ });
440
+ const withoutCookie: HttpResult = await send({ port, method, path });
441
+
442
+ expect(withStaleCookie).toEqual(withoutCookie);
443
+ },
444
+ );
445
+ });
446
+
447
+ describe("public status page routes", () => {
448
+ it("serves a public status page's resources", async () => {
449
+ const result: HttpResult = await send({
450
+ port,
451
+ method: "POST",
452
+ path: `/api/status-page/resources/${PUBLIC_STATUS_PAGE_ID.toString()}`,
453
+ cookies: STALE_SESSION_COOKIE,
454
+ });
455
+
456
+ expect(result.status).toBe(200);
457
+ expect(result.body?.["data"]).toEqual([]);
458
+ });
459
+ });
460
+
461
+ /*
462
+ * Mounted on the same routers, reached through the same prefixes. These
463
+ * need to know who is calling, so a dead session is still a 401 - the
464
+ * answer that makes the dashboard client refresh.
465
+ */
466
+ describe("authenticated routes", () => {
467
+ it.each([
468
+ ["the dashboard CRUD list", "/api/dashboard/get-list"],
469
+ ["the status page CRUD list", "/api/status-page/get-list"],
470
+ [
471
+ "the status page test email report",
472
+ "/api/status-page/test-email-report",
473
+ ],
474
+ ])("still answer %s with a 401", async (_label: string, path: string) => {
475
+ const result: HttpResult = await send({
476
+ port,
477
+ method: "POST",
478
+ path,
479
+ cookies: STALE_SESSION_COOKIE,
480
+ body: {
481
+ statusPageId: PUBLIC_STATUS_PAGE_ID.toString(),
482
+ email: "someone@example.com",
483
+ },
484
+ });
485
+
486
+ expect(result.status).toBe(401);
487
+ expect(errorMessageOf(result)).toBe(INVALID_ACCESS_TOKEN_MESSAGE);
488
+ });
489
+ });
490
+ });
491
+
492
+ /*
493
+ * Which middleware each route mounts. The tests above prove the behaviour on
494
+ * a sample; these keep every route on the right side of the line, including
495
+ * ones added later.
496
+ */
497
+ describe("which routes treat an undecodable access token as anonymous", () => {
498
+ type SplitRoutes = {
499
+ custom: Array<RegisteredRoute>;
500
+ inherited: Array<RegisteredRoute>;
501
+ };
502
+
503
+ type SplitRoutesFunction = (data: {
504
+ router: ExpressRouter;
505
+ inheritedRouter: ExpressRouter;
506
+ }) => SplitRoutes;
507
+
508
+ const splitRoutes: SplitRoutesFunction = (data: {
509
+ router: ExpressRouter;
510
+ inheritedRouter: ExpressRouter;
511
+ }): SplitRoutes => {
512
+ const inheritedKeys: Set<string> = new Set(
513
+ listRoutes(data.inheritedRouter).map((route: RegisteredRoute) => {
514
+ return route.key;
515
+ }),
516
+ );
517
+
518
+ const routes: Array<RegisteredRoute> = listRoutes(data.router);
519
+
520
+ return {
521
+ custom: routes.filter((route: RegisteredRoute) => {
522
+ return !inheritedKeys.has(route.key);
523
+ }),
524
+ inherited: routes.filter((route: RegisteredRoute) => {
525
+ return inheritedKeys.has(route.key);
526
+ }),
527
+ };
528
+ };
529
+
530
+ const mountsPublic: (route: RegisteredRoute) => boolean = (
531
+ route: RegisteredRoute,
532
+ ): boolean => {
533
+ return route.handlers.includes(
534
+ UserMiddleware.getPublicRouteUserMiddleware as RouteHandler,
535
+ );
536
+ };
537
+
538
+ const mountsStrict: (route: RegisteredRoute) => boolean = (
539
+ route: RegisteredRoute,
540
+ ): boolean => {
541
+ return route.handlers.includes(
542
+ UserMiddleware.getUserMiddleware as RouteHandler,
543
+ );
544
+ };
545
+
546
+ it("mounts the public variant on every public dashboard route", () => {
547
+ const { custom, inherited }: SplitRoutes = splitRoutes({
548
+ router: new DashboardAPI().getRouter(),
549
+ inheritedRouter: new BaseAPI(Dashboard, DashboardService).getRouter(),
550
+ });
551
+
552
+ expect(custom.length).toBeGreaterThan(0);
553
+
554
+ for (const route of custom) {
555
+ expect([route.key, mountsPublic(route), mountsStrict(route)]).toEqual([
556
+ route.key,
557
+ true,
558
+ false,
559
+ ]);
560
+ }
561
+
562
+ for (const route of inherited) {
563
+ expect([route.key, mountsPublic(route), mountsStrict(route)]).toEqual([
564
+ route.key,
565
+ false,
566
+ true,
567
+ ]);
568
+ }
569
+ });
570
+
571
+ /*
572
+ * The status page has the same exposure: its preview is served from the
573
+ * dashboard's host, so the dashboard cookie rides along, and its public
574
+ * routes decide access from the page's own cookies (StatusPageService
575
+ * .hasReadAccess), never from the dashboard session.
576
+ *
577
+ * test-email-report is the exception - it acts for a signed-in project
578
+ * member and requires authentication - and must stay strict.
579
+ */
580
+ it("mounts the public variant on every public status page route, and only there", () => {
581
+ const { custom, inherited }: SplitRoutes = splitRoutes({
582
+ router: new StatusPageAPI().getRouter(),
583
+ inheritedRouter: new BaseAPI(StatusPage, StatusPageService).getRouter(),
584
+ });
585
+
586
+ const strictCustomRoutes: Array<string> = custom
587
+ .filter(mountsStrict)
588
+ .map((route: RegisteredRoute) => {
589
+ return route.key;
590
+ });
591
+
592
+ expect(strictCustomRoutes).toEqual(["POST /status-page/test-email-report"]);
593
+
594
+ const testEmailReport: RegisteredRoute | undefined = custom.find(
595
+ (route: RegisteredRoute) => {
596
+ return route.key === "POST /status-page/test-email-report";
597
+ },
598
+ );
599
+
600
+ expect(testEmailReport?.handlers).toContain(
601
+ UserMiddleware.requireUserAuthentication as RouteHandler,
602
+ );
603
+ expect(mountsPublic(testEmailReport!)).toBe(false);
604
+
605
+ expect(custom.filter(mountsPublic).length).toBeGreaterThan(0);
606
+
607
+ for (const route of inherited) {
608
+ expect([route.key, mountsPublic(route), mountsStrict(route)]).toEqual([
609
+ route.key,
610
+ false,
611
+ true,
612
+ ]);
613
+ }
614
+ });
615
+ });