@oneuptime/common 12.0.13 → 12.0.14
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.
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
- package/Models/DatabaseModels/UserCall.ts +94 -3
- package/Models/DatabaseModels/UserEmail.ts +94 -3
- package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
- package/Models/DatabaseModels/UserPush.ts +37 -0
- package/Models/DatabaseModels/UserSMS.ts +94 -3
- package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
- package/Server/API/BaseAPI.ts +79 -14
- package/Server/API/UserCallAPI.ts +53 -45
- package/Server/API/UserEmailAPI.ts +53 -44
- package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
- package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
- package/Server/API/UserPushAPI.ts +126 -2
- package/Server/API/UserSmsAPI.ts +53 -44
- package/Server/API/UserWhatsAppAPI.ts +53 -50
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
- package/Server/Services/AnalyticsDatabaseService.ts +169 -0
- package/Server/Services/DatabaseService.ts +101 -0
- package/Server/Services/LogAggregationService.ts +5 -0
- package/Server/Services/PushNotificationService.ts +127 -12
- package/Server/Services/TeamMemberService.ts +53 -13
- package/Server/Services/UserCallService.ts +55 -24
- package/Server/Services/UserEmailService.ts +83 -17
- package/Server/Services/UserIncomingCallNumberService.ts +49 -85
- package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
- package/Server/Services/UserNotificationRuleService.ts +26 -0
- package/Server/Services/UserPushService.ts +89 -0
- package/Server/Services/UserSmsService.ts +50 -17
- package/Server/Services/UserWhatsAppService.ts +48 -15
- package/Server/Types/Database/QueryHelper.ts +30 -0
- package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
- package/Server/Utils/ChannelVerification.ts +510 -0
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
- package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
- package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
- package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
- package/Server/Utils/TotpAuth.ts +137 -9
- package/Server/Utils/UserRegistrationToken.ts +182 -0
- package/Server/Utils/VerificationCode.ts +134 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
- package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
- package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
- package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
- package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
- package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
- package/Tests/Server/API/UserSmsApi.test.ts +107 -14
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
- package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
- package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
- package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
- package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
- package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
- package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
- package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
- package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
- package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
- package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
- package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
- package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
- package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
- package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
- package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
- package/Tests/Server/Utils/Browser.test.ts +162 -0
- package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
- package/Tests/Server/Utils/Express.test.ts +371 -0
- package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
- package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
- package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
- package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
- package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
- package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
- package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
- package/Tests/Types/Call/CallRequest.test.ts +189 -0
- package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
- package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
- package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
- package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
- package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
- package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
- package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
- package/Tests/Types/TextRandomGeneration.test.ts +211 -0
- package/Tests/UI/Components/Button.test.tsx +175 -1
- package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
- package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
- package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
- package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
- package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
- package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
- package/Tests/UI/Components/List.test.tsx +9 -2
- package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
- package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
- package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
- package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
- package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
- package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
- package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
- package/Tests/UI/Components/Skeleton.test.tsx +71 -0
- package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
- package/Tests/UI/Components/Tabs.test.tsx +24 -0
- package/Tests/UI/Components/Toast.test.tsx +130 -7
- package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
- package/Tests/UI/Utils/Dropdown.test.ts +423 -0
- package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
- package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
- package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
- package/Tests/Utils/API.test.ts +16 -2
- package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
- package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
- package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
- package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
- package/Types/Permission.ts +35 -0
- package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
- package/Types/PushNotification/PushNotificationMessage.ts +15 -0
- package/Types/PushNotification/PushNotificationRequest.ts +8 -16
- package/Types/Service/ServiceLanguage.ts +144 -0
- package/Types/Text.ts +111 -19
- package/UI/Components/Button/Button.tsx +27 -14
- package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
- package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
- package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
- package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
- package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
- package/UI/Components/CommandPalette/Types.ts +49 -0
- package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/Feed/FeedItem.tsx +1 -1
- package/UI/Components/List/List.tsx +37 -5
- package/UI/Components/List/ListSkeleton.tsx +54 -0
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
- package/UI/Components/Modal/Modal.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
- package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
- package/UI/Components/Navbar/NavBar.tsx +14 -1
- package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
- package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
- package/UI/Components/SideOver/SideOver.tsx +37 -2
- package/UI/Components/Skeleton/Skeleton.tsx +53 -0
- package/UI/Components/Table/Table.tsx +74 -44
- package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
- package/UI/Components/Tabs/Tab.tsx +1 -1
- package/UI/Components/Toast/Toast.tsx +118 -53
- package/UI/Components/Toast/ToastInit.tsx +82 -32
- package/UI/Styles/Theme.css +40 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
- package/UI/Utils/ModelListCache.ts +188 -0
- package/UI/Utils/Project.ts +29 -0
- package/Utils/API.ts +6 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +69 -13
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +42 -38
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +42 -38
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
- package/build/dist/Server/API/UserPushAPI.js +95 -2
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +42 -38
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +74 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +5 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +69 -30
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +43 -7
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +54 -22
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +78 -17
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
- package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +77 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +50 -17
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
- package/build/dist/Server/Utils/ChannelVerification.js +348 -0
- package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/TotpAuth.js +127 -8
- package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
- package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
- package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
- package/build/dist/Server/Utils/VerificationCode.js +120 -0
- package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +33 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
- package/build/dist/Types/Service/ServiceLanguage.js +96 -0
- package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
- package/build/dist/Types/Text.js +82 -17
- package/build/dist/Types/Text.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +22 -14
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
- package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
- package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/List/List.js +19 -5
- package/build/dist/UI/Components/List/List.js.map +1 -1
- package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
- package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
- package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +35 -24
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
- package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
- package/build/dist/UI/Components/Tabs/Tab.js +1 -1
- package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
- package/build/dist/UI/Components/Toast/Toast.js +77 -23
- package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
- package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelListCache.js +126 -0
- package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
- package/build/dist/UI/Utils/Project.js +24 -1
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/Utils/API.js +6 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/jest.config.json +1 -1
- package/package.json +2 -3
- package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
- package/Tests/__mocks__/otpauth.js +0 -30
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import UserCallAPI from "../../../Server/API/UserCallAPI";
|
|
2
|
+
import UserEmailAPI from "../../../Server/API/UserEmailAPI";
|
|
3
|
+
import UserIncomingCallNumberAPI from "../../../Server/API/UserIncomingCallNumberAPI";
|
|
4
|
+
import UserSmsAPI from "../../../Server/API/UserSmsAPI";
|
|
5
|
+
import UserWhatsAppAPI from "../../../Server/API/UserWhatsAppAPI";
|
|
6
|
+
import UserCallService from "../../../Server/Services/UserCallService";
|
|
7
|
+
import UserEmailService from "../../../Server/Services/UserEmailService";
|
|
8
|
+
import UserIncomingCallNumberService from "../../../Server/Services/UserIncomingCallNumberService";
|
|
9
|
+
import UserNotificationRuleService from "../../../Server/Services/UserNotificationRuleService";
|
|
10
|
+
import UserSmsService from "../../../Server/Services/UserSmsService";
|
|
11
|
+
import UserWhatsAppService from "../../../Server/Services/UserWhatsAppService";
|
|
12
|
+
import ChannelVerification, {
|
|
13
|
+
MAX_VERIFICATION_ATTEMPTS,
|
|
14
|
+
VerifiableChannelFields,
|
|
15
|
+
} from "../../../Server/Utils/ChannelVerification";
|
|
16
|
+
import VerificationCode from "../../../Server/Utils/VerificationCode";
|
|
17
|
+
import {
|
|
18
|
+
NextFunction,
|
|
19
|
+
OneUptimeRequest,
|
|
20
|
+
OneUptimeResponse,
|
|
21
|
+
} from "../../../Server/Utils/Express";
|
|
22
|
+
import Response from "../../../Server/Utils/Response";
|
|
23
|
+
import { mockRouter } from "./Helpers";
|
|
24
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
25
|
+
import JSONWebTokenData from "../../../Types/JsonWebTokenData";
|
|
26
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
27
|
+
import { beforeEach, describe, expect, it } from "@jest/globals";
|
|
28
|
+
|
|
29
|
+
jest.mock("../../../Server/Utils/Express", () => {
|
|
30
|
+
return {
|
|
31
|
+
getRouter: () => {
|
|
32
|
+
return mockRouter;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
jest.mock("../../../Server/Utils/Response", () => {
|
|
38
|
+
return {
|
|
39
|
+
sendEntityArrayResponse: jest.fn(),
|
|
40
|
+
sendJsonObjectResponse: jest.fn(),
|
|
41
|
+
sendEmptySuccessResponse: jest.fn(),
|
|
42
|
+
sendEntityResponse: jest.fn(),
|
|
43
|
+
sendErrorResponse: jest.fn(),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
jest.mock("../../../Server/Services/UserEmailService");
|
|
48
|
+
jest.mock("../../../Server/Services/UserSmsService");
|
|
49
|
+
jest.mock("../../../Server/Services/UserCallService");
|
|
50
|
+
jest.mock("../../../Server/Services/UserWhatsAppService");
|
|
51
|
+
jest.mock("../../../Server/Services/UserIncomingCallNumberService");
|
|
52
|
+
jest.mock("../../../Server/Services/UserNotificationRuleService");
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* GHSA-5cr8-vph4-3hrf, at the HTTP layer, for every affected channel at once.
|
|
56
|
+
*
|
|
57
|
+
* The advisory's own words are "the same pattern is used by the user email,
|
|
58
|
+
* SMS, incoming call, and WhatsApp verification APIs" — five copies of the
|
|
59
|
+
* same handler, so a fix applied to one of them proves nothing about the other
|
|
60
|
+
* four. This file therefore runs one table of cases against all five routes,
|
|
61
|
+
* and a channel that regresses on its own fails here rather than quietly
|
|
62
|
+
* shipping.
|
|
63
|
+
*
|
|
64
|
+
* The service is faked at the row level rather than method-by-method, so these
|
|
65
|
+
* exercise the real ChannelVerification state machine reached through the real
|
|
66
|
+
* route handler. A test that stubbed verifyCode would pass against a handler
|
|
67
|
+
* that never called it.
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
const ITEM_ID: string = "9d3f0e2a-1111-4111-8111-111111111111";
|
|
71
|
+
const OWNER_ID: string = "9d3f0e2a-2222-4222-8222-222222222222";
|
|
72
|
+
const OTHER_USER_ID: string = "9d3f0e2a-3333-4333-8333-333333333333";
|
|
73
|
+
const PROJECT_ID: string = "9d3f0e2a-4444-4444-8444-444444444444";
|
|
74
|
+
|
|
75
|
+
const CORRECT_CODE: string = "424242";
|
|
76
|
+
|
|
77
|
+
interface FakeRow extends VerifiableChannelFields {
|
|
78
|
+
_id?: string | undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type AnyService = Record<string, unknown>;
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* Point a mocked service at an in-memory row: reads return it, writes merge
|
|
85
|
+
* into it, and the attempt counter really counts.
|
|
86
|
+
*/
|
|
87
|
+
const installFakeRow: (data: { service: unknown; row: FakeRow | null }) => {
|
|
88
|
+
current: FakeRow | null;
|
|
89
|
+
} = (data: { service: unknown; row: FakeRow | null }) => {
|
|
90
|
+
const store: { current: FakeRow | null } = { current: data.row };
|
|
91
|
+
const service: AnyService = data.service as AnyService;
|
|
92
|
+
|
|
93
|
+
service["findOneById"] = jest.fn().mockImplementation(() => {
|
|
94
|
+
return Promise.resolve(store.current ? { ...store.current } : null);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
service["updateOneById"] = jest
|
|
98
|
+
.fn()
|
|
99
|
+
.mockImplementation((update: { data: Record<string, unknown> }) => {
|
|
100
|
+
if (store.current) {
|
|
101
|
+
store.current = { ...store.current, ...(update.data as FakeRow) };
|
|
102
|
+
}
|
|
103
|
+
return Promise.resolve(1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
service["atomicIncrementColumnValueByOneAndGetValue"] = jest
|
|
107
|
+
.fn()
|
|
108
|
+
.mockImplementation((input: { columnName: string }) => {
|
|
109
|
+
if (!store.current) {
|
|
110
|
+
return Promise.reject(new Error("row not found"));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const next: number =
|
|
114
|
+
((store.current as unknown as Record<string, number>)[
|
|
115
|
+
input.columnName
|
|
116
|
+
] || 0) + 1;
|
|
117
|
+
|
|
118
|
+
(store.current as unknown as Record<string, number>)[input.columnName] =
|
|
119
|
+
next;
|
|
120
|
+
|
|
121
|
+
return Promise.resolve(next);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
service["getModel"] = jest.fn().mockReturnValue({ tableName: "FakeTable" });
|
|
125
|
+
|
|
126
|
+
service["resendVerificationCode"] = jest.fn().mockResolvedValue(undefined);
|
|
127
|
+
|
|
128
|
+
return store;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const buildRow: (overrides?: Partial<FakeRow>) => FakeRow = (
|
|
132
|
+
overrides: Partial<FakeRow> = {},
|
|
133
|
+
) => {
|
|
134
|
+
return {
|
|
135
|
+
_id: ITEM_ID,
|
|
136
|
+
userId: new ObjectID(OWNER_ID),
|
|
137
|
+
projectId: new ObjectID(PROJECT_ID),
|
|
138
|
+
isVerified: false,
|
|
139
|
+
verificationCode: VerificationCode.hashCode({
|
|
140
|
+
code: CORRECT_CODE,
|
|
141
|
+
channelId: new ObjectID(ITEM_ID),
|
|
142
|
+
}),
|
|
143
|
+
verificationCodeExpiresAt: ChannelVerification.getExpiresAt(),
|
|
144
|
+
verificationFailedAttempts: 0,
|
|
145
|
+
verificationCodeSentAt: new Date(),
|
|
146
|
+
...overrides,
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
interface Channel {
|
|
151
|
+
name: string;
|
|
152
|
+
verifyPath: string;
|
|
153
|
+
resendPath: string;
|
|
154
|
+
service: unknown;
|
|
155
|
+
build: () => void;
|
|
156
|
+
createsDefaultRules: boolean;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const CHANNELS: Array<Channel> = [
|
|
160
|
+
{
|
|
161
|
+
name: "UserEmailAPI",
|
|
162
|
+
verifyPath: "/user-email/verify",
|
|
163
|
+
resendPath: "/user-email/resend-verification-code",
|
|
164
|
+
service: UserEmailService,
|
|
165
|
+
build: () => {
|
|
166
|
+
new UserEmailAPI();
|
|
167
|
+
},
|
|
168
|
+
createsDefaultRules: true,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "UserSmsAPI",
|
|
172
|
+
verifyPath: "/user-sms/verify",
|
|
173
|
+
resendPath: "/user-sms/resend-verification-code",
|
|
174
|
+
service: UserSmsService,
|
|
175
|
+
build: () => {
|
|
176
|
+
new UserSmsAPI();
|
|
177
|
+
},
|
|
178
|
+
createsDefaultRules: true,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "UserCallAPI",
|
|
182
|
+
verifyPath: "/user-call/verify",
|
|
183
|
+
resendPath: "/user-call/resend-verification-code",
|
|
184
|
+
service: UserCallService,
|
|
185
|
+
build: () => {
|
|
186
|
+
new UserCallAPI();
|
|
187
|
+
},
|
|
188
|
+
createsDefaultRules: true,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "UserWhatsAppAPI",
|
|
192
|
+
verifyPath: "/user-whatsapp/verify",
|
|
193
|
+
resendPath: "/user-whatsapp/resend-verification-code",
|
|
194
|
+
service: UserWhatsAppService,
|
|
195
|
+
build: () => {
|
|
196
|
+
new UserWhatsAppAPI();
|
|
197
|
+
},
|
|
198
|
+
createsDefaultRules: true,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "UserIncomingCallNumberAPI",
|
|
202
|
+
verifyPath: "/user-incoming-call-number/verify",
|
|
203
|
+
resendPath: "/user-incoming-call-number/resend-verification-code",
|
|
204
|
+
service: UserIncomingCallNumberService,
|
|
205
|
+
build: () => {
|
|
206
|
+
new UserIncomingCallNumberAPI();
|
|
207
|
+
},
|
|
208
|
+
createsDefaultRules: false,
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
describe.each(CHANNELS)("$name verification routes", (channel: Channel) => {
|
|
213
|
+
let mockRequest: OneUptimeRequest;
|
|
214
|
+
let mockResponse: OneUptimeResponse;
|
|
215
|
+
let nextFunction: NextFunction;
|
|
216
|
+
let store: { current: FakeRow | null };
|
|
217
|
+
|
|
218
|
+
const callVerify: (body: Record<string, unknown>) => Promise<void> = async (
|
|
219
|
+
body: Record<string, unknown>,
|
|
220
|
+
) => {
|
|
221
|
+
mockRequest.body = body;
|
|
222
|
+
|
|
223
|
+
await mockRouter
|
|
224
|
+
.match("post", channel.verifyPath)
|
|
225
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const callResend: (body: Record<string, unknown>) => Promise<void> = async (
|
|
229
|
+
body: Record<string, unknown>,
|
|
230
|
+
) => {
|
|
231
|
+
mockRequest.body = body;
|
|
232
|
+
|
|
233
|
+
await mockRouter
|
|
234
|
+
.match("post", channel.resendPath)
|
|
235
|
+
.handlerFunction(mockRequest, mockResponse, nextFunction);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const lastError: () => BadDataException = () => {
|
|
239
|
+
const calls: Array<Array<unknown>> = (
|
|
240
|
+
Response.sendErrorResponse as unknown as jest.Mock
|
|
241
|
+
).mock.calls as Array<Array<unknown>>;
|
|
242
|
+
|
|
243
|
+
return calls[calls.length - 1]?.[2] as BadDataException;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
beforeEach(() => {
|
|
247
|
+
jest.clearAllMocks();
|
|
248
|
+
mockRouter.routes.length = 0;
|
|
249
|
+
|
|
250
|
+
channel.build();
|
|
251
|
+
|
|
252
|
+
mockRequest = {} as OneUptimeRequest;
|
|
253
|
+
mockRequest.userAuthorization = {
|
|
254
|
+
userId: new ObjectID(OWNER_ID),
|
|
255
|
+
} as JSONWebTokenData;
|
|
256
|
+
|
|
257
|
+
mockResponse = {
|
|
258
|
+
send: jest.fn(),
|
|
259
|
+
json: jest.fn(),
|
|
260
|
+
status: jest.fn().mockReturnThis(),
|
|
261
|
+
setHeader: jest.fn(),
|
|
262
|
+
} as unknown as OneUptimeResponse;
|
|
263
|
+
|
|
264
|
+
nextFunction = jest.fn();
|
|
265
|
+
|
|
266
|
+
store = installFakeRow({ service: channel.service, row: buildRow() });
|
|
267
|
+
|
|
268
|
+
(
|
|
269
|
+
UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod as unknown as jest.Mock
|
|
270
|
+
).mockResolvedValue(undefined as never);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe("POST verify", () => {
|
|
274
|
+
it("requires an item ID", async () => {
|
|
275
|
+
await callVerify({});
|
|
276
|
+
|
|
277
|
+
expect(Response.sendErrorResponse).toHaveBeenCalledWith(
|
|
278
|
+
mockRequest,
|
|
279
|
+
mockResponse,
|
|
280
|
+
new BadDataException("Invalid item ID"),
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("requires a code", async () => {
|
|
285
|
+
await callVerify({ itemId: ITEM_ID });
|
|
286
|
+
|
|
287
|
+
expect(lastError().message).toBe("Invalid code");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("refuses an unknown item", async () => {
|
|
291
|
+
store.current = null;
|
|
292
|
+
|
|
293
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
294
|
+
|
|
295
|
+
expect(lastError().message).toBe("Item not found");
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
/*
|
|
299
|
+
* Ownership. A row can legitimately hold somebody else's address — that
|
|
300
|
+
* is how "add my phone" works — so this is what keeps one user from
|
|
301
|
+
* verifying another user's channel.
|
|
302
|
+
*/
|
|
303
|
+
it("refuses a caller who does not own the item", async () => {
|
|
304
|
+
mockRequest.userAuthorization = {
|
|
305
|
+
userId: new ObjectID(OTHER_USER_ID),
|
|
306
|
+
} as JSONWebTokenData;
|
|
307
|
+
|
|
308
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
309
|
+
|
|
310
|
+
expect(lastError().message).toBe("Invalid user ID");
|
|
311
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("refuses a wrong code", async () => {
|
|
315
|
+
await callVerify({ itemId: ITEM_ID, code: "111111" });
|
|
316
|
+
|
|
317
|
+
expect(lastError().message).toBe("Invalid code");
|
|
318
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("accepts the right code and marks the row verified", async () => {
|
|
322
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
323
|
+
|
|
324
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
325
|
+
mockRequest,
|
|
326
|
+
mockResponse,
|
|
327
|
+
);
|
|
328
|
+
expect(store.current?.isVerified).toBe(true);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
/*
|
|
332
|
+
* The stored column is a digest now. This is the test that fails if
|
|
333
|
+
* anybody reintroduces `item.verificationCode === req.body.code`.
|
|
334
|
+
*/
|
|
335
|
+
it("does not accept the stored column value as the code", async () => {
|
|
336
|
+
await callVerify({
|
|
337
|
+
itemId: ITEM_ID,
|
|
338
|
+
code: store.current?.verificationCode as string,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(lastError().message).toBe("Invalid code");
|
|
342
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("refuses a code that has expired", async () => {
|
|
346
|
+
store.current = buildRow({
|
|
347
|
+
verificationCodeExpiresAt: new Date(Date.now() - 1000),
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
351
|
+
|
|
352
|
+
expect(lastError().message).toContain("expired");
|
|
353
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
/*
|
|
357
|
+
* Rows written before the expiry column existed carry a plaintext code
|
|
358
|
+
* and no expiry. They must read as "ask for a new code", not as a
|
|
359
|
+
* permanently valid challenge.
|
|
360
|
+
*/
|
|
361
|
+
it("refuses a legacy row that carries no expiry", async () => {
|
|
362
|
+
store.current = buildRow({ verificationCodeExpiresAt: undefined });
|
|
363
|
+
|
|
364
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
365
|
+
|
|
366
|
+
expect(lastError().message).toContain("expired");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("refuses a row that is already verified", async () => {
|
|
370
|
+
store.current = buildRow({ isVerified: true });
|
|
371
|
+
|
|
372
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
373
|
+
|
|
374
|
+
expect(lastError().message).toBe("This is already verified");
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
/*
|
|
378
|
+
* The advisory's reproduction, run against the route: submit many
|
|
379
|
+
* incorrect values and check that the attempts are not all processed,
|
|
380
|
+
* that the stored code does not survive, and that the correct code
|
|
381
|
+
* submitted after the failures no longer verifies the row.
|
|
382
|
+
*/
|
|
383
|
+
describe("brute force", () => {
|
|
384
|
+
it("stops accepting guesses after the attempt limit", async () => {
|
|
385
|
+
for (let i: number = 0; i < MAX_VERIFICATION_ATTEMPTS; i++) {
|
|
386
|
+
await callVerify({
|
|
387
|
+
itemId: ITEM_ID,
|
|
388
|
+
code: i.toString().padStart(6, "0"),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
expect(lastError().message).toBe("Invalid code");
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
await callVerify({ itemId: ITEM_ID, code: "999999" });
|
|
395
|
+
|
|
396
|
+
expect(lastError().message).toContain("Too many incorrect attempts");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("refuses the correct code once the limit has been reached", async () => {
|
|
400
|
+
for (let i: number = 0; i < MAX_VERIFICATION_ATTEMPTS; i++) {
|
|
401
|
+
await callVerify({ itemId: ITEM_ID, code: "111111" });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
405
|
+
|
|
406
|
+
expect(lastError().message).toContain("Too many incorrect attempts");
|
|
407
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("burns the stored challenge rather than merely pausing the caller", async () => {
|
|
411
|
+
const originalDigest: string | undefined =
|
|
412
|
+
store.current?.verificationCode;
|
|
413
|
+
|
|
414
|
+
for (let i: number = 0; i < MAX_VERIFICATION_ATTEMPTS; i++) {
|
|
415
|
+
await callVerify({ itemId: ITEM_ID, code: "111111" });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
expect(store.current?.verificationCode).not.toBe(originalDigest);
|
|
419
|
+
expect(store.current?.verificationCodeExpiresAt).toBeNull();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("cannot be walked to a verification", async () => {
|
|
423
|
+
for (let guess: number = 0; guess < 300; guess++) {
|
|
424
|
+
await callVerify({
|
|
425
|
+
itemId: ITEM_ID,
|
|
426
|
+
code: guess.toString().padStart(6, "0"),
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
|
|
431
|
+
expect(store.current?.isVerified).toBeFalsy();
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
if (channel.createsDefaultRules) {
|
|
436
|
+
it("creates the default notification rules once verified", async () => {
|
|
437
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
438
|
+
|
|
439
|
+
expect(
|
|
440
|
+
UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod,
|
|
441
|
+
).toHaveBeenCalled();
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("does not create notification rules for a failed verification", async () => {
|
|
445
|
+
await callVerify({ itemId: ITEM_ID, code: "111111" });
|
|
446
|
+
|
|
447
|
+
expect(
|
|
448
|
+
UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod,
|
|
449
|
+
).not.toHaveBeenCalled();
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it("still reports success if rule creation fails", async () => {
|
|
453
|
+
(
|
|
454
|
+
UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod as unknown as jest.Mock
|
|
455
|
+
).mockRejectedValue(new Error("boom") as never);
|
|
456
|
+
|
|
457
|
+
await callVerify({ itemId: ITEM_ID, code: CORRECT_CODE });
|
|
458
|
+
|
|
459
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalled();
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
describe("POST resend-verification-code", () => {
|
|
465
|
+
it("requires an item ID", async () => {
|
|
466
|
+
await callResend({});
|
|
467
|
+
|
|
468
|
+
expect(lastError().message).toBe("Invalid item ID");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("refuses an unknown item", async () => {
|
|
472
|
+
store.current = null;
|
|
473
|
+
|
|
474
|
+
await callResend({ itemId: ITEM_ID });
|
|
475
|
+
|
|
476
|
+
expect(lastError().message).toBe("Item not found");
|
|
477
|
+
expect(
|
|
478
|
+
(channel.service as AnyService)["resendVerificationCode"],
|
|
479
|
+
).not.toHaveBeenCalled();
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
/*
|
|
483
|
+
* Without this the resend route is a way to make somebody else's device
|
|
484
|
+
* ring on demand, at the project's expense.
|
|
485
|
+
*/
|
|
486
|
+
it("refuses to send a code for a row the caller does not own", async () => {
|
|
487
|
+
mockRequest.userAuthorization = {
|
|
488
|
+
userId: new ObjectID(OTHER_USER_ID),
|
|
489
|
+
} as JSONWebTokenData;
|
|
490
|
+
|
|
491
|
+
await callResend({ itemId: ITEM_ID });
|
|
492
|
+
|
|
493
|
+
expect(lastError().message).toBe("Invalid user ID");
|
|
494
|
+
expect(
|
|
495
|
+
(channel.service as AnyService)["resendVerificationCode"],
|
|
496
|
+
).not.toHaveBeenCalled();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("sends a code for a row the caller owns", async () => {
|
|
500
|
+
await callResend({ itemId: ITEM_ID });
|
|
501
|
+
|
|
502
|
+
expect(
|
|
503
|
+
(channel.service as AnyService)["resendVerificationCode"],
|
|
504
|
+
).toHaveBeenCalledWith(ITEM_ID);
|
|
505
|
+
expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
|
|
506
|
+
mockRequest,
|
|
507
|
+
mockResponse,
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
/*
|
|
513
|
+
* The limiter is what bounds the two things a per-row counter cannot see:
|
|
514
|
+
* a caller rotating rows to farm fresh attempt budgets, and the volume of
|
|
515
|
+
* real messages sent to somebody who never asked for them. A route that
|
|
516
|
+
* loses it still passes every functional test above, so its registration
|
|
517
|
+
* is asserted directly.
|
|
518
|
+
*/
|
|
519
|
+
describe("rate limiting", () => {
|
|
520
|
+
it("registers a limiter in front of the verify handler", () => {
|
|
521
|
+
expect(
|
|
522
|
+
mockRouter.match("post", channel.verifyPath).middlewares.length,
|
|
523
|
+
).toBeGreaterThanOrEqual(2);
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it("registers a limiter in front of the resend handler", () => {
|
|
527
|
+
expect(
|
|
528
|
+
mockRouter.match("post", channel.resendPath).middlewares.length,
|
|
529
|
+
).toBeGreaterThanOrEqual(2);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("runs the limiter after user authorization, so it can key on the user", () => {
|
|
533
|
+
const middlewares: Array<unknown> = mockRouter.match(
|
|
534
|
+
"post",
|
|
535
|
+
channel.verifyPath,
|
|
536
|
+
).middlewares;
|
|
537
|
+
|
|
538
|
+
/* UserMiddleware.getUserMiddleware is registered first. */
|
|
539
|
+
expect(middlewares[0]).not.toBe(middlewares[1]);
|
|
540
|
+
expect(typeof middlewares[1]).toBe("function");
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
});
|