@oneuptime/common 14.0.7 → 14.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1535 -65
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +68 -18
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +242 -5
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1052 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -7,7 +7,18 @@ import Exception from "../../../../../Types/Exception/Exception";
|
|
|
7
7
|
import { JSONObject } from "../../../../../Types/JSON";
|
|
8
8
|
import ObjectID from "../../../../../Types/ObjectID";
|
|
9
9
|
import nodemailer from "nodemailer";
|
|
10
|
-
import
|
|
10
|
+
import SMTPTransport from "nodemailer/lib/smtp-transport";
|
|
11
|
+
import dns from "dns";
|
|
12
|
+
import { EventEmitter } from "events";
|
|
13
|
+
import net from "net";
|
|
14
|
+
import {
|
|
15
|
+
afterEach,
|
|
16
|
+
beforeEach,
|
|
17
|
+
describe,
|
|
18
|
+
expect,
|
|
19
|
+
jest,
|
|
20
|
+
test,
|
|
21
|
+
} from "@jest/globals";
|
|
11
22
|
|
|
12
23
|
jest.mock("nodemailer", () => {
|
|
13
24
|
return {
|
|
@@ -70,8 +81,146 @@ function validArgs(overrides: JSONObject = {}): JSONObject {
|
|
|
70
81
|
const createTransportMock: ReturnType<typeof jest.fn> =
|
|
71
82
|
nodemailer.createTransport as unknown as ReturnType<typeof jest.fn>;
|
|
72
83
|
|
|
84
|
+
type LookupSpy = jest.SpiedFunction<
|
|
85
|
+
(
|
|
86
|
+
hostname: string,
|
|
87
|
+
options: { all: true },
|
|
88
|
+
) => Promise<Array<{ address: string; family: number }>>
|
|
89
|
+
>;
|
|
90
|
+
|
|
91
|
+
const BILLING_ENV: string = "BILLING_ENABLED";
|
|
92
|
+
const BLOCK_PRIVATE_ENV: string = "DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES";
|
|
93
|
+
|
|
94
|
+
let lookupSpy: LookupSpy;
|
|
95
|
+
let originalBilling: string | undefined;
|
|
96
|
+
let originalBlockPrivate: string | undefined;
|
|
97
|
+
|
|
98
|
+
function restoreEnv(name: string, value: string | undefined): void {
|
|
99
|
+
if (value === undefined) {
|
|
100
|
+
delete process.env[name];
|
|
101
|
+
} else {
|
|
102
|
+
process.env[name] = value;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function transportOptions(): SMTPTransport.Options {
|
|
107
|
+
return createTransportMock.mock.calls[0]![0] as SMTPTransport.Options;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface SocketTarget {
|
|
111
|
+
host: string;
|
|
112
|
+
port: number;
|
|
113
|
+
autoSelectFamily: boolean | undefined;
|
|
114
|
+
// Everything the socket's lookup will ever answer, for any name.
|
|
115
|
+
addresses: Array<{ address: string; family: number }>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type PinnedLookup = (
|
|
119
|
+
hostname: string,
|
|
120
|
+
options: { all: boolean },
|
|
121
|
+
callback: (
|
|
122
|
+
error: Error | null,
|
|
123
|
+
addresses: Array<{ address: string; family: number }>,
|
|
124
|
+
) => void,
|
|
125
|
+
) => void;
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
* nodemailer is mocked here, so nothing ever calls the getSocket hook the
|
|
129
|
+
* component hands it. Call it, catch the net.connect it makes, and ask that
|
|
130
|
+
* socket's lookup what it will connect to — for the typed name and for an
|
|
131
|
+
* unrelated one, to show it can reach nothing but the validated addresses.
|
|
132
|
+
* EmailSmtpPinning.test.ts drives the same path through real sockets.
|
|
133
|
+
*/
|
|
134
|
+
async function socketTarget(
|
|
135
|
+
options: SMTPTransport.Options,
|
|
136
|
+
): Promise<SocketTarget> {
|
|
137
|
+
interface CapturedConnect {
|
|
138
|
+
host: string;
|
|
139
|
+
port: number;
|
|
140
|
+
autoSelectFamily?: boolean;
|
|
141
|
+
lookup: PinnedLookup;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const seen: { connect?: CapturedConnect } = {};
|
|
145
|
+
|
|
146
|
+
const connectSpy: ReturnType<typeof jest.spyOn> = jest
|
|
147
|
+
.spyOn(net, "connect")
|
|
148
|
+
.mockImplementation(((given: CapturedConnect): net.Socket => {
|
|
149
|
+
seen.connect = given;
|
|
150
|
+
return Object.assign(new EventEmitter(), {
|
|
151
|
+
setTimeout: jest.fn(),
|
|
152
|
+
destroy: jest.fn(),
|
|
153
|
+
}) as unknown as net.Socket;
|
|
154
|
+
}) as never);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
expect(options.getSocket).toBeInstanceOf(Function);
|
|
158
|
+
options.getSocket!(options, () => {});
|
|
159
|
+
} finally {
|
|
160
|
+
connectSpy.mockRestore();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
expect(seen.connect).toBeDefined();
|
|
164
|
+
const captured: CapturedConnect = seen.connect!;
|
|
165
|
+
|
|
166
|
+
const answer: (
|
|
167
|
+
hostname: string,
|
|
168
|
+
) => Promise<Array<{ address: string; family: number }>> = (
|
|
169
|
+
hostname: string,
|
|
170
|
+
) => {
|
|
171
|
+
return new Promise(
|
|
172
|
+
(
|
|
173
|
+
resolve: (value: Array<{ address: string; family: number }>) => void,
|
|
174
|
+
reject: (error: Error) => void,
|
|
175
|
+
) => {
|
|
176
|
+
captured.lookup(
|
|
177
|
+
hostname,
|
|
178
|
+
{ all: true },
|
|
179
|
+
(
|
|
180
|
+
error: Error | null,
|
|
181
|
+
addresses: Array<{ address: string; family: number }>,
|
|
182
|
+
) => {
|
|
183
|
+
return error ? reject(error) : resolve(addresses);
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const addresses: Array<{ address: string; family: number }> = await answer(
|
|
191
|
+
captured.host,
|
|
192
|
+
);
|
|
193
|
+
expect(await answer("anything-else.example")).toEqual(addresses);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
host: captured.host,
|
|
197
|
+
port: captured.port,
|
|
198
|
+
autoSelectFamily: captured.autoSelectFamily,
|
|
199
|
+
addresses,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
73
203
|
beforeEach(() => {
|
|
74
204
|
jest.clearAllMocks();
|
|
205
|
+
|
|
206
|
+
originalBilling = process.env[BILLING_ENV];
|
|
207
|
+
originalBlockPrivate = process.env[BLOCK_PRIVATE_ENV];
|
|
208
|
+
delete process.env[BILLING_ENV];
|
|
209
|
+
delete process.env[BLOCK_PRIVATE_ENV];
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* The component resolves the SMTP host through the egress guard before it
|
|
213
|
+
* connects. Answer with a public address unless a test says otherwise, so
|
|
214
|
+
* nothing here depends on the machine's resolver.
|
|
215
|
+
*/
|
|
216
|
+
lookupSpy = jest.spyOn(dns.promises, "lookup") as unknown as LookupSpy;
|
|
217
|
+
lookupSpy.mockResolvedValue([{ address: "93.184.216.34", family: 4 }]);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
afterEach(() => {
|
|
221
|
+
lookupSpy.mockRestore();
|
|
222
|
+
restoreEnv(BILLING_ENV, originalBilling);
|
|
223
|
+
restoreEnv(BLOCK_PRIVATE_ENV, originalBlockPrivate);
|
|
75
224
|
});
|
|
76
225
|
|
|
77
226
|
describe("Email workflow component failures", () => {
|
|
@@ -149,3 +298,344 @@ describe("Email workflow component failures", () => {
|
|
|
149
298
|
expect(sendMail).toHaveBeenCalledTimes(1);
|
|
150
299
|
});
|
|
151
300
|
});
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
* The SMTP host is typed into the workflow node by any project member who can
|
|
304
|
+
* edit workflows, and whatever the socket reports comes back through the
|
|
305
|
+
* Error port. These pin the component to the guard, and the policy, that a
|
|
306
|
+
* project's own SMTP server gets in MailService: private ranges refused on
|
|
307
|
+
* SaaS and allowed on self-hosted installs, loopback / link-local / metadata
|
|
308
|
+
* refused everywhere, and the socket opened to the address that was checked.
|
|
309
|
+
*/
|
|
310
|
+
describe("Email workflow component egress guard", () => {
|
|
311
|
+
function mockSendMail(): ReturnType<typeof jest.fn> {
|
|
312
|
+
const sendMail: ReturnType<typeof jest.fn> = jest
|
|
313
|
+
.fn()
|
|
314
|
+
.mockResolvedValue(undefined as never);
|
|
315
|
+
createTransportMock.mockReturnValue({ sendMail });
|
|
316
|
+
return sendMail;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Returns the error the workflow saw, for tests that compare refusals.
|
|
320
|
+
async function expectRefused(
|
|
321
|
+
args: JSONObject,
|
|
322
|
+
reason: string,
|
|
323
|
+
): Promise<string> {
|
|
324
|
+
const sendMail: ReturnType<typeof jest.fn> = mockSendMail();
|
|
325
|
+
const fixture: OptionsFixture = makeOptions();
|
|
326
|
+
|
|
327
|
+
const result: RunReturnType = await new Email().run(
|
|
328
|
+
validArgs(args),
|
|
329
|
+
fixture.options,
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
expect(result.executePort?.id).toBe("error");
|
|
333
|
+
expect(String(result.returnValues["error"])).toContain(reason);
|
|
334
|
+
expect(fixture.log).toHaveBeenCalledWith(result.returnValues["error"]);
|
|
335
|
+
expect(fixture.onError).not.toHaveBeenCalled();
|
|
336
|
+
expect(createTransportMock).not.toHaveBeenCalled();
|
|
337
|
+
expect(sendMail).not.toHaveBeenCalled();
|
|
338
|
+
|
|
339
|
+
return String(result.returnValues["error"]);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function expectSent(args: JSONObject): Promise<SMTPTransport.Options> {
|
|
343
|
+
const sendMail: ReturnType<typeof jest.fn> = mockSendMail();
|
|
344
|
+
const fixture: OptionsFixture = makeOptions();
|
|
345
|
+
|
|
346
|
+
const result: RunReturnType = await new Email().run(
|
|
347
|
+
validArgs(args),
|
|
348
|
+
fixture.options,
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
expect(result.executePort?.id).toBe("success");
|
|
352
|
+
expect(createTransportMock).toHaveBeenCalledTimes(1);
|
|
353
|
+
expect(sendMail).toHaveBeenCalledTimes(1);
|
|
354
|
+
|
|
355
|
+
return transportOptions();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
describe("on SaaS (BILLING_ENABLED=true)", () => {
|
|
359
|
+
beforeEach(() => {
|
|
360
|
+
process.env[BILLING_ENV] = "true";
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test.each([
|
|
364
|
+
["10.0.0.25", "private network address"],
|
|
365
|
+
["172.16.4.2", "private network address"],
|
|
366
|
+
["192.168.1.10", "private network address"],
|
|
367
|
+
["100.64.0.7", "carrier-grade NAT address"],
|
|
368
|
+
["[fd12:3456::25]", "private network address"],
|
|
369
|
+
["::ffff:10.0.0.25", "private network address"],
|
|
370
|
+
])(
|
|
371
|
+
"refuses the private SMTP host %s without connecting",
|
|
372
|
+
async (host: string, reason: string) => {
|
|
373
|
+
await expectRefused({ "smtp-host": host }, reason);
|
|
374
|
+
expect(lookupSpy).not.toHaveBeenCalled();
|
|
375
|
+
},
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
test("refuses a hostname that resolves to a private address without saying where it points", async () => {
|
|
379
|
+
lookupSpy.mockResolvedValue([{ address: "10.20.30.40", family: 4 }]);
|
|
380
|
+
|
|
381
|
+
const error: string = await expectRefused(
|
|
382
|
+
{ "smtp-host": "smtp.internal.example" },
|
|
383
|
+
"SMTP server host smtp.internal.example could not be reached.",
|
|
384
|
+
);
|
|
385
|
+
expect(error).not.toContain("10.20.30.40");
|
|
386
|
+
expect(error).not.toContain("private network");
|
|
387
|
+
expect(lookupSpy).toHaveBeenCalledWith("smtp.internal.example", {
|
|
388
|
+
all: true,
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test("refuses a hostname when any one of its addresses is private", async () => {
|
|
393
|
+
lookupSpy.mockResolvedValue([
|
|
394
|
+
{ address: "93.184.216.34", family: 4 },
|
|
395
|
+
{ address: "192.168.0.5", family: 4 },
|
|
396
|
+
]);
|
|
397
|
+
|
|
398
|
+
const error: string = await expectRefused(
|
|
399
|
+
{ "smtp-host": "smtp.mixed.example" },
|
|
400
|
+
"SMTP server host smtp.mixed.example could not be reached.",
|
|
401
|
+
);
|
|
402
|
+
expect(error).not.toContain("192.168.0.5");
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
/*
|
|
406
|
+
* The error lands in the workflow log, which every member who can read
|
|
407
|
+
* the workflow sees. If "no such name" and "that name is internal" read
|
|
408
|
+
* differently there, the SMTP host field is a free DNS oracle for the
|
|
409
|
+
* network the workflow worker runs in.
|
|
410
|
+
*/
|
|
411
|
+
test("a missing name and an internal name produce the same error", async () => {
|
|
412
|
+
const host: string = "redis";
|
|
413
|
+
|
|
414
|
+
lookupSpy.mockRejectedValue(
|
|
415
|
+
Object.assign(new Error(`getaddrinfo ENOTFOUND ${host}`), {
|
|
416
|
+
code: "ENOTFOUND",
|
|
417
|
+
}) as never,
|
|
418
|
+
);
|
|
419
|
+
const missing: string = await expectRefused(
|
|
420
|
+
{ "smtp-host": host },
|
|
421
|
+
"could not be reached",
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
lookupSpy.mockResolvedValue([]);
|
|
425
|
+
const empty: string = await expectRefused(
|
|
426
|
+
{ "smtp-host": host },
|
|
427
|
+
"could not be reached",
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
lookupSpy.mockResolvedValue([{ address: "10.96.0.12", family: 4 }]);
|
|
431
|
+
const internal: string = await expectRefused(
|
|
432
|
+
{ "smtp-host": host },
|
|
433
|
+
"could not be reached",
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
lookupSpy.mockResolvedValue([{ address: "127.0.0.1", family: 4 }]);
|
|
437
|
+
const loopback: string = await expectRefused(
|
|
438
|
+
{ "smtp-host": host },
|
|
439
|
+
"could not be reached",
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
expect(missing).toBe("SMTP server host redis could not be reached.");
|
|
443
|
+
expect([empty, internal, loopback]).toEqual([missing, missing, missing]);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("still sends through a public SMTP server", async () => {
|
|
447
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
448
|
+
"smtp-host": "smtp.example.com",
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
expect((await socketTarget(options)).addresses).toEqual([
|
|
452
|
+
{ address: "93.184.216.34", family: 4 },
|
|
453
|
+
]);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
describe("on a self-hosted install (BILLING_ENABLED unset)", () => {
|
|
458
|
+
test("reaches a private SMTP host typed as an IP", async () => {
|
|
459
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
460
|
+
"smtp-host": "10.0.0.25",
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
expect(options.host).toBe("10.0.0.25");
|
|
464
|
+
expect(options.port).toBe(587);
|
|
465
|
+
expect(await socketTarget(options)).toMatchObject({
|
|
466
|
+
host: "10.0.0.25",
|
|
467
|
+
port: 587,
|
|
468
|
+
addresses: [{ address: "10.0.0.25", family: 4 }],
|
|
469
|
+
});
|
|
470
|
+
expect(lookupSpy).not.toHaveBeenCalled();
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("reaches a private IPv6 literal written in brackets", async () => {
|
|
474
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
475
|
+
"smtp-host": "[fd12:3456::25]",
|
|
476
|
+
"smtp-port": 25,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Bare, so TLS treats it as an IP literal rather than a server name.
|
|
480
|
+
expect(options.host).toBe("fd12:3456::25");
|
|
481
|
+
expect((await socketTarget(options)).addresses).toEqual([
|
|
482
|
+
{ address: "fd12:3456::25", family: 6 },
|
|
483
|
+
]);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("reaches a hostname that resolves to a private address", async () => {
|
|
487
|
+
lookupSpy.mockResolvedValue([{ address: "192.168.1.10", family: 4 }]);
|
|
488
|
+
|
|
489
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
490
|
+
"smtp-host": "postfix.corp.example",
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
expect(options.host).toBe("postfix.corp.example");
|
|
494
|
+
expect((await socketTarget(options)).addresses).toEqual([
|
|
495
|
+
{ address: "192.168.1.10", family: 4 },
|
|
496
|
+
]);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test("treats BILLING_ENABLED=false the same as unset", async () => {
|
|
500
|
+
process.env[BILLING_ENV] = "false";
|
|
501
|
+
|
|
502
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
503
|
+
"smtp-host": "172.20.0.9",
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
expect((await socketTarget(options)).addresses).toEqual([
|
|
507
|
+
{ address: "172.20.0.9", family: 4 },
|
|
508
|
+
]);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test("refuses private hosts once DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES=true", async () => {
|
|
512
|
+
process.env[BLOCK_PRIVATE_ENV] = "true";
|
|
513
|
+
|
|
514
|
+
await expectRefused(
|
|
515
|
+
{ "smtp-host": "10.0.0.25" },
|
|
516
|
+
"SMTP server host 10.0.0.25 is not allowed: private network address.",
|
|
517
|
+
);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("stops saying where a refused name points once DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES=true", async () => {
|
|
521
|
+
process.env[BLOCK_PRIVATE_ENV] = "true";
|
|
522
|
+
lookupSpy.mockResolvedValue([{ address: "192.168.1.10", family: 4 }]);
|
|
523
|
+
|
|
524
|
+
const error: string = await expectRefused(
|
|
525
|
+
{ "smtp-host": "postfix.corp.example" },
|
|
526
|
+
"SMTP server host postfix.corp.example could not be reached.",
|
|
527
|
+
);
|
|
528
|
+
expect(error).not.toContain("192.168.1.10");
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test.each([
|
|
532
|
+
["127.0.0.1", "loopback address"],
|
|
533
|
+
["127.1.2.3", "loopback address"],
|
|
534
|
+
["169.254.169.254", "link-local address (cloud metadata range)"],
|
|
535
|
+
["0.0.0.0", "unspecified address"],
|
|
536
|
+
["[::1]", "loopback address"],
|
|
537
|
+
["[fe80::1]", "link-local address"],
|
|
538
|
+
["::ffff:127.0.0.1", "loopback address"],
|
|
539
|
+
])(
|
|
540
|
+
"still refuses %s, which no setting opens",
|
|
541
|
+
async (host: string, reason: string) => {
|
|
542
|
+
await expectRefused({ "smtp-host": host }, reason);
|
|
543
|
+
},
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
test("still refuses a name that resolves to loopback, and says so", async () => {
|
|
547
|
+
lookupSpy.mockResolvedValue([{ address: "127.0.0.1", family: 4 }]);
|
|
548
|
+
|
|
549
|
+
// The operator configured this network, so they get the diagnosis.
|
|
550
|
+
await expectRefused(
|
|
551
|
+
{ "smtp-host": "localhost" },
|
|
552
|
+
"SMTP server host localhost resolves to 127.0.0.1, which is not allowed: loopback address.",
|
|
553
|
+
);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test("still refuses a name that resolves to the metadata endpoint", async () => {
|
|
557
|
+
lookupSpy.mockResolvedValue([{ address: "169.254.169.254", family: 4 }]);
|
|
558
|
+
|
|
559
|
+
await expectRefused(
|
|
560
|
+
{ "smtp-host": "metadata.attacker.example" },
|
|
561
|
+
"link-local address (cloud metadata range)",
|
|
562
|
+
);
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
describe("connecting to the checked address", () => {
|
|
567
|
+
test("connects only to the validated address, keeping the typed name for TLS", async () => {
|
|
568
|
+
lookupSpy.mockResolvedValue([{ address: "203.0.114.10", family: 4 }]);
|
|
569
|
+
|
|
570
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
571
|
+
"smtp-host": "smtp.example.com",
|
|
572
|
+
"smtp-port": "465",
|
|
573
|
+
secure: true,
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
/*
|
|
577
|
+
* host is what nodemailer sends as SNI and verifies the certificate
|
|
578
|
+
* against; the socket it is given is what decides where it connects.
|
|
579
|
+
*/
|
|
580
|
+
expect(options).toMatchObject({
|
|
581
|
+
host: "smtp.example.com",
|
|
582
|
+
port: 465,
|
|
583
|
+
secure: true,
|
|
584
|
+
});
|
|
585
|
+
expect(await socketTarget(options)).toEqual({
|
|
586
|
+
host: "smtp.example.com",
|
|
587
|
+
port: 465,
|
|
588
|
+
autoSelectFamily: true,
|
|
589
|
+
addresses: [{ address: "203.0.114.10", family: 4 }],
|
|
590
|
+
});
|
|
591
|
+
// The name is resolved once, by the guard, and never again.
|
|
592
|
+
expect(lookupSpy).toHaveBeenCalledTimes(1);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
test("offers every validated address, both families, for fallback", async () => {
|
|
596
|
+
const addresses: Array<{ address: string; family: number }> = [
|
|
597
|
+
{ address: "2606:2800:220:1:248:1893:25c8:1946", family: 6 },
|
|
598
|
+
{ address: "93.184.216.34", family: 4 },
|
|
599
|
+
{ address: "93.184.216.35", family: 4 },
|
|
600
|
+
];
|
|
601
|
+
lookupSpy.mockResolvedValue(addresses);
|
|
602
|
+
|
|
603
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
604
|
+
"smtp-host": "smtp.example.com",
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
/*
|
|
608
|
+
* An IPv6-only worker must still reach a dual-stack server, and one
|
|
609
|
+
* dead record must not fail the send: autoSelectFamily walks this list.
|
|
610
|
+
*/
|
|
611
|
+
expect(await socketTarget(options)).toMatchObject({
|
|
612
|
+
autoSelectFamily: true,
|
|
613
|
+
addresses,
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
test("trims whitespace pasted around the host", async () => {
|
|
618
|
+
const options: SMTPTransport.Options = await expectSent({
|
|
619
|
+
"smtp-host": " smtp.example.com \n",
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
expect(lookupSpy).toHaveBeenCalledWith("smtp.example.com", {
|
|
623
|
+
all: true,
|
|
624
|
+
});
|
|
625
|
+
expect(options.host).toBe("smtp.example.com");
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
test("routes a host that does not resolve to the Error port, naming the resolver error on a self-hosted install", async () => {
|
|
629
|
+
lookupSpy.mockRejectedValue(
|
|
630
|
+
Object.assign(new Error("getaddrinfo ENOTFOUND smtp.typo.example"), {
|
|
631
|
+
code: "ENOTFOUND",
|
|
632
|
+
}) as never,
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
await expectRefused(
|
|
636
|
+
{ "smtp-host": "smtp.typo.example" },
|
|
637
|
+
"Could not resolve smtp server host smtp.typo.example",
|
|
638
|
+
);
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
});
|