@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
@@ -27,6 +27,8 @@ import LlmProvider from "../../../Models/DatabaseModels/LlmProvider";
27
27
  import Project from "../../../Models/DatabaseModels/Project";
28
28
  import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
29
29
  import BadDataException from "../../../Types/Exception/BadDataException";
30
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
31
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
30
32
  import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
31
33
  import Dictionary from "../../../Types/Dictionary";
32
34
  import { JSONObject } from "../../../Types/JSON";
@@ -265,6 +267,24 @@ function withProps(props: DatabaseCommonInteractionProps): void {
265
267
  .mockResolvedValue(props);
266
268
  }
267
269
 
270
+ /*
271
+ * The refusal an anonymous caller must get: 401, not 422. A request with no
272
+ * credentials is almost always a signed-in user whose access-token cookie
273
+ * expired together with its JWT, and the dashboard refreshes the session and
274
+ * replays the request on a 401 and on nothing else. A 422 would strand that
275
+ * user on "You are not authorized to access this project's data."
276
+ */
277
+ function expectAuthenticationRequired(thrown: unknown): void {
278
+ expect(thrown).toBeInstanceOf(NotAuthenticatedException);
279
+ expect(thrown).not.toBeInstanceOf(NotAuthorizedException);
280
+ expect((thrown as NotAuthenticatedException).code).toBe(
281
+ ExceptionCode.NotAuthenticatedException,
282
+ );
283
+ expect((thrown as NotAuthenticatedException).message).toBe(
284
+ CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE,
285
+ );
286
+ }
287
+
268
288
  // A provider row with the exact metadata the advisory says leaked.
269
289
  function victimProvider(): LlmProvider {
270
290
  const provider: LlmProvider = new LlmProvider(PROVIDER_ID);
@@ -441,17 +461,35 @@ describe("POST /ai-chat/providers - cross-tenant disclosure (GHSA-hm7m-9qjj-xj5x
441
461
  });
442
462
  });
443
463
 
464
+ /*
465
+ * Anonymous callers used to get the same 422 as an outsider. They now get
466
+ * 401 (see expectAuthenticationRequired): still a refusal, still before any
467
+ * lookup, but one the browser client answers by refreshing the session
468
+ * instead of showing an authorization error to someone who is signed in.
469
+ */
444
470
  describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
