@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,395 @@
1
+ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
2
+
3
+ /*
4
+ * Complements Captcha.test.ts (which covers the enable/disable and fail-closed
5
+ * decisions). This suite pins down the details of the outbound siteverify
6
+ * request - timeout, content type, form encoding - and what gets logged on
7
+ * each failure path, including real axios errors such as timeouts.
8
+ *
9
+ * Captcha reads CaptchaEnabled / CaptchaSecretKey at module load, so each
10
+ * configuration is loaded into an isolated registry. axios and the logger are
11
+ * replaced with recording fakes; nothing here touches the network.
12
+ */
13
+
14
+ interface CaptchaUtilLike {
15
+ isCaptchaEnabled: () => boolean;
16
+ verifyCaptcha: (options: {
17
+ token: string | null | undefined;
18
+ remoteIp?: string | null;
19
+ }) => Promise<void>;
20
+ }
21
+
22
+ interface RequestConfig {
23
+ headers?: { [key: string]: string };
24
+ timeout?: number;
25
+ }
26
+
27
+ interface PostCall {
28
+ url: string;
29
+ body: string;
30
+ config: RequestConfig;
31
+ }
32
+
33
+ interface FakeAxiosError extends Error {
34
+ isAxiosError: boolean;
35
+ code?: string;
36
+ }
37
+
38
+ interface Loaded {
39
+ captcha: CaptchaUtilLike;
40
+ posts: Array<PostCall>;
41
+ errorLogs: Array<string>;
42
+ warnLogs: Array<string>;
43
+ respondWith: (response: unknown) => void;
44
+ rejectWith: (error: unknown) => void;
45
+ }
46
+
47
+ const GENERIC_ERROR_MESSAGE: string =
48
+ "Captcha verification failed. Please try again.";
49
+
50
+ const makeAxiosError: (message: string, code?: string) => FakeAxiosError = (
51
+ message: string,
52
+ code?: string,
53
+ ): FakeAxiosError => {
54
+ const error: FakeAxiosError = new Error(message) as FakeAxiosError;
55
+ error.isAxiosError = true;
56
+ if (code) {
57
+ error.code = code;
58
+ }
59
+ return error;
60
+ };
61
+
62
+ const loadCaptcha: (config: {
63
+ enabled: boolean;
64
+ secret: string;
65
+ }) => Loaded = (config: { enabled: boolean; secret: string }): Loaded => {
66
+ const posts: Array<PostCall> = [];
67
+ const errorLogs: Array<string> = [];
68
+ const warnLogs: Array<string> = [];
69
+ let response: unknown = { data: { success: true } };
70
+ let rejection: unknown = null;
71
+ let captcha: CaptchaUtilLike | null = null;
72
+
73
+ jest.isolateModules((): void => {
74
+ jest.doMock("../../../Server/EnvironmentConfig", (): unknown => {
75
+ return {
76
+ ...(jest.requireActual("../../../Server/EnvironmentConfig") as Record<
77
+ string,
78
+ unknown
79
+ >),
80
+ CaptchaEnabled: config.enabled,
81
+ CaptchaSecretKey: config.secret,
82
+ };
83
+ });
84
+
85
+ jest.doMock("../../../Server/Utils/Logger", (): unknown => {
86
+ const logger: unknown = {
87
+ error: (message: unknown): void => {
88
+ errorLogs.push(String(message));
89
+ },
90
+ warn: (message: unknown): void => {
91
+ warnLogs.push(String(message));
92
+ },
93
+ info: (): void => {},
94
+ debug: (): void => {},
95
+ };
96
+ return { __esModule: true, default: logger };
97
+ });
98
+
99
+ jest.doMock("axios", (): unknown => {
100
+ const post: unknown = (
101
+ url: string,
102
+ body: string,
103
+ requestConfig: RequestConfig,
104
+ ): Promise<unknown> => {
105
+ posts.push({ url, body, config: requestConfig });
106
+
107
+ if (rejection) {
108
+ return Promise.reject(rejection);
109
+ }
110
+
111
+ return Promise.resolve(response);
112
+ };
113
+
114
+ const isAxiosError: unknown = (err: unknown): boolean => {
115
+ return Boolean(
116
+ err && (err as { isAxiosError?: boolean }).isAxiosError === true,
117
+ );
118
+ };
119
+
120
+ return {
121
+ __esModule: true,
122
+ default: { post, isAxiosError },
123
+ post,
124
+ isAxiosError,
125
+ };
126
+ });
127
+
128
+ captcha = (
129
+ jest.requireActual("../../../Server/Utils/Captcha") as {
130
+ default: CaptchaUtilLike;
131
+ }
132
+ ).default;
133
+ });
134
+
135
+ return {
136
+ captcha: captcha as unknown as CaptchaUtilLike,
137
+ posts,
138
+ errorLogs,
139
+ warnLogs,
140
+ respondWith: (next: unknown): void => {
141
+ response = next;
142
+ },
143
+ rejectWith: (next: unknown): void => {
144
+ rejection = next;
145
+ },
146
+ };
147
+ };
148
+
149
+ const captureRejection: (work: Promise<void>) => Promise<Error> = async (
150
+ work: Promise<void>,
151
+ ): Promise<Error> => {
152
+ try {
153
+ await work;
154
+ } catch (err) {
155
+ expect(err).toBeInstanceOf(Error);
156
+ // Class identity differs across isolated registries; compare by name.
157
+ expect((err as Error).constructor.name).toBe("BadDataException");
158
+ return err as Error;
159
+ }
160
+
161
+ throw new Error("Expected verifyCaptcha to reject, but it resolved");
162
+ };
163
+
164
+ describe("CaptchaUtil outbound request", () => {
165
+ beforeEach((): void => {
166
+ jest.resetModules();
167
+ });
168
+
169
+ it("posts form-encoded data to hCaptcha with a 5 second timeout", async () => {
170
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s3cret" });
171
+
172
+ await loaded.captcha.verifyCaptcha({ token: "tok" });
173
+
174
+ expect(loaded.posts).toHaveLength(1);
175
+ const call: PostCall = loaded.posts[0] as PostCall;
176
+ expect(call.url).toBe("https://hcaptcha.com/siteverify");
177
+ expect(call.config.timeout).toBe(5000);
178
+ expect(call.config.headers).toEqual({
179
+ "content-type": "application/x-www-form-urlencoded",
180
+ });
181
+ expect(typeof call.body).toBe("string");
182
+ });
183
+
184
+ it("form-encodes tokens and IPs containing reserved characters", async () => {
185
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "a&b=c" });
186
+ const token: string = "P1_abc+/=&x y";
187
+
188
+ await loaded.captcha.verifyCaptcha({
189
+ token,
190
+ remoteIp: "2001:db8::1",
191
+ });
192
+
193
+ const params: URLSearchParams = new URLSearchParams(
194
+ (loaded.posts[0] as PostCall).body,
195
+ );
196
+ expect(params.get("secret")).toBe("a&b=c");
197
+ expect(params.get("response")).toBe(token);
198
+ expect(params.get("remoteip")).toBe("2001:db8::1");
199
+ expect(Array.from(params.keys())).toEqual([
200
+ "secret",
201
+ "response",
202
+ "remoteip",
203
+ ]);
204
+ });
205
+
206
+ it("omits remoteip when it is null", async () => {
207
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
208
+
209
+ await loaded.captcha.verifyCaptcha({ token: "tok", remoteIp: null });
210
+
211
+ const params: URLSearchParams = new URLSearchParams(
212
+ (loaded.posts[0] as PostCall).body,
213
+ );
214
+ expect(params.has("remoteip")).toBe(false);
215
+ });
216
+
217
+ it("makes exactly one request per verification (no retries)", async () => {
218
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
219
+ loaded.rejectWith(
220
+ makeAxiosError("timeout of 5000ms exceeded", "ECONNABORTED"),
221
+ );
222
+
223
+ await captureRejection(loaded.captcha.verifyCaptcha({ token: "tok" }));
224
+
225
+ expect(loaded.posts).toHaveLength(1);
226
+ });
227
+ });
228
+
229
+ describe("CaptchaUtil token validation", () => {
230
+ beforeEach((): void => {
231
+ jest.resetModules();
232
+ });
233
+
234
+ it.each([
235
+ ["null", null],
236
+ ["undefined", undefined],
237
+ ["empty", ""],
238
+ ["whitespace", " \t\n "],
239
+ ])(
240
+ "rejects a %s token with the 'missing' message without calling the provider",
241
+ async (_label: string, token: string | null | undefined) => {
242
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
243
+
244
+ const error: Error = await captureRejection(
245
+ loaded.captcha.verifyCaptcha({ token }),
246
+ );
247
+
248
+ expect(error.message).toBe(
249
+ "Captcha token is missing. Please complete the verification challenge.",
250
+ );
251
+ expect(loaded.posts).toHaveLength(0);
252
+ },
253
+ );
254
+
255
+ it("ignores a missing token entirely when captcha is disabled", async () => {
256
+ const loaded: Loaded = loadCaptcha({ enabled: false, secret: "" });
257
+
258
+ await expect(
259
+ loaded.captcha.verifyCaptcha({ token: null }),
260
+ ).resolves.toBeUndefined();
261
+ expect(loaded.posts).toHaveLength(0);
262
+ expect(loaded.errorLogs).toHaveLength(0);
263
+ });
264
+
265
+ it("is disabled even with a secret when the flag is off, and skips verification", async () => {
266
+ const loaded: Loaded = loadCaptcha({ enabled: false, secret: "s" });
267
+
268
+ expect(loaded.captcha.isCaptchaEnabled()).toBe(false);
269
+ await loaded.captcha.verifyCaptcha({ token: "anything" });
270
+ expect(loaded.posts).toHaveLength(0);
271
+ });
272
+ });
273
+
274
+ describe("CaptchaUtil failure handling and logging", () => {
275
+ beforeEach((): void => {
276
+ jest.resetModules();
277
+ });
278
+
279
+ it("logs a configuration error when enabled without a secret", async () => {
280
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "" });
281
+
282
+ const error: Error = await captureRejection(
283
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
284
+ );
285
+
286
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
287
+ expect(loaded.errorLogs).toEqual([
288
+ "Captcha is enabled but CAPTCHA_SECRET_KEY is not configured.",
289
+ ]);
290
+ });
291
+
292
+ it("checks the secret before the token, so a blank token still reports misconfiguration", async () => {
293
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "" });
294
+
295
+ const error: Error = await captureRejection(
296
+ loaded.captcha.verifyCaptcha({ token: "" }),
297
+ );
298
+
299
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
300
+ expect(loaded.errorLogs).toHaveLength(1);
301
+ });
302
+
303
+ it("fails closed on an axios timeout and logs the axios message", async () => {
304
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
305
+ loaded.rejectWith(
306
+ makeAxiosError("timeout of 5000ms exceeded", "ECONNABORTED"),
307
+ );
308
+
309
+ const error: Error = await captureRejection(
310
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
311
+ );
312
+
313
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
314
+ expect(loaded.errorLogs).toEqual([
315
+ "Captcha provider verification failure: timeout of 5000ms exceeded",
316
+ ]);
317
+ });
318
+
319
+ it("fails closed on an HTTP error status from the provider", async () => {
320
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
321
+ loaded.rejectWith(makeAxiosError("Request failed with status code 500"));
322
+
323
+ const error: Error = await captureRejection(
324
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
325
+ );
326
+
327
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
328
+ expect(loaded.errorLogs[0]).toContain("status code 500");
329
+ });
330
+
331
+ it("fails closed on a non-axios error and logs its message", async () => {
332
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
333
+ loaded.rejectWith(new TypeError("unexpected"));
334
+
335
+ const error: Error = await captureRejection(
336
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
337
+ );
338
+
339
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
340
+ expect(loaded.errorLogs).toEqual([
341
+ "Captcha provider verification failure: unexpected",
342
+ ]);
343
+ });
344
+
345
+ it("warns with the provider payload when success is false, then logs the failure", async () => {
346
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
347
+ loaded.respondWith({
348
+ data: { success: false, "error-codes": ["invalid-input-response"] },
349
+ });
350
+
351
+ const error: Error = await captureRejection(
352
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
353
+ );
354
+
355
+ expect(error.message).toBe(GENERIC_ERROR_MESSAGE);
356
+ expect(loaded.warnLogs).toEqual([
357
+ 'hCaptcha verification failed: {"success":false,"error-codes":["invalid-input-response"]}',
358
+ ]);
359
+ expect(loaded.errorLogs).toEqual([
360
+ `Captcha provider verification failure: ${GENERIC_ERROR_MESSAGE}`,
361
+ ]);
362
+ });
363
+
364
+ it("warns with an empty payload when the provider returns no body", async () => {
365
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
366
+ loaded.respondWith({ data: null });
367
+
368
+ await captureRejection(loaded.captcha.verifyCaptcha({ token: "tok" }));
369
+
370
+ expect(loaded.warnLogs).toEqual(["hCaptcha verification failed: {}"]);
371
+ });
372
+
373
+ it("never logs the secret key", async () => {
374
+ const secret: string = "very-private-secret";
375
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret });
376
+ loaded.respondWith({ data: { success: false } });
377
+
378
+ await captureRejection(loaded.captcha.verifyCaptcha({ token: "tok" }));
379
+
380
+ for (const line of [...loaded.errorLogs, ...loaded.warnLogs]) {
381
+ expect(line).not.toContain(secret);
382
+ }
383
+ });
384
+
385
+ it("does not log anything on success", async () => {
386
+ const loaded: Loaded = loadCaptcha({ enabled: true, secret: "s" });
387
+ loaded.respondWith({ data: { success: true, hostname: "example.com" } });
388
+
389
+ await expect(
390
+ loaded.captcha.verifyCaptcha({ token: "tok" }),
391
+ ).resolves.toBeUndefined();
392
+ expect(loaded.errorLogs).toHaveLength(0);
393
+ expect(loaded.warnLogs).toHaveLength(0);
394
+ });
395
+ });
@@ -0,0 +1,251 @@
1
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
2
+ import { ExpressRequest, ExpressResponse } from "../../../Server/Utils/Express";
3
+ import JSONWebToken from "../../../Server/Utils/JsonWebToken";
4
+ import logger from "../../../Server/Utils/Logger";
5
+ import {
6
+ ensureMasterAdminPageAccess,
7
+ NOT_A_MASTER_ADMIN_MESSAGE,
8
+ } from "../../../Server/Utils/MasterAdminPageAccess";
9
+ import Response from "../../../Server/Utils/Response";
10
+ import BadDataException from "../../../Types/Exception/BadDataException";
11
+ import Exception from "../../../Types/Exception/Exception";
12
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
13
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
14
+ import { JSONObject } from "../../../Types/JSON";
15
+ import JSONWebTokenData from "../../../Types/JsonWebTokenData";
16
+ import ObjectID from "../../../Types/ObjectID";
17
+ import { getJestSpyOn } from "../../Spy";
18
+ import { beforeEach, describe, expect, jest, test } from "@jest/globals";
19
+
20
+ // The decode-failure branch logs at debug; keep the run quiet and observable.
21
+ jest.mock("../../../Server/Utils/Logger");
22
+
23
+ /*
24
+ * ensureMasterAdminPageAccess gates the Admin Dashboard's HTML shell (not its
25
+ * APIs), and replaced two copies of the same inline gate in
26
+ * App/FeatureSet/Frontend/Index.ts and App/FeatureSet/AdminDashboard/Serve.ts.
27
+ *
28
+ * The case it exists for: the access-token cookie expires together with the
29
+ * JWT inside it. An admin who reloads a tab left open past the token lifetime
30
+ * arrives with NO token at all. The old gates answered that with a 422 JSON
31
+ * error page, so the SPA never loaded and never got the chance to refresh the
32
+ * session - the admin was stuck until they found the login page by hand. Now
33
+ * a missing or undecodable token is served the shell (it holds nothing the
34
+ * public static bundle does not), and the SPA's refresh-aware API client
35
+ * recovers the session or sends a signed-out visitor to the login page.
36
+ *
37
+ * The one refusal that did NOT move: a session that decodes and is positively
38
+ * not a master admin still gets the error. Refreshing would hand back the
39
+ * same non-admin session.
40
+ */
41
+
42
+ const SERVICE: string = "admin-dashboard-test";
43
+
44
+ describe("ensureMasterAdminPageAccess", () => {
45
+ let request: ExpressRequest;
46
+ let response: ExpressResponse;
47
+ let getAccessTokenSpy: jest.SpyInstance<any, any>;
48
+ let decodeSpy: jest.SpyInstance<any, any>;
49
+ let sendErrorSpy: jest.SpyInstance<any, any>;
50
+
51
+ type RunFunction = () => Promise<JSONObject>;
52
+
53
+ const run: RunFunction = async (): Promise<JSONObject> => {
54
+ return await ensureMasterAdminPageAccess({
55
+ req: request,
56
+ res: response,
57
+ service: SERVICE,
58
+ });
59
+ };
60
+
61
+ type DecodedTokenFunction = (isMasterAdmin: unknown) => JSONWebTokenData;
62
+
63
+ const decodedToken: DecodedTokenFunction = (
64
+ isMasterAdmin: unknown,
65
+ ): JSONWebTokenData => {
66
+ return {
67
+ userId: ObjectID.generate(),
68
+ isMasterAdmin: isMasterAdmin,
69
+ isGlobalLogin: true,
70
+ } as unknown as JSONWebTokenData;
71
+ };
72
+
73
+ beforeEach(() => {
74
+ jest.restoreAllMocks();
75
+ jest.clearAllMocks();
76
+
77
+ request = { headers: {}, cookies: {} } as unknown as ExpressRequest;
78
+
79
+ response = {
80
+ status: jest.fn().mockReturnThis(),
81
+ json: jest.fn().mockReturnThis(),
82
+ send: jest.fn().mockReturnThis(),
83
+ } as unknown as ExpressResponse;
84
+
85
+ getAccessTokenSpy = getJestSpyOn(
86
+ UserMiddleware,
87
+ "getAccessTokenFromExpressRequest",
88
+ ).mockReturnValue(undefined);
89
+
90
+ /*
91
+ * Each test that needs a decodable token says what it decodes to. The
92
+ * default is the real failure: JSONWebToken.decode wraps every jwt.verify
93
+ * error (expired, bad signature, malformed) in this BadDataException.
94
+ */
95
+ decodeSpy = getJestSpyOn(JSONWebToken, "decode").mockImplementation(() => {
96
+ throw new BadDataException("AccessToken is invalid or expired");
97
+ });
98
+
99
+ sendErrorSpy = getJestSpyOn(
100
+ Response,
101
+ "sendErrorResponse",
102
+ ).mockImplementation(() => {
103
+ return undefined as never;
104
+ });
105
+ });
106
+
107
+ describe("with no access token (the expired-cookie reload)", () => {
108
+ test("resolves to an empty object and sends no error, so the SPA loads", async () => {
109
+ await expect(run()).resolves.toEqual({});
110
+
111
+ expect(sendErrorSpy).not.toHaveBeenCalled();
112
+ expect(response.status).not.toHaveBeenCalled();
113
+ expect(response.send).not.toHaveBeenCalled();
114
+ expect(response.json).not.toHaveBeenCalled();
115
+ });
116
+
117
+ test("reads the token from the request it was given", async () => {
118
+ await run();
119
+
120
+ expect(getAccessTokenSpy).toHaveBeenCalledTimes(1);
121
+ expect(getAccessTokenSpy).toHaveBeenCalledWith(request);
122
+ });
123
+
124
+ test("never tries to decode a token it does not have", async () => {
125
+ await run();
126
+
127
+ expect(decodeSpy).not.toHaveBeenCalled();
128
+ });
129
+
130
+ test("treats an empty-string token as no token", async () => {
131
+ getAccessTokenSpy.mockReturnValue("");
132
+
133
+ await expect(run()).resolves.toEqual({});
134
+
135
+ expect(decodeSpy).not.toHaveBeenCalled();
136
+ expect(sendErrorSpy).not.toHaveBeenCalled();
137
+ });
138
+ });
139
+
140
+ describe("with a token that does not decode", () => {
141
+ beforeEach(() => {
142
+ getAccessTokenSpy.mockReturnValue("expired.jwt.token");
143
+ });
144
+
145
+ test("resolves to an empty object and sends no error", async () => {
146
+ await expect(run()).resolves.toEqual({});
147
+
148
+ expect(decodeSpy).toHaveBeenCalledTimes(1);
149
+ expect(decodeSpy).toHaveBeenCalledWith("expired.jwt.token");
150
+ expect(sendErrorSpy).not.toHaveBeenCalled();
151
+ expect(response.status).not.toHaveBeenCalled();
152
+ });
153
+
154
+ // Whatever decode throws, the shell is served and nothing escapes.
155
+ test.each([
156
+ [
157
+ "the BadDataException JSONWebToken.decode raises",
158
+ (): Error => {
159
+ return new BadDataException("AccessToken is invalid or expired");
160
+ },
161
+ ],
162
+ [
163
+ "a plain Error",
164
+ (): Error => {
165
+ return new Error("jwt expired");
166
+ },
167
+ ],
168
+ [
169
+ "a NotAuthorizedException",
170
+ (): Error => {
171
+ return new NotAuthorizedException("should not surface");
172
+ },
173
+ ],
174
+ ])(
175
+ "does not reject or send an error when decode throws %s",
176
+ async (_label: string, makeError: () => Error) => {
177
+ decodeSpy.mockImplementation(() => {
178
+ throw makeError();
179
+ });
180
+
181
+ await expect(run()).resolves.toEqual({});
182
+
183
+ expect(sendErrorSpy).not.toHaveBeenCalled();
184
+ },
185
+ );
186
+
187
+ test("logs the decode failure at debug, tagged with the service", async () => {
188
+ const thrown: Error = new Error("jwt expired");
189
+
190
+ decodeSpy.mockImplementation(() => {
191
+ throw thrown;
192
+ });
193
+
194
+ await run();
195
+
196
+ expect(logger.debug).toHaveBeenCalledWith(thrown, { service: SERVICE });
197
+ });
198
+ });
199
+
200
+ describe("with a token that decodes", () => {
201
+ beforeEach(() => {
202
+ getAccessTokenSpy.mockReturnValue("valid.jwt.token");
203
+ });
204
+
205
+ test("refuses a session that is not a master admin with a 422", async () => {
206
+ decodeSpy.mockReturnValue(decodedToken(false) as never);
207
+
208
+ await expect(run()).resolves.toEqual({});
209
+
210
+ expect(sendErrorSpy).toHaveBeenCalledTimes(1);
211
+
212
+ const [sentRequest, sentResponse, sentError] = sendErrorSpy.mock
213
+ .calls[0] as [ExpressRequest, ExpressResponse, Exception];
214
+
215
+ expect(sentRequest).toBe(request);
216
+ expect(sentResponse).toBe(response);
217
+ expect(sentError).toBeInstanceOf(NotAuthorizedException);
218
+ expect(sentError).not.toBeInstanceOf(NotAuthenticatedException);
219
+ expect(sentError.code).toBe(422);
220
+ expect(sentError.message).toBe(NOT_A_MASTER_ADMIN_MESSAGE);
221
+ });
222
+
223
+ test("refuses a token with no isMasterAdmin claim at all", async () => {
224
+ decodeSpy.mockReturnValue(decodedToken(undefined) as never);
225
+
226
+ await run();
227
+
228
+ expect(sendErrorSpy).toHaveBeenCalledTimes(1);
229
+ expect(
230
+ (sendErrorSpy.mock.calls[0] as Array<unknown>)[2] as Exception,
231
+ ).toBeInstanceOf(NotAuthorizedException);
232
+ });
233
+
234
+ test("lets a master admin through without sending anything", async () => {
235
+ decodeSpy.mockReturnValue(decodedToken(true) as never);
236
+
237
+ await expect(run()).resolves.toEqual({});
238
+
239
+ expect(decodeSpy).toHaveBeenCalledWith("valid.jwt.token");
240
+ expect(sendErrorSpy).not.toHaveBeenCalled();
241
+ expect(response.status).not.toHaveBeenCalled();
242
+ expect(response.send).not.toHaveBeenCalled();
243
+ });
244
+ });
245
+
246
+ test("the refusal message names the admin dashboard", () => {
247
+ expect(NOT_A_MASTER_ADMIN_MESSAGE).toBe(
248
+ "Unauthorized: Only master admins can access the admin dashboard.",
249
+ );
250
+ });
251
+ });