@oneuptime/common 12.0.13 → 12.0.15

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 (561) hide show
  1. package/Models/DatabaseModels/GlobalOidc.ts +20 -0
  2. package/Models/DatabaseModels/GlobalSso.ts +20 -0
  3. package/Models/DatabaseModels/MarketingConversion.ts +19 -9
  4. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
  5. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  6. package/Models/DatabaseModels/StatusPage.ts +7 -4
  7. package/Models/DatabaseModels/UserCall.ts +94 -3
  8. package/Models/DatabaseModels/UserEmail.ts +94 -3
  9. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  10. package/Models/DatabaseModels/UserPush.ts +37 -0
  11. package/Models/DatabaseModels/UserSMS.ts +94 -3
  12. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  13. package/Server/API/BaseAPI.ts +79 -14
  14. package/Server/API/StatusPageAPI.ts +29 -4
  15. package/Server/API/UserCallAPI.ts +53 -45
  16. package/Server/API/UserEmailAPI.ts +53 -44
  17. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  18. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  19. package/Server/API/UserPushAPI.ts +126 -2
  20. package/Server/API/UserSmsAPI.ts +53 -44
  21. package/Server/API/UserWhatsAppAPI.ts +53 -50
  22. package/Server/EnvironmentConfig.ts +7 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  29. package/Server/Middleware/SlackAuthorization.ts +43 -6
  30. package/Server/Middleware/UserAuthorization.ts +236 -26
  31. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  32. package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
  33. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  34. package/Server/Services/DatabaseService.ts +101 -0
  35. package/Server/Services/GlobalOidcProjectService.ts +126 -1
  36. package/Server/Services/GlobalOidcService.ts +132 -0
  37. package/Server/Services/GlobalSsoProjectService.ts +126 -1
  38. package/Server/Services/GlobalSsoService.ts +132 -0
  39. package/Server/Services/LogAggregationService.ts +5 -0
  40. package/Server/Services/PushNotificationService.ts +127 -12
  41. package/Server/Services/TeamMemberService.ts +53 -13
  42. package/Server/Services/UserCallService.ts +55 -24
  43. package/Server/Services/UserEmailService.ts +83 -17
  44. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  45. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  46. package/Server/Services/UserNotificationRuleService.ts +26 -0
  47. package/Server/Services/UserPushService.ts +89 -0
  48. package/Server/Services/UserSmsService.ts +50 -17
  49. package/Server/Services/UserWhatsAppService.ts +48 -15
  50. package/Server/Types/Database/QueryHelper.ts +30 -0
  51. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  52. package/Server/Utils/ChannelVerification.ts +510 -0
  53. package/Server/Utils/CronTab.ts +73 -0
  54. package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
  55. package/Server/Utils/Marketing/ConversionUploadProvider.ts +35 -5
  56. package/Server/Utils/Marketing/Providers/GoogleAds.ts +1 -1
  57. package/Server/Utils/Marketing/Providers/LinkedIn.ts +1 -1
  58. package/Server/Utils/Marketing/Providers/Meta.ts +1 -1
  59. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +1 -1
  60. package/Server/Utils/Marketing/Providers/Reddit.ts +1 -1
  61. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
  62. package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
  63. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
  64. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
  65. package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
  66. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +47 -44
  67. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
  68. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
  69. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
  70. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  71. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +8 -0
  72. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  73. package/Server/Utils/Monitor/MonitorResource.ts +161 -21
  74. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  75. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  76. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  77. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  78. package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
  79. package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
  80. package/Server/Utils/TotpAuth.ts +137 -9
  81. package/Server/Utils/UserRegistrationToken.ts +182 -0
  82. package/Server/Utils/VerificationCode.ts +134 -0
  83. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  84. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  85. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  86. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  87. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  88. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  89. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  90. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  91. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  92. package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
  93. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  94. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  95. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  96. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  97. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  98. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +265 -0
  99. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
  100. package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
  101. package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
  102. package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
  103. package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
  104. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  105. package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
  106. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  107. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  108. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  109. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  110. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  111. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  112. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  113. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
  114. package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
  115. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  116. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  117. package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
  118. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
  119. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  120. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  121. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  122. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  123. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  124. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  125. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  126. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  127. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  128. package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
  129. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  130. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  131. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  132. package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
  133. package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
  134. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  135. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  136. package/Tests/Server/Utils/Browser.test.ts +162 -0
  137. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  138. package/Tests/Server/Utils/CronTab.test.ts +116 -0
  139. package/Tests/Server/Utils/Express.test.ts +371 -0
  140. package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
  141. package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
  142. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +254 -0
  143. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +1 -1
  144. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
  145. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
  146. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  147. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  148. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
  149. package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
  150. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  151. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  152. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
  153. package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
  154. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  155. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  156. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  157. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  158. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
  159. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  160. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  161. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  162. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  163. package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
  164. package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
  165. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  166. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  167. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  168. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  169. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  170. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  171. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  172. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  173. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  174. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  175. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  176. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  177. package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
  178. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  179. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  180. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  181. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  182. package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
  183. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  184. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  185. package/Tests/UI/Components/Button.test.tsx +175 -1
  186. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  187. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  188. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  189. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  190. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  191. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  192. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  193. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  194. package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
  195. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  196. package/Tests/UI/Components/List.test.tsx +9 -2
  197. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  198. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  199. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  200. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  201. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  202. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  203. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  204. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  205. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  206. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  207. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  208. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  209. package/Tests/UI/Components/Toast.test.tsx +130 -7
  210. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  211. package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
  212. package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
  213. package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
  214. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  215. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  216. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  217. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  218. package/Tests/Utils/API.test.ts +16 -2
  219. package/Tests/Utils/Analytics.test.ts +187 -0
  220. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  221. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  222. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  223. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
  224. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1291 -0
  225. package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
  226. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  227. package/Types/Email/EmailTemplateType.ts +1 -0
  228. package/Types/Marketing/MarketingConversion.ts +12 -0
  229. package/Types/Monitor/CriteriaFilter.ts +9 -0
  230. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  231. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  232. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  233. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  234. package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
  235. package/Types/Permission.ts +35 -0
  236. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  237. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  238. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  239. package/Types/Service/ServiceLanguage.ts +144 -0
  240. package/Types/Text.ts +111 -19
  241. package/UI/Components/Button/Button.tsx +27 -14
  242. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  243. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  244. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  245. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  246. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  247. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  248. package/UI/Components/CommandPalette/Types.ts +49 -0
  249. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  250. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  251. package/UI/Components/Feed/FeedItem.tsx +1 -1
  252. package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
  253. package/UI/Components/Forms/BasicForm.tsx +13 -1
  254. package/UI/Components/Forms/BasicModelForm.tsx +2 -1
  255. package/UI/Components/Forms/ModelForm.tsx +10 -1
  256. package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
  257. package/UI/Components/List/List.tsx +37 -5
  258. package/UI/Components/List/ListSkeleton.tsx +54 -0
  259. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  260. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  261. package/UI/Components/Modal/Modal.tsx +2 -2
  262. package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
  263. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  264. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  265. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  266. package/UI/Components/Navbar/NavBar.tsx +14 -1
  267. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  268. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  269. package/UI/Components/SideOver/SideOver.tsx +37 -2
  270. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  271. package/UI/Components/Table/Table.tsx +74 -44
  272. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  273. package/UI/Components/Tabs/Tab.tsx +1 -1
  274. package/UI/Components/Toast/Toast.tsx +118 -53
  275. package/UI/Components/Toast/ToastInit.tsx +82 -32
  276. package/UI/Styles/Theme.css +40 -0
  277. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  278. package/UI/Utils/ModelListCache.ts +188 -0
  279. package/UI/Utils/Project.ts +29 -0
  280. package/Utils/API.ts +6 -1
  281. package/Utils/Analytics.ts +55 -2
  282. package/Utils/Monitor/MonitorMetricType.ts +40 -0
  283. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +587 -37
  284. package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
  285. package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
  286. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  287. package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
  288. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  289. package/build/dist/Models/DatabaseModels/MarketingConversion.js +20 -10
  290. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
  291. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
  292. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
  293. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  294. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  295. package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
  296. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  297. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  298. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  299. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  300. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  301. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  302. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  303. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  304. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  305. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  306. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  307. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  308. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  309. package/build/dist/Server/API/BaseAPI.js +69 -13
  310. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  311. package/build/dist/Server/API/StatusPageAPI.js +25 -4
  312. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  313. package/build/dist/Server/API/UserCallAPI.js +42 -38
  314. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  315. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  316. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  317. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  318. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  319. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  320. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  321. package/build/dist/Server/API/UserPushAPI.js +95 -2
  322. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  323. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  324. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  325. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  326. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  327. package/build/dist/Server/EnvironmentConfig.js +6 -0
  328. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  329. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  330. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  331. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  332. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  333. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  334. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  335. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
  336. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
  337. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
  338. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js.map +1 -0
  339. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  340. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  341. package/build/dist/Server/Middleware/SlackAuthorization.js +28 -4
  342. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
  343. package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
  344. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  345. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  346. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  347. package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
  348. package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
  349. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  350. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  351. package/build/dist/Server/Services/DatabaseService.js +74 -0
  352. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  353. package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
  354. package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
  355. package/build/dist/Server/Services/GlobalOidcService.js +127 -0
  356. package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
  357. package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
  358. package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
  359. package/build/dist/Server/Services/GlobalSsoService.js +127 -0
  360. package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
  361. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  362. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  363. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  364. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  365. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  366. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  367. package/build/dist/Server/Services/UserCallService.js +54 -22
  368. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  369. package/build/dist/Server/Services/UserEmailService.js +78 -17
  370. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  371. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  372. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  373. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  374. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  375. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  376. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  377. package/build/dist/Server/Services/UserPushService.js +77 -0
  378. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  379. package/build/dist/Server/Services/UserSmsService.js +50 -17
  380. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  381. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  382. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  383. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  384. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  385. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  386. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  387. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  388. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  389. package/build/dist/Server/Utils/CronTab.js +66 -0
  390. package/build/dist/Server/Utils/CronTab.js.map +1 -1
  391. package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
  392. package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
  393. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +17 -1
  394. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
  395. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +1 -1
  396. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
  397. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +1 -1
  398. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
  399. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +1 -1
  400. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
  401. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +1 -1
  402. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
  403. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +1 -1
  404. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
  405. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
  406. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  407. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
  408. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
  409. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
  410. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  411. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
  412. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  413. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
  414. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
  415. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +34 -37
  416. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  417. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
  418. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  419. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
  420. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
  421. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
  422. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  424. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  425. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +8 -0
  426. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  427. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  428. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  429. package/build/dist/Server/Utils/Monitor/MonitorResource.js +123 -23
  430. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  431. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  432. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  433. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  434. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  435. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  436. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  437. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  438. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
  439. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
  440. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
  441. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
  442. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  443. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  444. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  445. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  446. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  447. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  448. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  449. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  450. package/build/dist/Types/Marketing/MarketingConversion.js +10 -0
  451. package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
  452. package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
  453. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  454. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  455. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  456. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  457. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  458. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  459. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  460. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  461. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  462. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
  463. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
  464. package/build/dist/Types/Permission.js +33 -0
  465. package/build/dist/Types/Permission.js.map +1 -1
  466. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  467. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  468. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  469. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  470. package/build/dist/Types/Text.js +82 -17
  471. package/build/dist/Types/Text.js.map +1 -1
  472. package/build/dist/UI/Components/Button/Button.js +22 -14
  473. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  474. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  475. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  476. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  477. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  478. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  479. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  480. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  481. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  482. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  483. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  484. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  485. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  486. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  487. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  488. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  489. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  490. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  491. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  492. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  493. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  494. package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
  495. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  496. package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
  497. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  498. package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
  499. package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
  500. package/build/dist/UI/Components/Forms/ModelForm.js +4 -1
  501. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  502. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
  503. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
  504. package/build/dist/UI/Components/List/List.js +19 -5
  505. package/build/dist/UI/Components/List/List.js.map +1 -1
  506. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  507. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  508. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  509. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  510. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  511. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  512. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
  513. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  514. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  515. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  516. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  517. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  518. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  519. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  520. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  521. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  522. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  523. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  524. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  525. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  526. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  527. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  528. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  529. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  530. package/build/dist/UI/Components/Table/Table.js +35 -24
  531. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  532. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  533. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  534. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  535. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  536. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  537. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  538. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  539. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  540. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  541. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  542. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  543. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  544. package/build/dist/UI/Utils/Project.js +24 -1
  545. package/build/dist/UI/Utils/Project.js.map +1 -1
  546. package/build/dist/Utils/API.js +6 -1
  547. package/build/dist/Utils/API.js.map +1 -1
  548. package/build/dist/Utils/Analytics.js +50 -2
  549. package/build/dist/Utils/Analytics.js.map +1 -1
  550. package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
  551. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  552. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +318 -37
  553. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
  554. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
  555. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
  556. package/jest.config.json +1 -1
  557. package/package.json +2 -3
  558. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  559. package/Tests/__mocks__/otpauth.js +0 -30
  560. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  561. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -0,0 +1,427 @@