445
- test("an unauthenticated caller (getUserMiddleware admits these as public) is refused", async () => {
471
+ test("an unauthenticated caller (getUserMiddleware admits these as public) is refused with 401, not 422", async () => {
446
472
  withProps(buildProps({ tenantId: PROJECT_B_ID }));
447
473
 
448
474
  const call: RouteCall = await callProviders();
449
475
 
450
- expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
476
+ expectAuthenticationRequired(call.thrown);
477
+ expect(providerLookupsRan()).toBe(false);
478
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
479
+ });
480
+
481
+ test("an explicitly Public caller is anonymous too, and gets 401", async () => {
482
+ withProps(
483
+ buildProps({ tenantId: PROJECT_B_ID, userType: UserType.Public }),
484
+ );
485
+
486
+ const call: RouteCall = await callProviders();
487
+
488
+ expectAuthenticationRequired(call.thrown);
451
489
  expect(providerLookupsRan()).toBe(false);
452
490
  });
453
491
 
454
- test("an anonymous caller carrying a stray tenant permission entry but no user id is refused", async () => {
492
+ test("an anonymous caller carrying a stray tenant permission entry but no user id is refused with 401", async () => {
455
493
  withProps(
456
494
  buildProps({
457
495
  userId: undefined,
@@ -462,7 +500,37 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
462
500
 
463
501
  const call: RouteCall = await callProviders();
464
502
 
465
- expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
503
+ expectAuthenticationRequired(call.thrown);
504
+ expect(providerLookupsRan()).toBe(false);
505
+ });
506
+
507
+ /*
508
+ * "Who are you?" comes before "which project?". An expired session that
509
+ * also lost its tenantid header must still be told to authenticate, or the
510
+ * client shows "Project ID is required" instead of refreshing.
511
+ */
512
+ test("an anonymous caller with no tenantid header gets 401, not the missing-project 400", async () => {
513
+ withProps(buildProps({}));
514
+
515
+ const call: RouteCall = await callProviders();
516
+
517
+ expectAuthenticationRequired(call.thrown);
518
+ expect(call.thrown).not.toBeInstanceOf(BadDataException);
519
+ expect(providerLookupsRan()).toBe(false);
520
+ });
521
+
522
+ /*
523
+ * isMasterAdmin is derived from a master-admin SESSION (userType
524
+ * MasterAdmin). The flag on its own, with no user and no such session, is
525
+ * not a credential and must not turn an anonymous request into an
526
+ * authenticated one.
527
+ */
528
+ test("an isMasterAdmin flag without a user or a master-admin session is not a credential", async () => {
529
+ withProps(buildProps({ tenantId: PROJECT_B_ID, isMasterAdmin: true }));
530
+
531
+ const call: RouteCall = await callProviders();
532
+
533
+ expectAuthenticationRequired(call.thrown);
466
534
  expect(providerLookupsRan()).toBe(false);
467
535
  });
468
536
 
@@ -483,6 +551,11 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
483
551
  expect(providerLookupsRan()).toBe(false);
484
552
  });
485
553
 
554
+ /*
555
+ * An API key IS a credential, so it is not "anonymous" and must not get
556
+ * the 401 an expired session gets - its client has no session to refresh
557
+ * and would only retry. It keeps the member-route refusal it always had.
558
+ */
486
559
  test("a project API key (no user id) cannot use this route - it is a logged-in-member route", async () => {
487
560
  withProps(
488
561
  buildProps({
@@ -495,6 +568,13 @@ describe("POST /ai-chat/providers - anonymous and unscoped callers", () => {
495
568
  const call: RouteCall = await callProviders();
496
569
 
497
570
  expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
571
+ expect(call.thrown).not.toBeInstanceOf(NotAuthenticatedException);
572
+ expect((call.thrown as NotAuthorizedException).code).toBe(
573
+ ExceptionCode.NotAuthorizedException,
574
+ );
575
+ expect((call.thrown as NotAuthorizedException).message).toBe(
576
+ "You are not authorized to access this project's data.",
577
+ );
498
578
  expect(providerLookupsRan()).toBe(false);
499
579
  });
500
580
  });
@@ -767,8 +847,13 @@ describe("every /ai-chat/* route requires membership of the project it was given
767
847
  },
768
848
  );
769
849
 
850
+ /*
851
+ * Was NotAuthorizedException (422). An unauthenticated caller is now told
852
+ * to authenticate (401) so an expired dashboard session refreshes and
853
+ * replays instead of surfacing an authorization error.
854
+ */
770
855
  test.each(routes)(
771
- "$uri refuses an unauthenticated caller",
856
+ "$uri refuses an unauthenticated caller with 401 and touches nothing",
772
857
  async (route: { uri: string; body: JSONObject }) => {
773
858
  withProps(buildProps({ tenantId: PROJECT_B_ID }));
774
859
 
@@ -777,7 +862,55 @@ describe("every /ai-chat/* route requires membership of the project it was given
777
862
  body: route.body,
778
863
  });
779
864
 
865
+ expectAuthenticationRequired(call.thrown);
866
+ expect(call.nextCallCount).toBe(1);
867
+ expect(AIConversationService.findOneById).not.toHaveBeenCalled();
868
+ expect(AIConversationMessageService.findOneById).not.toHaveBeenCalled();
869
+ expect(ProjectService.findOneById).not.toHaveBeenCalled();
870
+ expect(providerLookupsRan()).toBe(false);
871
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
872
+ },
873
+ );
874
+
875
+ test.each(routes)(
876
+ "$uri refuses an unauthenticated caller with no tenantid header with 401, not 400",
877
+ async (route: { uri: string; body: JSONObject }) => {
878
+ withProps(buildProps({}));
879
+
880
+ const call: RouteCall = await callRoute({
881
+ uri: route.uri,
882
+ body: route.body,
883
+ });
884
+
885
+ expectAuthenticationRequired(call.thrown);
886
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
887
+ },
888
+ );
889
+
890
+ test.each(routes)(
891
+ "$uri still refuses a project API key with 422, not 401",
892
+ async (route: { uri: string; body: JSONObject }) => {
893
+ withProps(
894
+ buildProps({
895
+ tenantId: PROJECT_B_ID,
896
+ memberOfProjectId: PROJECT_B_ID,
897
+ userType: UserType.API,
898
+ }),
899
+ );
900
+
901
+ const call: RouteCall = await callRoute({
902
+ uri: route.uri,
903
+ body: route.body,
904
+ });
905
+
780
906
  expect(call.thrown).toBeInstanceOf(NotAuthorizedException);
907
+ expect((call.thrown as NotAuthorizedException).code).toBe(
908
+ ExceptionCode.NotAuthorizedException,
909
+ );
910
+ expect(AIConversationService.findOneById).not.toHaveBeenCalled();
911
+ expect(AIConversationMessageService.findOneById).not.toHaveBeenCalled();
912
+ expect(providerLookupsRan()).toBe(false);
913
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
781
914
  },
782
915
  );
783
916
 
@@ -4,6 +4,7 @@ import CommonAPI from "../../../Server/API/CommonAPI";
4
4
  import IncidentAPI from "../../../Server/API/IncidentAPI";
5
5
  import IncidentEpisodeAPI from "../../../Server/API/IncidentEpisodeAPI";
6
6
  import ScheduledMaintenanceAPI from "../../../Server/API/ScheduledMaintenanceAPI";
7
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
7
8
  import AIService from "../../../Server/Services/AIService";
8
9
  import AlertService from "../../../Server/Services/AlertService";
9
10
  import IncidentEpisodeService from "../../../Server/Services/IncidentEpisodeService";
@@ -30,12 +31,15 @@ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMainte
30
31
  import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
31
32
  import Dictionary from "../../../Types/Dictionary";
32
33
  import BadDataException from "../../../Types/Exception/BadDataException";
34
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
35
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
33
36
  import { JSONObject } from "../../../Types/JSON";
34
37
  import ObjectID from "../../../Types/ObjectID";
35
38
  import Permission, {
36
39
  UserPermission,
37
40
  UserTenantAccessPermission,
38
41
  } from "../../../Types/Permission";
42
+ import UserType from "../../../Types/UserType";
39
43
  import {
40
44
  afterEach,
41
45
  beforeAll,
@@ -539,6 +543,189 @@ describe.each(GENERATE_ROUTES)(
539
543
  },
540
544
  );
541
545
 
546
+ /*
547
+ * A project API key holding Project Admin in `tenantId`: what
548
+ * getDatabaseCommonInteractionProps returns for a key - no userId, userType
549
+ * API, and the permissions ProjectMiddleware attached to the key itself.
550
+ */
551
+ function apiKeyProps(tenantId: ObjectID): DatabaseCommonInteractionProps {
552
+ const props: DatabaseCommonInteractionProps = adminProps(tenantId);
553
+
554
+ return {
555
+ ...props,
556
+ userId: undefined,
557
+ userType: UserType.API,
558
+ };
559
+ }
560
+
561
+ type GateResult = {
562
+ // False when a middleware answered the request itself.
563
+ handlerReached: boolean;
564
+ thrown: unknown;
565
+ };
566
+
567
+ /*
568
+ * Runs a registered route the way Express does once getUserMiddleware has
569
+ * classified the caller as `userType`: every later middleware in order, then
570
+ * the handler, stopping at the first middleware that answers instead of
571
+ * calling next(). getUserMiddleware itself is not run - it needs a real
572
+ * token to verify - so the request carries the userType it would have set.
573
+ */
574
+ async function callThroughAuthGate(
575
+ route: GenerateRoute,
576
+ userType: UserType | undefined,
577
+ ): Promise<GateResult> {
578
+ const req: ExpressRequest = {
579
+ params: route.params,
580
+ query: {},
581
+ body: route.body,
582
+ headers: {},
583
+ userType: userType,
584
+ } as unknown as ExpressRequest;
585
+
586
+ const res: ExpressResponse = {} as ExpressResponse;
587
+
588
+ const registered: {
589
+ middlewares: Array<
590
+ (
591
+ req: ExpressRequest,
592
+ res: ExpressResponse,
593
+ next: NextFunction,
594
+ ) => void | Promise<void>
595
+ >;
596
+ } = mockRouter.match("post", route.uri);
597
+
598
+ for (const middleware of registered.middlewares.slice(1)) {
599
+ let calledNext: boolean = false;
600
+
601
+ await middleware(req, res, ((): void => {
602
+ calledNext = true;
603
+ }) as NextFunction);
604
+
605
+ if (!calledNext) {
606
+ return { handlerReached: false, thrown: undefined };
607
+ }
608
+ }
609
+
610
+ const next: jest.Mock = jest.fn();
611
+
612
+ await mockRouter
613
+ .match("post", route.uri)
614
+ .handlerFunction(req, res, next as unknown as NextFunction);
615
+
616
+ return {
617
+ handlerReached: true,
618
+ thrown: next.mock.calls[0] ? next.mock.calls[0][0] : undefined,
619
+ };
620
+ }
621
+
622
+ /*
623
+ * The expired-session half of these routes. The dashboard's access-token
624
+ * cookie expires with the JWT inside it, so a tab left open past the token
625
+ * lifetime sends "Generate with AI" with no credentials at all.
626
+ * getUserMiddleware lets that through as Public; requireUserAuthentication,
627
+ * mounted straight after it, answers 401 - the one status the browser client
628
+ * refreshes the session on and replays. Before it was mounted the handler's
629
+ * own permission check answered 400, which the client showed as an error.
630
+ */
631
+ describe.each(GENERATE_ROUTES)(
632
+ "POST $uri - expired session",
633
+ (route: GenerateRoute) => {
634
+ test("is mounted with requireUserAuthentication directly after getUserMiddleware", () => {
635
+ expect(mockRouter.match("post", route.uri).middlewares).toEqual([
636
+ UserMiddleware.getUserMiddleware,
637
+ UserMiddleware.requireUserAuthentication,
638
+ ]);
639
+ });
640
+
641
+ test.each([
642
+ ["a Public caller", UserType.Public],
643
+ ["a caller getUserMiddleware left unclassified", undefined],
644
+ ])(
645
+ "%s is answered 401 before the handler runs, and nothing is read or spent",
646
+ async (_label: string, userType: UserType | undefined) => {
647
+ const stubs: RouteStubs = route.stub(PROJECT_ID);
648
+
649
+ const result: GateResult = await callThroughAuthGate(route, userType);
650
+
651
+ expect(result.handlerReached).toBe(false);
652
+
653
+ const sendError: jest.Mock =
654
+ Response.sendErrorResponse as unknown as jest.Mock;
655
+ expect(sendError).toHaveBeenCalledTimes(1);
656
+
657
+ const error: unknown = sendError.mock.calls[0]![2];
658
+ expect(error).toBeInstanceOf(NotAuthenticatedException);
659
+ expect((error as NotAuthenticatedException).code).toBe(
660
+ ExceptionCode.NotAuthenticatedException,
661
+ );
662
+ expect((error as NotAuthenticatedException).message).toBe(
663
+ UserMiddleware.AUTHENTICATION_REQUIRED_MESSAGE,
664
+ );
665
+
666
+ expect(stubs.resourceLookup).not.toHaveBeenCalled();
667
+ expect(stubs.contextBuild).not.toHaveBeenCalled();
668
+ expect(projectLookup).not.toHaveBeenCalled();
669
+ expect(executeWithLogging).not.toHaveBeenCalled();
670
+ expect(Response.sendJsonObjectResponse).not.toHaveBeenCalled();
671
+ },
672
+ );
673
+
674
+ /*
675
+ * Generating from CI or a script is supported: the gate is about "no
676
+ * credentials", not "no human". A key with the right permission still
677
+ * gets its text, subject to the same kill switch as everyone else.
678
+ */
679
+ test("a project API key passes the gate and still generates", async () => {
680
+ const stubs: RouteStubs = route.stub(PROJECT_ID);
681
+ jest
682
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
683
+ .mockResolvedValue(apiKeyProps(PROJECT_ID));
684
+
685
+ const result: GateResult = await callThroughAuthGate(route, UserType.API);
686
+
687
+ expect(result.handlerReached).toBe(true);
688
+ expect(result.thrown).toBeUndefined();
689
+ expect(Response.sendErrorResponse).not.toHaveBeenCalled();
690
+ expect(stubs.contextBuild).toHaveBeenCalledTimes(1);
691
+ expect(executeWithLogging).toHaveBeenCalledTimes(1);
692
+ expect(sentPayload()[route.responseKey]).toBe(GENERATED_TEXT);
693
+ });
694
+
695
+ test("a logged-in user passes the gate unchanged", async () => {
696
+ route.stub(PROJECT_ID);
697
+
698
+ const result: GateResult = await callThroughAuthGate(
699
+ route,
700
+ UserType.User,
701
+ );
702
+
703
+ expect(result.handlerReached).toBe(true);
704
+ expect(result.thrown).toBeUndefined();
705
+ expect(executeWithLogging).toHaveBeenCalledTimes(1);
706
+ });
707
+
708
+ /*
709
+ * Defence in depth: the handler does not rely on the middleware alone.
710
+ * Its own tenant check refuses a credential-less caller with the same
711
+ * 401 - ahead of the permission check that used to answer it with 400.
712
+ */
713
+ test("the handler itself answers credential-less props with 401 before reading the row", async () => {
714
+ const stubs: RouteStubs = route.stub(PROJECT_ID);
715
+ jest
716
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
717
+ .mockResolvedValue({ tenantId: PROJECT_ID });
718
+
719
+ const call: RouteCall = await callRoute(route);
720
+
721
+ expect(call.thrown).toBeInstanceOf(NotAuthenticatedException);
722
+ expect(call.thrown).not.toBeInstanceOf(BadDataException);
723
+ expect(stubs.resourceLookup).not.toHaveBeenCalled();
724
+ expect(executeWithLogging).not.toHaveBeenCalled();
725
+ });
726
+ },
727
+ );
728
+
542
729
  /*
543
730
  * The shape of the gate's own read is load-bearing, and wrong in a way no
544
731
  * route-level assertion above would notice — because getting either half
@@ -20,9 +20,12 @@ import AIRunStatus from "../../../Types/AI/AIRunStatus";
20
20
  import AIRunType from "../../../Types/AI/AIRunType";
21
21
  import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
22
22
  import BadDataException from "../../../Types/Exception/BadDataException";
23
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
24
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
23
25
  import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
24
26
  import { JSONObject } from "../../../Types/JSON";
25
27
  import ObjectID from "../../../Types/ObjectID";
28
+ import UserType from "../../../Types/UserType";
26
29
 
27
30
  jest.mock("../../../Server/Utils/Express", () => {
28
31
  return {
@@ -225,13 +228,58 @@ describe.each(["incident", "alert"] as const)(
225
228
  expectNoPrivateReads();
226
229
  });
227
230
 
228
- test("requires a logged-in user before any subject or explanation read", async () => {
231
+ /*
232
+ * A caller with no credentials at all (only a tenantid header) is an
233
+ * expired session far more often than a stranger, so it gets 401 - the
234
+ * status the dashboard answers by refreshing the session and replaying -
235
+ * rather than the 422 it used to get.
236
+ */
237
+ test("requires a logged-in user before any subject or explanation read, answering an anonymous caller with 401", async () => {
229
238
  jest
230
239
  .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
231
240
  .mockResolvedValue({ tenantId: PROJECT_ID });
232
241
 
233
- expect(await callRoute()).toHaveBeenCalledWith(
234
- expect.any(NotAuthorizedException),
242
+ const next: jest.Mock = await callRoute();
243
+
244
+ expect(next).toHaveBeenCalledTimes(1);
245
+ expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
246
+ expect((next.mock.calls[0]![0] as NotAuthenticatedException).code).toBe(
247
+ ExceptionCode.NotAuthenticatedException,
248
+ );
249
+ expect(subjectRead).not.toHaveBeenCalled();
250
+ expectNoPrivateReads();
251
+ });
252
+
253
+ test("an anonymous caller with no tenant either still gets 401, not the missing-tenant 400", async () => {
254
+ jest
255
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
256
+ .mockResolvedValue({ userType: UserType.Public });
257
+
258
+ const next: jest.Mock = await callRoute();
259
+
260
+ expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
261
+ expect(subjectRead).not.toHaveBeenCalled();
262
+ expectNoPrivateReads();
263
+ });
264
+
265
+ /*
266
+ * A project API key is authenticated, just not as a person. The panel
267
+ * is a person's view, so the key is refused - with 422, never the 401
268
+ * that would send its client off to refresh a session it never had.
269
+ */
270
+ test("refuses a project API key with 422 before any subject or explanation read", async () => {
271
+ jest
272
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
273
+ .mockResolvedValue({ tenantId: PROJECT_ID, userType: UserType.API });
274
+
275
+ const next: jest.Mock = await callRoute();
276
+
277
+ expect(next).toHaveBeenCalledWith(expect.any(NotAuthorizedException));
278
+ expect(next).not.toHaveBeenCalledWith(
279
+ expect.any(NotAuthenticatedException),
280
+ );
281
+ expect((next.mock.calls[0]![0] as NotAuthorizedException).message).toBe(
282
+ "A logged-in user session is required.",
235
283
  );
236
284
  expect(subjectRead).not.toHaveBeenCalled();
237
285
  expectNoPrivateReads();
@@ -37,9 +37,12 @@ import { InvestigationEvidenceRowsResponse } from "../../../Types/AI/Investigati
37
37
  import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
38
38
  import SortOrder from "../../../Types/BaseDatabase/SortOrder";
39
39
  import BadDataException from "../../../Types/Exception/BadDataException";
40
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
41
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
40
42
  import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
41
43
  import { JSONObject } from "../../../Types/JSON";
42
44
  import ObjectID from "../../../Types/ObjectID";
45
+ import UserType from "../../../Types/UserType";
43
46
  import {
44
47
  afterEach,
45
48
  beforeEach,
@@ -340,7 +343,12 @@ describe("POST /ai-investigation/evidence", () => {
340
343
  expectNoReads();
341
344
  });
342
345
 
343
- it("requires a logged-in user session", async () => {
346
+ /*
347
+ * No credentials at all is 401, not 422: it is almost always an expired
348
+ * dashboard session, and 401 is what makes the client refresh it and
349
+ * replay the request.
350
+ */
351
+ it("requires a logged-in user session, answering a caller with no credentials with 401", async () => {
344
352
  jest
345
353
  .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
346
354
  .mockResolvedValue({
@@ -350,7 +358,52 @@ describe("POST /ai-investigation/evidence", () => {
350
358
  const next: ReturnType<typeof jest.fn> =
351
359
  await callEvidenceRoute(incidentBody());
352
360
 
353
- expect(next).toHaveBeenCalledWith(expect.any(NotAuthorizedException));
361
+ expect(next).toHaveBeenCalledTimes(1);
362
+ expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
363
+ expect((next.mock.calls[0]![0] as NotAuthenticatedException).code).toBe(
364
+ ExceptionCode.NotAuthenticatedException,
365
+ );
366
+ expectNoReads();
367
+ });
368
+
369
+ it("answers an anonymous caller with 401 even when its body is malformed too", async () => {
370
+ jest
371
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
372
+ .mockResolvedValue({
373
+ userType: UserType.Public,
374
+ } as DatabaseCommonInteractionProps);
375
+
376
+ const next: ReturnType<typeof jest.fn> = await callEvidenceRoute(
377
+ incidentBody({ citationId: "not-a-citation" }),
378
+ );
379
+
380
+ expect(next).toHaveBeenCalledWith(expect.any(NotAuthenticatedException));
381
+ expectNoReads();
382
+ });
383
+
384
+ /*
385
+ * A project API key is authenticated, so it keeps the 422 this
386
+ * person-only route has always given it; a 401 would only send its
387
+ * client to refresh a session that does not exist.
388
+ */
389
+ it("refuses a project API key with 422 before any read", async () => {
390
+ jest
391
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
392
+ .mockResolvedValue({
393
+ tenantId: PROJECT_ID,
394
+ userType: UserType.API,
395
+ userTenantAccessPermission: viewerProps.userTenantAccessPermission,
396
+ } as DatabaseCommonInteractionProps);
397
+
398
+ const next: ReturnType<typeof jest.fn> =
399
+ await callEvidenceRoute(incidentBody());
400
+
401
+ expect(next).toHaveBeenCalledWith(
402
+ new NotAuthorizedException("A logged-in user session is required."),
403
+ );
404
+ expect(next).not.toHaveBeenCalledWith(
405
+ expect.any(NotAuthenticatedException),
406
+ );
354
407
  expectNoReads();
355
408
  });
356
409