1
+ import UserTotpAuthAPI from "../../../Server/API/UserTotpAuthAPI";
2
+ import UserTotpAuthService from "../../../Server/Services/UserTotpAuthService";
3
+ import UserService from "../../../Server/Services/UserService";
4
+ import TotpAuth from "../../../Server/Utils/TotpAuth";
5
+ import {
6
+ NextFunction,
7
+ OneUptimeRequest,
8
+ OneUptimeResponse,
9
+ } from "../../../Server/Utils/Express";
10
+ import Response from "../../../Server/Utils/Response";
11
+ import { mockRouter } from "./Helpers";
12
+ import Email from "../../../Types/Email";
13
+ import BadDataException from "../../../Types/Exception/BadDataException";
14
+ import Exception from "../../../Types/Exception/Exception";
15
+ import ObjectID from "../../../Types/ObjectID";
16
+ import UserTotpAuth from "../../../Models/DatabaseModels/UserTotpAuth";
17
+ import User from "../../../Models/DatabaseModels/User";
18
+ import * as OTPAuth from "otpauth";
19
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
20
+
21
+ /*
22
+ * POST /user-totp-auth/validate — the endpoint that turns a scanned QR code
23
+ * into a working second factor.
24
+ *
25
+ * TotpAuth itself is deliberately NOT mocked here. The bug this endpoint
26
+ * failed on (issue #3275) lived precisely in the seam between what the QR code
27
+ * told the authenticator app and what the server recomputed, so a test that
28
+ * stubs the verifier proves nothing about it. The secret is generated by the
29
+ * real service path, the code is produced by parsing the stored otpauth:// URI
30
+ * exactly as an authenticator app would, and the endpoint gets the real thing.
31
+ *
32
+ * The second concern is ownership. `id` comes from the request body, so
33
+ * without the userId comparison any signed-in user could mark a stranger's
34
+ * pending TOTP row as verified using a code from their own phone — a second
35
+ * factor the victim does not hold. That check is asserted directly, and the
36
+ * assertion that matters is that isVerified is never written.
37
+ */
38
+
39
+ jest.mock("../../../Server/Utils/Express", () => {
40
+ return {
41
+ getRouter: () => {
42
+ return mockRouter;
43
+ },
44
+ };
45
+ });
46
+
47
+ jest.mock("../../../Server/Utils/Response", () => {
48
+ return {
49
+ sendEntityArrayResponse: jest.fn(),
50
+ sendJsonArrayResponse: jest.fn(),
51
+ sendJsonObjectResponse: jest.fn(),
52
+ sendEmptySuccessResponse: jest.fn(),
53
+ sendEntityResponse: jest.fn(),
54
+ sendErrorResponse: jest.fn(),
55
+ setNoCacheHeaders: jest.fn(),
56
+ };
57
+ });
58
+
59
+ jest.mock("../../../Server/Services/UserTotpAuthService");
60
+ jest.mock("../../../Server/Services/UserService");
61
+
62
+ const VALIDATE_ROUTE: string = "/user-totp-auth/validate";
63
+
64
+ const TOTP_ID: ObjectID = new ObjectID("11111111-1111-4111-8111-111111111111");
65
+ const OWNER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
66
+ const ATTACKER_ID: ObjectID = new ObjectID(
67
+ "33333333-3333-4333-8333-333333333333",
68
+ );
69
+
70
+ const OWNER_EMAIL: Email = new Email("owner@example.com");
71
+
72
+ type CallValidateResult = {
73
+ thrown: unknown;
74
+ nextCallCount: number;
75
+ };
76
+
77
+ type CallValidateFunction = (data: {
78
+ body: Record<string, unknown>;
79
+ signedInUserId?: ObjectID | undefined;
80
+ }) => Promise<CallValidateResult>;
81
+
82
+ const callValidate: CallValidateFunction = async (data: {
83
+ body: Record<string, unknown>;
84
+ signedInUserId?: ObjectID | undefined;
85
+ }): Promise<CallValidateResult> => {
86
+ const req: OneUptimeRequest = {
87
+ body: data.body,
88
+ params: {},
89
+ query: {},
90
+ headers: {},
91
+ userAuthorization: {
92
+ userId: data.signedInUserId || OWNER_ID,
93
+ },
94
+ } as unknown as OneUptimeRequest;
95
+
96
+ const res: OneUptimeResponse = {
97
+ send: jest.fn(),
98
+ json: jest.fn(),
99
+ setHeader: jest.fn(),
100
+ status: jest.fn().mockReturnThis(),
101
+ } as unknown as OneUptimeResponse;
102
+
103
+ const next: NextFunction = jest.fn() as unknown as NextFunction;
104
+
105
+ await mockRouter
106
+ .match("post", VALIDATE_ROUTE)
107
+ .handlerFunction(req, res, next);
108
+
109
+ const nextMock: jest.Mock = next as unknown as jest.Mock;
110
+
111
+ return {
112
+ thrown: nextMock.mock.calls[0]?.[0],
113
+ nextCallCount: nextMock.mock.calls.length,
114
+ };
115
+ };
116
+
117
+ /*
118
+ * A pending enrolment, built the way the service builds one: a real secret and
119
+ * the real otpauth:// URI derived from it.
120
+ */
121
+ type PendingEnrolmentFunction = (options?: { ownerId?: ObjectID }) => {
122
+ row: UserTotpAuth;
123
+ secret: string;
124
+ codeFromTheAuthenticatorApp: () => string;
125
+ };
126
+
127
+ const pendingEnrolment: PendingEnrolmentFunction = (options?: {
128
+ ownerId?: ObjectID;
129
+ }) => {
130
+ const secret: string = TotpAuth.generateSecret();
131
+
132
+ const otpUrl: string = TotpAuth.generateUri({
133
+ secret: secret,
134
+ userEmail: OWNER_EMAIL,
135
+ });
136
+
137
+ const row: UserTotpAuth = new UserTotpAuth();
138
+ row.id = TOTP_ID;
139
+ row.userId = options?.ownerId || OWNER_ID;
140
+ row.twoFactorSecret = secret;
141
+ row.twoFactorOtpUrl = otpUrl;
142
+ row.isVerified = false;
143
+
144
+ return {
145
+ row: row,
146
+ secret: secret,
147
+ codeFromTheAuthenticatorApp: (): string => {
148
+ return (OTPAuth.URI.parse(otpUrl) as OTPAuth.TOTP).generate();
149
+ },
150
+ };
151
+ };
152
+
153
+ let findTotpById: jest.Mock;
154
+ let updateTotpById: jest.Mock;
155
+ let findUserById: jest.Mock;
156
+
157
+ describe("POST /user-totp-auth/validate", () => {
158
+ beforeEach(() => {
159
+ mockRouter.routes.length = 0;
160
+
161
+ findTotpById = jest.fn();
162
+ updateTotpById = jest.fn(async () => {
163
+ return undefined;
164
+ });
165
+ findUserById = jest.fn();
166
+
167
+ UserTotpAuthService.findOneById = findTotpById as never;
168
+ UserTotpAuthService.updateOneById = updateTotpById as never;
169
+ UserTotpAuthService.getModel = jest
170
+ .fn()
171
+ .mockReturnValue({ tableName: "UserTotpAuth" }) as never;
172
+
173
+ UserService.findOneById = findUserById as never;
174
+
175
+ const owner: User = new User();
176
+ owner.id = OWNER_ID;
177
+ owner.email = OWNER_EMAIL;
178
+ findUserById.mockResolvedValue(owner as never);
179
+
180
+ new UserTotpAuthAPI();
181
+ });
182
+
183
+ afterEach(() => {
184
+ jest.restoreAllMocks();
185
+ });
186
+
187
+ describe("the route itself", () => {
188
+ test("is registered as a POST", () => {
189
+ expect(() => {
190
+ return mockRouter.match("post", VALIDATE_ROUTE);
191
+ }).not.toThrow();
192
+ });
193
+
194
+ /*
195
+ * Without an authentication middleware the handler would read
196
+ * `userAuthorization` off an unauthenticated request, the ownership
197
+ * comparison below would compare against `undefined`, and anybody could
198
+ * verify anybody's TOTP row.
199
+ */
200
+ test("is behind an authentication middleware", () => {
201
+ expect(mockRouter.match("post", VALIDATE_ROUTE).middlewares.length).toBe(
202
+ 1,
203
+ );
204
+ });
205
+ });
206
+
207
+ describe("when the code is the one the user's app is showing", () => {
208
+ test("marks the enrolment verified", async () => {
209
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
210
+ pendingEnrolment();
211
+
212
+ findTotpById.mockResolvedValue(enrolment.row as never);
213
+
214
+ const result: CallValidateResult = await callValidate({
215
+ body: {
216
+ id: TOTP_ID.toString(),
217
+ code: enrolment.codeFromTheAuthenticatorApp(),
218
+ },
219
+ });
220
+
221
+ expect(result.thrown).toBeUndefined();
222
+
223
+ expect(updateTotpById).toHaveBeenCalledWith(
224
+ expect.objectContaining({
225
+ id: TOTP_ID,
226
+ data: { isVerified: true },
227
+ }),
228
+ );
229
+
230
+ expect(Response.sendEmptySuccessResponse).toHaveBeenCalled();
231
+ });
232
+
233
+ /*
234
+ * Google Authenticator renders the code as "123 456" and the phone
235
+ * clipboard keeps the space. This is the same enrolment, pasted.
236
+ */
237
+ test("accepts the code as the phone's clipboard hands it over", async () => {
238
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
239
+ pendingEnrolment();
240
+
241
+ findTotpById.mockResolvedValue(enrolment.row as never);
242
+
243
+ const code: string = enrolment.codeFromTheAuthenticatorApp();
244
+
245
+ const result: CallValidateResult = await callValidate({
246
+ body: {
247
+ id: TOTP_ID.toString(),
248
+ code: `${code.slice(0, 3)} ${code.slice(3)}`,
249
+ },
250
+ });
251
+
252
+ expect(result.thrown).toBeUndefined();
253
+ expect(updateTotpById).toHaveBeenCalled();
254
+ });
255
+
256
+ test("reads the secret with root privileges, since the column is unreadable otherwise", async () => {
257
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
258
+ pendingEnrolment();
259
+
260
+ findTotpById.mockResolvedValue(enrolment.row as never);
261
+
262
+ await callValidate({
263
+ body: {
264
+ id: TOTP_ID.toString(),
265
+ code: enrolment.codeFromTheAuthenticatorApp(),
266
+ },
267
+ });
268
+
269
+ expect(findTotpById).toHaveBeenCalledWith(
270
+ expect.objectContaining({
271
+ select: expect.objectContaining({ twoFactorSecret: true }),
272
+ props: { isRoot: true },
273
+ }),
274
+ );
275
+ });
276
+ });
277
+
278
+ describe("when the code is wrong", () => {
279
+ test("fails with Invalid code and leaves the enrolment unverified", async () => {
280
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
281
+ pendingEnrolment();
282
+
283
+ findTotpById.mockResolvedValue(enrolment.row as never);
284
+
285
+ const result: CallValidateResult = await callValidate({
286
+ body: { id: TOTP_ID.toString(), code: "000000" },
287
+ });
288
+
289
+ expect(result.thrown).toBeInstanceOf(BadDataException);
290
+ expect((result.thrown as Exception).message).toBe("Invalid code");
291
+ expect(updateTotpById).not.toHaveBeenCalled();
292
+ });
293
+
294
+ test.each([
295
+ ["a missing code", undefined],
296
+ ["an empty code", ""],
297
+ ["a truncated code", "1234"],
298
+ ])(
299
+ "rejects %s without verifying anything",
300
+ async (_name: string, code: string | undefined) => {
301
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
302
+ pendingEnrolment();
303
+
304
+ findTotpById.mockResolvedValue(enrolment.row as never);
305
+
306
+ const result: CallValidateResult = await callValidate({
307
+ body: { id: TOTP_ID.toString(), code: code },
308
+ });
309
+
310
+ expect(result.thrown).toBeInstanceOf(BadDataException);
311
+ expect(updateTotpById).not.toHaveBeenCalled();
312
+ },
313
+ );
314
+
315
+ /*
316
+ * A code that is valid for a DIFFERENT enrolment. This is what an attacker
317
+ * with their own authenticator would submit.
318
+ */
319
+ test("rejects a code that is valid for somebody else's secret", async () => {
320
+ const victim: ReturnType<PendingEnrolmentFunction> = pendingEnrolment();
321
+ const attacker: ReturnType<PendingEnrolmentFunction> = pendingEnrolment();
322
+
323
+ findTotpById.mockResolvedValue(victim.row as never);
324
+
325
+ const result: CallValidateResult = await callValidate({
326
+ body: {
327
+ id: TOTP_ID.toString(),
328
+ code: attacker.codeFromTheAuthenticatorApp(),
329
+ },
330
+ });
331
+
332
+ expect(result.thrown).toBeInstanceOf(BadDataException);
333
+ expect(updateTotpById).not.toHaveBeenCalled();
334
+ });
335
+ });
336
+
337
+ describe("ownership", () => {
338
+ /*
339
+ * `id` is attacker-controlled. If the handler stopped comparing userId, a
340
+ * signed-in attacker could point this endpoint at any pending enrolment in
341
+ * the database and verify it with a code from their own phone.
342
+ */
343
+ test("refuses to verify an enrolment belonging to another user", async () => {
344
+ const enrolment: ReturnType<PendingEnrolmentFunction> = pendingEnrolment({
345
+ ownerId: OWNER_ID,
346
+ });
347
+
348
+ findTotpById.mockResolvedValue(enrolment.row as never);
349
+
350
+ const result: CallValidateResult = await callValidate({
351
+ body: {
352
+ id: TOTP_ID.toString(),
353
+ code: enrolment.codeFromTheAuthenticatorApp(),
354
+ },
355
+ signedInUserId: ATTACKER_ID,
356
+ });
357
+
358
+ expect(result.thrown).toBeInstanceOf(BadDataException);
359
+ expect(updateTotpById).not.toHaveBeenCalled();
360
+ });
361
+
362
+ test("does not reveal that the enrolment exists", async () => {
363
+ const enrolment: ReturnType<PendingEnrolmentFunction> = pendingEnrolment({
364
+ ownerId: OWNER_ID,
365
+ });
366
+
367
+ findTotpById.mockResolvedValue(enrolment.row as never);
368
+
369
+ const result: CallValidateResult = await callValidate({
370
+ body: {
371
+ id: TOTP_ID.toString(),
372
+ code: enrolment.codeFromTheAuthenticatorApp(),
373
+ },
374
+ signedInUserId: ATTACKER_ID,
375
+ });
376
+
377
+ expect((result.thrown as Exception).message).toBe(
378
+ "Two factor auth not found",
379
+ );
380
+ });
381
+ });
382
+
383
+ describe("when the record is not usable", () => {
384
+ test("fails when no such enrolment exists", async () => {
385
+ findTotpById.mockResolvedValue(null as never);
386
+
387
+ const result: CallValidateResult = await callValidate({
388
+ body: { id: TOTP_ID.toString(), code: "123456" },
389
+ });
390
+
391
+ expect(result.thrown).toBeInstanceOf(BadDataException);
392
+ expect(updateTotpById).not.toHaveBeenCalled();
393
+ });
394
+
395
+ test("fails when the owning user has no email to label the entry with", async () => {
396
+ const enrolment: ReturnType<PendingEnrolmentFunction> =
397
+ pendingEnrolment();
398
+
399
+ findTotpById.mockResolvedValue(enrolment.row as never);
400
+
401
+ const emailless: User = new User();
402
+ emailless.id = OWNER_ID;
403
+ findUserById.mockResolvedValue(emailless as never);
404
+
405
+ const result: CallValidateResult = await callValidate({
406
+ body: {
407
+ id: TOTP_ID.toString(),
408
+ code: enrolment.codeFromTheAuthenticatorApp(),
409
+ },
410
+ });
411
+
412
+ expect(result.thrown).toBeInstanceOf(BadDataException);
413
+ expect(updateTotpById).not.toHaveBeenCalled();
414
+ });
415
+
416
+ test("surfaces the failure through next() rather than crashing the request", async () => {
417
+ findTotpById.mockRejectedValue(new Error("database is on fire") as never);
418
+
419
+ const result: CallValidateResult = await callValidate({
420
+ body: { id: TOTP_ID.toString(), code: "123456" },
421
+ });
422
+
423
+ expect(result.nextCallCount).toBe(1);
424
+ expect(result.thrown).toBeInstanceOf(Error);
425
+ });
426
+ });
427
+ });
@@ -0,0 +1,265 @@
1
+ import { FixTotpOtpUrlAlgorithm1787700000000 } from "../../../../Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm";
2
+ import SchemaMigrations from "../../../../Server/Infrastructure/Postgres/SchemaMigrations/Index";
3
+ import TotpAuth from "../../../../Server/Utils/TotpAuth";
4
+ import Email from "../../../../Types/Email";
5
+ import * as OTPAuth from "otpauth";
6
+ import { MigrationInterface, QueryRunner } from "typeorm";
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import { beforeEach, describe, expect, test } from "@jest/globals";
10
+
11
+ /*
12
+ * The data repair behind the code fix.
13
+ *
14
+ * A row created before the fix still has `algorithm=SHA256` in the URL its QR
15
+ * code is rendered from. That no longer locks anyone out — verification tries
16
+ * SHA1 first, which is what those phones were emitting all along, and the tests
17
+ * below prove both a phone and a conforming app get in either way. What the
18
+ * rewrite buys is a stored URL that stops lying, which matters for the apps
19
+ * that DO honour the parameter and have no SHA256.
20
+ *
21
+ * Two properties are load-bearing and both are asserted here:
22
+ *
23
+ * 1. the secret is untouched, so an app that already scanned the old QR code
24
+ * keeps working (verification accepts SHA1 and legacy SHA256 either way);
25
+ * 2. verified rows are left alone. Their QR is never displayed again — the
26
+ * Verify action only exists for unverified entries — so the stored URL is
27
+ * a record of what the user's authenticator actually holds, and rewriting
28
+ * it would falsify that record for no benefit.
29
+ *
30
+ * The statements are inspected rather than executed: these tests must run
31
+ * without a database, and what can go wrong here is the WHERE clause, not
32
+ * Postgres' REPLACE().
33
+ *
34
+ * The ordering of this migration against the others is no longer asserted
35
+ * here. It used to be — as a guard hand-carried from one migration's test to
36
+ * the next, which broke the moment a migration was added without moving it.
37
+ * It now lives in SchemaMigrationsOrdering.test.ts, written against the
38
+ * registry as a whole so that it covers whichever migration is newest without
39
+ * anybody having to remember it.
40
+ */
41
+
42
+ const MIGRATION_TIMESTAMP: string = "1787700000000";
43
+
44
+ const MIGRATION_DIRECTORY: string = path.join(
45
+ __dirname,
46
+ "../../../../Server/Infrastructure/Postgres/SchemaMigrations",
47
+ );
48
+
49
+ type CapturedQueryRunner = {
50
+ runner: QueryRunner;
51
+ queries: Array<string>;
52
+ };
53
+
54
+ type MockQueryRunnerFunction = () => CapturedQueryRunner;
55
+
56
+ const mockQueryRunner: MockQueryRunnerFunction = (): CapturedQueryRunner => {
57
+ const queries: Array<string> = [];
58
+
59
+ return {
60
+ queries: queries,
61
+ runner: {
62
+ query: async (sql: string): Promise<void> => {
63
+ queries.push(sql);
64
+ },
65
+ } as unknown as QueryRunner,
66
+ };
67
+ };
68
+
69
+ const migration: FixTotpOtpUrlAlgorithm1787700000000 =
70
+ new FixTotpOtpUrlAlgorithm1787700000000();
71
+
72
+ let up: CapturedQueryRunner;
73
+ let down: CapturedQueryRunner;
74
+
75
+ describe("FixTotpOtpUrlAlgorithm migration", () => {
76
+ beforeEach(async () => {
77
+ up = mockQueryRunner();
78
+ down = mockQueryRunner();
79
+
80
+ await migration.up(up.runner);
81
+ await migration.down(down.runner);
82
+ });
83
+
84
+ /*
85
+ * TypeORM matches migrations by this name. A mismatch between the class name
86
+ * and the `name` property makes the migration run again on every boot.
87
+ */
88
+ test("names itself consistently so it is recorded as run", () => {
89
+ expect(migration.name).toBe("FixTotpOtpUrlAlgorithm1787700000000");
90
+ });
91
+
92
+ test("is registered in SchemaMigrations/Index.ts, or it never runs", () => {
93
+ expect(SchemaMigrations).toContain(FixTotpOtpUrlAlgorithm1787700000000);
94
+ });
95
+
96
+ describe("registration", () => {
97
+ test("it is registered exactly once", () => {
98
+ const occurrences: number = SchemaMigrations.filter(
99
+ (migration: new () => MigrationInterface): boolean => {
100
+ return migration.name === "FixTotpOtpUrlAlgorithm1787700000000";
101
+ },
102
+ ).length;
103
+
104
+ expect(occurrences).toBe(1);
105
+ });
106
+
107
+ /*
108
+ * ...and the timestamp in the class name is the one on disk. A class
109
+ * renamed without renaming its file (or two migrations landing on the
110
+ * same timestamp) leaves the registry pointing at a different file from
111
+ * the one this test read.
112
+ */
113
+ test("exactly one file on disk carries its timestamp, and it is this one", () => {
114
+ const matching: Array<string> = fs
115
+ .readdirSync(MIGRATION_DIRECTORY)
116
+ .filter((file: string): boolean => {
117
+ return file.startsWith(`${MIGRATION_TIMESTAMP}-`);
118
+ });
119
+
120
+ expect(matching).toEqual([
121
+ `${MIGRATION_TIMESTAMP}-FixTotpOtpUrlAlgorithm.ts`,
122
+ ]);
123
+ });
124
+ });
125
+
126
+ describe("up", () => {
127
+ test("rewrites the algorithm in the stored otpauth URL", () => {
128
+ expect(up.queries).toHaveLength(1);
129
+ expect(up.queries[0]).toContain(`UPDATE "UserTotpAuth"`);
130
+ expect(up.queries[0]).toContain(
131
+ `REPLACE("twoFactorOtpUrl", 'algorithm=SHA256', 'algorithm=SHA1')`,
132
+ );
133
+ });
134
+
135
+ /*
136
+ * The secret is the only irreplaceable thing on the row. A migration that
137
+ * regenerated it would invalidate every authenticator entry in existence.
138
+ */
139
+ test("never touches the secret", () => {
140
+ expect(up.queries[0]).not.toContain("twoFactorSecret");
141
+ });
142
+
143
+ test("only touches enrolments that are still pending", () => {
144
+ expect(up.queries[0]).toContain(`"isVerified" = false`);
145
+ });
146
+
147
+ test("only touches rows that actually carry the broken algorithm", () => {
148
+ expect(up.queries[0]).toContain(
149
+ `"twoFactorOtpUrl" LIKE '%algorithm=SHA256%'`,
150
+ );
151
+ });
152
+
153
+ test("does not delete anything", () => {
154
+ expect(up.queries[0]).not.toMatch(/\bDELETE\b|\bDROP\b|\bTRUNCATE\b/i);
155
+ });
156
+ });
157
+
158
+ describe("down", () => {
159
+ test("is the exact inverse of up", () => {
160
+ expect(down.queries).toHaveLength(1);
161
+ expect(down.queries[0]).toContain(
162
+ `REPLACE("twoFactorOtpUrl", 'algorithm=SHA1', 'algorithm=SHA256')`,
163
+ );
164
+ expect(down.queries[0]).toContain(`"isVerified" = false`);
165
+ });
166
+
167
+ test("never touches the secret either", () => {
168
+ expect(down.queries[0]).not.toContain("twoFactorSecret");
169
+ });
170
+ });
171
+
172
+ /*
173
+ * What the SQL means, applied to a real URL. Postgres' REPLACE is a plain
174
+ * substring substitution, so doing it here in JavaScript is a faithful model
175
+ * of the statement above — and it lets the result be handed to the real
176
+ * parser and the real verifier.
177
+ */
178
+ describe("the effect on a row written before the fix", () => {
179
+ const EMAIL: Email = new Email("stuck.user@example.com");
180
+
181
+ type ApplyUpFunction = (url: string) => string;
182
+
183
+ const applyUp: ApplyUpFunction = (url: string): string => {
184
+ return url.split("algorithm=SHA256").join("algorithm=SHA1");
185
+ };
186
+
187
+ type LegacyUrlFunction = (secret: string) => string;
188
+
189
+ /*
190
+ * What the old code wrote: everything as today, but SHA256.
191
+ */
192
+ const legacyUrl: LegacyUrlFunction = (secret: string): string => {
193
+ return new OTPAuth.TOTP({
194
+ issuer: "OneUptime",
195
+ label: EMAIL.toString(),
196
+ algorithm: "SHA256",
197
+ digits: 6,
198
+ period: 30,
199
+ secret: secret,
200
+ }).toString();
201
+ };
202
+
203
+ test("the repaired QR code produces codes the server accepts", () => {
204
+ const secret: string = TotpAuth.generateSecret();
205
+
206
+ const repaired: OTPAuth.TOTP = OTPAuth.URI.parse(
207
+ applyUp(legacyUrl(secret)),
208
+ ) as OTPAuth.TOTP;
209
+
210
+ expect(repaired.algorithm).toBe("SHA1");
211
+
212
+ expect(
213
+ TotpAuth.verifyToken({
214
+ secret: secret,
215
+ token: repaired.generate(),
216
+ email: EMAIL,
217
+ }),
218
+ ).toBe(true);
219
+ });
220
+
221
+ test("the secret survives the rewrite unchanged", () => {
222
+ const secret: string = TotpAuth.generateSecret();
223
+
224
+ const repaired: OTPAuth.TOTP = OTPAuth.URI.parse(
225
+ applyUp(legacyUrl(secret)),
226
+ ) as OTPAuth.TOTP;
227
+
228
+ expect(repaired.secret.base32).toBe(secret);
229
+ });
230
+
231
+ /*
232
+ * Somebody mid-enrolment may already have scanned the old SHA256 QR with
233
+ * an app that honours the parameter. Rewriting the URL must not strand
234
+ * them: their app still emits SHA256 codes, and those still verify.
235
+ */
236
+ test("an app that already scanned the old SHA256 QR still verifies", () => {
237
+ const secret: string = TotpAuth.generateSecret();
238
+
239
+ const alreadyScanned: OTPAuth.TOTP = OTPAuth.URI.parse(
240
+ legacyUrl(secret),
241
+ ) as OTPAuth.TOTP;
242
+
243
+ expect(alreadyScanned.algorithm).toBe("SHA256");
244
+
245
+ expect(
246
+ TotpAuth.verifyToken({
247
+ secret: secret,
248
+ token: alreadyScanned.generate(),
249
+ email: EMAIL,
250
+ }),
251
+ ).toBe(true);
252
+ });
253
+
254
+ test("a URL that is already SHA1 is left exactly as it was", () => {
255
+ const secret: string = TotpAuth.generateSecret();
256
+
257
+ const current: string = TotpAuth.generateUri({
258
+ secret: secret,
259
+ userEmail: EMAIL,
260
+ });
261
+
262
+ expect(applyUp(current)).toBe(current);
263
+ });
264
+ });
265
+ });