@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.
Files changed (440) hide show
  1. package/Models/DatabaseModels/Incident.ts +267 -1
  2. package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
  4. package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
  5. package/Models/DatabaseModels/StatusPage.ts +55 -0
  6. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  7. package/Server/API/IncidentAPI.ts +162 -0
  8. package/Server/API/StatusPageAPI.ts +487 -171
  9. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  18. package/Server/Infrastructure/Semaphore.ts +10 -0
  19. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  20. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  21. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  22. package/Server/Services/DatabaseService.ts +93 -10
  23. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  24. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  25. package/Server/Services/IncidentEpisodeService.ts +15 -0
  26. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  27. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  28. package/Server/Services/IncidentService.ts +1535 -65
  29. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  30. package/Server/Services/IncidentTemplateService.ts +109 -0
  31. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  32. package/Server/Services/ScheduledMaintenanceService.ts +68 -18
  33. package/Server/Services/SmsService.ts +10 -0
  34. package/Server/Services/StatusPageService.ts +65 -8
  35. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  36. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  37. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  38. package/Server/Types/Markdown.ts +153 -6
  39. package/Server/Types/Workflow/Components/Email.ts +61 -2
  40. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  41. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  42. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  43. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  44. package/Server/Utils/SSRFProtection.ts +76 -16
  45. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  46. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  47. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  48. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  49. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  50. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  51. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  52. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  53. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  54. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  55. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  56. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  57. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  58. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  59. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  60. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  61. package/Server/Utils/StatusPageResource.ts +51 -20
  62. package/Server/Utils/VM/VMAPI.ts +1 -0
  63. package/Server/Utils/VM/VMRunner.ts +10 -0
  64. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  65. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  66. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  67. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  68. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  69. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  70. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  71. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  72. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  73. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  74. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  75. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +242 -5
  76. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  77. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  78. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  79. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  80. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  81. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  82. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  83. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  84. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  85. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  86. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  87. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  88. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  89. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  90. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  91. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  92. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  93. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  94. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  95. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  96. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  97. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  98. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  99. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  100. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  101. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  102. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  103. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  104. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  105. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  106. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  107. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  108. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  109. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  110. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  111. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  112. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  113. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  114. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  115. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  116. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  117. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  118. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  119. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  120. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  121. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  122. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  123. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  124. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  125. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  126. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  127. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  128. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  129. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  130. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  131. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  132. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  133. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  134. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  135. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  136. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  137. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  138. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  139. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  140. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  141. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  142. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  143. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  144. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  145. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  146. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  147. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  148. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  149. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  150. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  151. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  152. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  153. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  154. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  155. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  156. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  157. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  158. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  159. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  160. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  161. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  162. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  163. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  164. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  165. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  166. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  167. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  168. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  169. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  170. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  171. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  172. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  173. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  174. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  175. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  176. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  177. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  178. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  179. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  180. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  181. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  182. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  183. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  184. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  185. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  186. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  187. package/Tests/Types/SafeHtml.test.ts +102 -0
  188. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  189. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  190. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  191. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  192. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  193. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  194. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  195. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  196. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  197. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  198. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  199. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  200. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  201. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  202. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  203. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  204. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  205. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  206. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  207. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  208. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  209. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  210. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  211. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  212. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  213. package/Types/CustomField/CustomFieldType.ts +14 -0
  214. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  215. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  216. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  217. package/Types/SafeHtml.ts +93 -0
  218. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  219. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  220. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  221. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  222. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  223. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  224. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  225. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  226. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  227. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  228. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  229. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  230. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  231. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  232. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  233. package/UI/Components/Forms/ModelForm.tsx +70 -8
  234. package/UI/Components/Forms/Validation.ts +1 -1
  235. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  236. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  237. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  238. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  239. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  240. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  241. package/Utils/API.ts +247 -2
  242. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  243. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  244. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  245. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  246. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  247. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  248. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  249. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  250. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  251. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  252. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  253. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  254. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  255. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  256. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  257. package/build/dist/Server/API/IncidentAPI.js +100 -1
  258. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  259. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  260. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  261. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  262. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  263. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  264. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  265. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  266. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  267. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  268. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  269. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  270. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  271. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  272. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  273. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  274. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  275. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  276. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  277. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  278. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  279. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  280. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  281. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  282. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  283. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  284. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  285. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  286. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  287. package/build/dist/Server/Services/DatabaseService.js +56 -7
  288. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  289. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  290. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  291. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  292. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  293. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  294. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  295. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  296. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  297. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  298. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  299. package/build/dist/Server/Services/IncidentService.js +1052 -0
  300. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  301. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  302. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  303. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  304. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  305. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  306. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  307. package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
  308. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  309. package/build/dist/Server/Services/SmsService.js +2 -22
  310. package/build/dist/Server/Services/SmsService.js.map +1 -1
  311. package/build/dist/Server/Services/StatusPageService.js +47 -9
  312. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  313. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  314. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  315. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  316. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  317. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  318. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  319. package/build/dist/Server/Types/Markdown.js +110 -6
  320. package/build/dist/Server/Types/Markdown.js.map +1 -1
  321. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  322. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  323. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  324. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  325. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  326. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  327. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  328. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  329. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  330. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  331. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  332. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  333. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  334. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  335. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  336. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  337. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  338. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  339. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  340. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  341. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  342. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  343. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  344. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  345. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  346. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  348. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  349. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  350. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  351. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  352. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  353. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  354. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  355. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  356. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  357. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  358. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  359. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  360. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  361. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  362. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  363. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  364. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  365. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  366. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  367. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  368. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  369. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  370. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  371. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  372. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  373. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  374. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  375. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  376. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  377. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  378. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  379. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  380. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  381. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  382. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  383. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  384. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  385. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  386. package/build/dist/Types/SafeHtml.js +84 -0
  387. package/build/dist/Types/SafeHtml.js.map +1 -0
  388. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  389. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  390. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  391. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  392. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  393. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  394. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  395. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  396. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  397. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  398. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  399. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  400. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  401. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  402. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  403. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  404. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  405. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  406. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  407. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  408. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  409. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  410. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  411. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  412. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  413. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  414. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  415. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  416. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  417. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  418. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  419. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  420. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  421. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  422. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  423. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  424. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  425. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  426. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  427. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  428. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  429. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  430. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  431. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  432. package/build/dist/Utils/API.js +177 -1
  433. package/build/dist/Utils/API.js.map +1 -1
  434. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  435. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  436. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  437. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  438. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  439. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  440. package/package.json +1 -1
@@ -15,9 +15,10 @@ import logger from "../Logger";
15
15
  * It started life guarding external Data Source connections — hence the
16
16
  * file's home — but the same policy now covers every sink where a project
17
17
  * member picks the host and a self-hosted install may legitimately point at
18
- * something internal: LLM providers (self-hosted Ollama/vLLM), SMTP OAuth
19
- * token endpoints, status page / dashboard domain verification, OIDC
20
- * discovery and Runbook HTTP steps. Sinks that can only ever be third-party
18
+ * something internal: LLM providers (self-hosted Ollama/vLLM), SMTP servers
19
+ * (project SMTP settings and the workflow Send Email step), SMTP OAuth token
20
+ * endpoints, status page / dashboard domain verification, OIDC discovery and
21
+ * Runbook HTTP steps. Sinks that can only ever be third-party
21
22
  * SaaS (Slack, Teams, subscriber webhooks) use SSRFProtection instead, which
22
23
  * blocks private ranges unconditionally.
23
24
  *
@@ -78,10 +79,21 @@ export interface EgressGuardOptions {
78
79
  */
79
80
  privateNetworkHint?: string | undefined;
80
81
  /*
81
- * Detailed DNS failures and address-policy reasons are useful to operators,
82
- * but tenant-facing callers can compare them to enumerate internal names.
83
- * Defaults to true for backwards compatibility; shared-probe monitor
84
- * requests set it to false and receive one indistinguishable failure.
82
+ * Whether a refused HOSTNAME may say why: which address it resolved to and
83
+ * which policy refused it, or the resolver's own error. That is exactly what
84
+ * an operator needs, and exactly what a tenant must not get — trying names
85
+ * like `redis` or `kubernetes.default.svc.cluster.local` and comparing
86
+ * "resolves to 10.1.2.3" with "could not resolve" maps the internal network
87
+ * without a socket ever being opened. With false, every resolve-path refusal
88
+ * is one sentence ("<Label> host X could not be reached.") and the precise
89
+ * cause goes to the debug log.
90
+ *
91
+ * Undefined ⇒ shouldIncludeResolutionDetail(): detail on a self-hosted
92
+ * install, where whoever configures the target also runs the network; none
93
+ * wherever shouldBlockPrivateAddresses() is true, i.e. multi-tenant SaaS or
94
+ * an operator who asked for SaaS policy. Shared-probe monitor requests pass
95
+ * false outright, whatever the deployment. Literal IPs are unaffected: their
96
+ * refusal echoes only what the caller typed.
85
97
  */
86
98
  includeResolvedAddressInError?: boolean | undefined;
87
99
  /*
@@ -196,6 +208,21 @@ export default class DataSourceEgressGuard {
196
208
  return process.env["BILLING_ENABLED"] === "true";
197
209
  }
198
210
 
211
+ /*
212
+ * Default for EgressGuardOptions.includeResolvedAddressInError, and for the
213
+ * equivalent switch on SSRFProtection. It keys off the private-address
214
+ * POLICY of the deployment, not off any per-call override of it: the oracle
215
+ * exists because the person choosing the target is not trusted with the
216
+ * network's layout, and that is a property of the deployment. So a caller
217
+ * that loosens blockPrivateAddresses on SaaS still gets no detail unless it
218
+ * asks for it explicitly.
219
+ */
220
+ public static shouldIncludeResolutionDetail(
221
+ includeResolvedAddressInError?: boolean | undefined,
222
+ ): boolean {
223
+ return includeResolvedAddressInError ?? !this.shouldBlockPrivateAddresses();
224
+ }
225
+
199
226
  private static ipv4ToInt(ip: string): number {
200
227
  const parts: Array<string> = ip.split(".");
201
228
  let value: number = 0;
@@ -672,15 +699,19 @@ export default class DataSourceEgressGuard {
672
699
  return this.defaultResolve(host, options);
673
700
  });
674
701
 
702
+ const includeDetail: boolean = this.shouldIncludeResolutionDetail(
703
+ options?.includeResolvedAddressInError,
704
+ );
705
+
675
706
  let addresses: Array<ResolvedAddress> = [];
676
707
  try {
677
708
  addresses = await resolveFunction(bareHostname);
678
709
  } catch (error) {
679
- if (options?.includeResolvedAddressInError === false) {
710
+ if (!includeDetail) {
680
711
  /*
681
712
  * The cause is destroyed for the caller by design, so log it here:
682
- * this is the only place an operator can still learn whether a
683
- * monitor failed on DNS or on address policy.
713
+ * this is the only place an operator can still learn whether the
714
+ * target failed on DNS or on address policy.
684
715
  */
685
716
  logger.debug(
686
717
  `EgressGuard: could not resolve ${bareHostname} - ${
@@ -701,7 +732,7 @@ export default class DataSourceEgressGuard {
701
732
  }
702
733
 
703
734
  if (addresses.length === 0) {
704
- if (options?.includeResolvedAddressInError === false) {
735
+ if (!includeDetail) {
705
736
  logger.debug(`EgressGuard: ${bareHostname} resolved to no addresses.`);
706
737
  throw new EgressGuardException(
707
738
  `${label} host ${bareHostname} could not be reached.`,
@@ -720,14 +751,14 @@ export default class DataSourceEgressGuard {
720
751
  options,
721
752
  );
722
753
  if (verdict.blocked) {
723
- if (options?.includeResolvedAddressInError === false) {
754
+ if (!includeDetail) {
724
755
  logger.debug(
725
- `EgressGuard: ${bareHostname} resolved to a blocked address - ${verdict.reason}.`,
756
+ `EgressGuard: ${bareHostname} resolved to a blocked address ${resolved.address} - ${verdict.reason}.`,
726
757
  );
727
758
  /*
728
759
  * Reported as Unreachable, NOT AddressBlocked. Sharing one reason
729
- * with the DNS branches above is what stops a tenant on a shared
730
- * probe from using the difference to enumerate internal names.
760
+ * with the DNS branches above is what stops a tenant from using the
761
+ * difference to enumerate internal names.
731
762
  */
732
763
  throw new EgressGuardException(
733
764
  `${label} host ${bareHostname} could not be reached.`,
@@ -798,6 +829,15 @@ export default class DataSourceEgressGuard {
798
829
  * that was checked the address that gets dialed, closing the DNS-rebind
799
830
  * window between validation and connect. TLS still verifies against the
800
831
  * original hostname because SNI is taken from the URL, not from here.
832
+ *
833
+ * It always answers asynchronously, the way dns.lookup does. Node's
834
+ * net/tls connect can fail every pinned address synchronously (a probe
835
+ * with no IPv6 gets EADDRNOTAVAIL or ENETUNREACH from connect() itself),
836
+ * and when the lookup has answered synchronously that destroys the
837
+ * TLSSocket before tls.connect calls setServername on it. The request then
838
+ * throws "Cannot read properties of null (reading 'setServername')" in
839
+ * place of the real connect error, and that error escapes as an unhandled
840
+ * 'error' event on the socket.
801
841
  */
802
842
  public static createPinnedLookup(
803
843
  addresses: Array<ResolvedAddress>,
@@ -823,22 +863,27 @@ export default class DataSourceEgressGuard {
823
863
  `No IPv${wantedFamily} address is available for the validated host.`,
824
864
  );
825
865
  error.code = "ENOTFOUND";
826
- callback(error, "");
866
+ process.nextTick(() => {
867
+ callback(error, "");
868
+ });
827
869
  return;
828
870
  }
829
871
 
830
872
  if (lookupOptions && lookupOptions.all) {
831
- callback(
832
- null,
873
+ const allAddresses: Array<{ address: string; family: number }> =
833
874
  usableAddresses.map((resolved: ResolvedAddress) => {
834
875
  return { address: resolved.address, family: resolved.family };
835
- }),
836
- );
876
+ });
877
+ process.nextTick(() => {
878
+ callback(null, allAddresses);
879
+ });
837
880
  return;
838
881
  }
839
882
 
840
883
  const first: ResolvedAddress = usableAddresses[0]!;
841
- callback(null, first.address, first.family);
884
+ process.nextTick(() => {
885
+ callback(null, first.address, first.family);
886
+ });
842
887
  };
843
888
  }
844
889
 
@@ -0,0 +1,130 @@
1
+ import net from "net";
2
+ import DataSourceEgressGuard, {
3
+ ResolvedAddress,
4
+ } from "../DataSource/EgressGuard";
5
+
6
+ // What nodemailer's getSocket hook hands back: an already connected socket.
7
+ export type SmtpSocketCallback = (
8
+ error: Error | null,
9
+ socketOptions?: { connection: net.Socket },
10
+ ) => void;
11
+
12
+ /*
13
+ * The socket half of pinning a tenant-chosen SMTP server to the addresses
14
+ * the egress guard validated.
15
+ *
16
+ * Given a host name, nodemailer resolves it again on its own (dns.resolve4/6,
17
+ * falling back to dns.lookup, behind a process-wide cache kept for five
18
+ * minutes whatever the record's TTL), so a DNS answer that changed after the
19
+ * check would choose the address actually connected to. Callers validate the
20
+ * host, open the socket here, and hand it to nodemailer through its getSocket
21
+ * hook as `{ connection: socket }`; nodemailer then resolves and dials
22
+ * nothing itself.
23
+ *
24
+ * nodemailer still gets the name the user typed as `host`: it is what TLS
25
+ * sends as SNI and checks the certificate against, on implicit TLS and on the
26
+ * STARTTLS upgrade alike.
27
+ *
28
+ * Used by the workflow Send Email component and by MailService for project
29
+ * SMTP configs.
30
+ */
31
+ export default class PinnedSmtpSocket {
32
+ /*
33
+ * Open a TCP connection that can only reach the given, already validated
34
+ * addresses.
35
+ *
36
+ * The pinned lookup answers with every validated address, and
37
+ * autoSelectFamily tries them in turn, alternating families. That keeps
38
+ * what nodemailer did when it resolved the name itself: a host whose
39
+ * address family this worker cannot route (IPv4 on an IPv6-only cluster),
40
+ * or one record that is down, falls through to the next address instead
41
+ * of failing the send.
42
+ *
43
+ * timeoutInMs bounds the TCP connect. nodemailer's own connectionTimeout
44
+ * only starts once it is handed the connected socket, so without this a
45
+ * blackholed address would hang the send.
46
+ */
47
+ public static connect(data: {
48
+ host: string;
49
+ port: number;
50
+ addresses: Array<ResolvedAddress>;
51
+ timeoutInMs: number;
52
+ onDone: SmtpSocketCallback;
53
+ }): void {
54
+ const socket: net.Socket = net.connect({
55
+ host: data.host,
56
+ port: data.port,
57
+ lookup: DataSourceEgressGuard.createPinnedLookup(data.addresses) as never,
58
+ autoSelectFamily: true,
59
+ });
60
+
61
+ let settled: boolean = false;
62
+
63
+ const settle: (error: Error | null) => void = (
64
+ error: Error | null,
65
+ ): void => {
66
+ if (settled) {
67
+ return;
68
+ }
69
+
70
+ settled = true;
71
+ socket.setTimeout(0);
72
+
73
+ if (error) {
74
+ // A late error from a socket nobody owns would crash the process.
75
+ socket.on("error", () => {});
76
+ socket.destroy();
77
+ data.onDone(error);
78
+ return;
79
+ }
80
+
81
+ /*
82
+ * nodemailer attaches its own error and timeout handlers synchronously
83
+ * inside this callback, so the socket is never left unwatched. The
84
+ * listeners below stay behind as no-ops.
85
+ */
86
+ data.onDone(null, { connection: socket });
87
+ };
88
+
89
+ socket.once("connect", () => {
90
+ settle(null);
91
+ });
92
+ socket.once("error", (error: Error) => {
93
+ settle(PinnedSmtpSocket.describeConnectionError(error));
94
+ });
95
+ socket.once("timeout", () => {
96
+ settle(new Error("Connection timeout"));
97
+ });
98
+ socket.setTimeout(data.timeoutInMs);
99
+ }
100
+
101
+ /*
102
+ * When every address fails, autoSelectFamily reports an AggregateError
103
+ * whose own message is empty, which would reach the user as a generic
104
+ * "could not be sent". Name each attempt instead, the way nodemailer's
105
+ * "connect ECONNREFUSED <ip>:<port>" did.
106
+ */
107
+ public static describeConnectionError(error: Error): Error {
108
+ const attempts: unknown = (error as { errors?: unknown }).errors;
109
+
110
+ if (error.message || !Array.isArray(attempts) || attempts.length === 0) {
111
+ return error;
112
+ }
113
+
114
+ const described: NodeJS.ErrnoException = new Error(
115
+ attempts
116
+ .map((attempt: unknown) => {
117
+ const message: unknown = (attempt as { message?: unknown } | null)
118
+ ?.message;
119
+ return typeof message === "string" ? message : String(attempt);
120
+ })
121
+ .join("; "),
122
+ );
123
+ const code: string | undefined = (error as NodeJS.ErrnoException).code;
124
+ if (code) {
125
+ described.code = code;
126
+ }
127
+
128
+ return described;
129
+ }
130
+ }
@@ -36,6 +36,7 @@ import MonitorMetricType from "../../../Types/Monitor/MonitorMetricType";
36
36
  import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
37
37
  import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
38
38
  import MonitorCriteriaExpectationBuilder from "./MonitorCriteriaExpectationBuilder";
39
+ import ProbeNetworkFailureUtil from "../../../Utils/ProbeNetworkFailureUtil";
39
40
  import MetricMonitorResponse from "../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
40
41
  import MetricQueryConfigData from "../../../Types/Metrics/MetricQueryConfigData";
41
42
  import MetricFormulaConfigData from "../../../Types/Metrics/MetricFormulaConfigData";
@@ -1556,6 +1557,15 @@ export default class MonitorCriteriaObservationBuilder {
1556
1557
  return "Device is reachable (answered ping or SNMP).";
1557
1558
  }
1558
1559
 
1560
+ /*
1561
+ * A probe that could not send at all (no IPv6 on the probe, say) never
1562
+ * contacted the device, so "unreachable" would be a verdict on a device
1563
+ * nobody checked, and would contradict the cause that follows it.
1564
+ */
1565
+ if (ProbeNetworkFailureUtil.isProbeSideCause(probeResponse.failureCause)) {
1566
+ return `Device could not be checked from this probe: ${probeResponse.failureCause}`;
1567
+ }
1568
+
1559
1569
  if (probeResponse.failureCause) {
1560
1570
  return `Device is unreachable by ping and SNMP: ${probeResponse.failureCause}`;
1561
1571
  }
@@ -1,5 +1,7 @@
1
1
  import URL from "../../Types/API/URL";
2
2
  import BadDataException from "../../Types/Exception/BadDataException";
3
+ import DataSourceEgressGuard from "./DataSource/EgressGuard";
4
+ import logger from "./Logger";
3
5
  import PrivateNetworkWebhookConfig from "./PrivateNetworkWebhookConfig";
4
6
  import dns from "dns";
5
7
  import net from "net";
@@ -101,6 +103,21 @@ export interface WebhookTargetValidationOptions {
101
103
  * filing the bug this feature came from.
102
104
  */
103
105
  privateNetworkHint?: string | undefined;
106
+
107
+ /*
108
+ * Whether a refusal decided by DNS may say what DNS answered. "could not be
109
+ * resolved via DNS" next to "resolves to a private network address" tells
110
+ * whoever typed the URL which internal names exist, so without detail every
111
+ * such refusal is the one sentence "<label> could not be reached." and the
112
+ * precise cause goes to the debug log. Refusals of an IP literal or a
113
+ * well-known local name keep their detail: they only restate the URL.
114
+ *
115
+ * Undefined ⇒ DataSourceEgressGuard.shouldIncludeResolutionDetail(), the
116
+ * same default the egress guard uses: detail on a self-hosted install, none
117
+ * on SaaS or wherever the operator forced SaaS policy. A shared probe passes
118
+ * false whatever its deployment.
119
+ */
120
+ includeResolutionDetailInError?: boolean | undefined;
104
121
  }
105
122
 
106
123
  export interface ValidatedWebhookTargetAddress {
@@ -215,6 +232,10 @@ export default class SSRFProtection {
215
232
  const label: string = options?.targetLabel || DEFAULT_TARGET_LABEL;
216
233
  const privateNetworkHint: string =
217
234
  options?.privateNetworkHint ?? DEFAULT_PRIVATE_NETWORK_HINT;
235
+ const includeResolutionDetail: boolean =
236
+ DataSourceEgressGuard.shouldIncludeResolutionDetail(
237
+ options?.includeResolutionDetailInError,
238
+ );
218
239
 
219
240
  /*
220
241
  * URL.fromString only knows http/https/ws/wss/mongodb/mailto/tel/sms and
@@ -373,16 +394,24 @@ export default class SSRFProtection {
373
394
  let resolved: Array<{ address: string; family: number }> = [];
374
395
  try {
375
396
  resolved = await dns.promises.lookup(host, { all: true });
376
- } catch {
377
- throw new BadDataException(
378
- `${label} hostname could not be resolved via DNS.`,
379
- );
397
+ } catch (error) {
398
+ throw SSRFProtection.refuseResolvedHost({
399
+ label: label,
400
+ includeDetail: includeResolutionDetail,
401
+ detailedMessage: `${label} hostname could not be resolved via DNS.`,
402
+ cause: `${host} could not be resolved - ${
403
+ error instanceof Error ? error.message : String(error)
404
+ }`,
405
+ });
380
406
  }
381
407
 
382
408
  if (resolved.length === 0 && isConnectionHostname) {
383
- throw new BadDataException(
384
- `${label} hostname could not be resolved via DNS.`,
385
- );
409
+ throw SSRFProtection.refuseResolvedHost({
410
+ label: label,
411
+ includeDetail: includeResolutionDetail,
412
+ detailedMessage: `${label} hostname could not be resolved via DNS.`,
413
+ cause: `${host} resolved to no addresses`,
414
+ });
386
415
  }
387
416
 
388
417
  const normalizedAddresses: Array<ValidatedWebhookTargetAddress> =
@@ -420,15 +449,21 @@ export default class SSRFProtection {
420
449
  SSRFProtection.classifyHostnameLiteral(address);
421
450
 
422
451
  if (tier === WebhookAddressTier.Forbidden) {
423
- throw new BadDataException(
424
- `${label} resolves to a private, loopback, or link-local address and is not allowed.`,
425
- );
452
+ throw SSRFProtection.refuseResolvedHost({
453
+ label: label,
454
+ includeDetail: includeResolutionDetail,
455
+ detailedMessage: `${label} resolves to a private, loopback, or link-local address and is not allowed.`,
456
+ cause: `${host} resolved to ${address}, which is forbidden`,
457
+ });
426
458
  }
427
459
 
428
460
  if (tier === WebhookAddressTier.Private && !isPrivateTierAllowed) {
429
- throw new BadDataException(
430
- `${label} resolves to a private network address and is not allowed.${privateNetworkHint}`,
431
- );
461
+ throw SSRFProtection.refuseResolvedHost({
462
+ label: label,
463
+ includeDetail: includeResolutionDetail,
464
+ detailedMessage: `${label} resolves to a private network address and is not allowed.${privateNetworkHint}`,
465
+ cause: `${host} resolved to ${address}, a private network address`,
466
+ });
432
467
  }
433
468
  }
434
469
  }
@@ -453,14 +488,39 @@ export default class SSRFProtection {
453
488
  resolvedAddressesByHostname.get(whatwgHostname);
454
489
 
455
490
  if (!addresses || addresses.length === 0) {
456
- throw new BadDataException(
457
- `${label} hostname could not be resolved via DNS.`,
458
- );
491
+ throw SSRFProtection.refuseResolvedHost({
492
+ label: label,
493
+ includeDetail: includeResolutionDetail,
494
+ detailedMessage: `${label} hostname could not be resolved via DNS.`,
495
+ cause: `${whatwgHostname} resolved to no addresses`,
496
+ });
459
497
  }
460
498
 
461
499
  return { url: canonicalUrl, addresses: addresses };
462
500
  }
463
501
 
502
+ /*
503
+ * The refusal for a hostname whose DNS answer decided the outcome. Without
504
+ * detail, a name that does not exist and a name that resolves somewhere
505
+ * internal produce the same sentence — that sameness is the whole point, so
506
+ * nothing about the cause may reach the message — and the operator learns
507
+ * the difference from the debug log instead.
508
+ */
509
+ private static refuseResolvedHost(data: {
510
+ label: string;
511
+ includeDetail: boolean;
512
+ detailedMessage: string;
513
+ cause: string;
514
+ }): BadDataException {
515
+ if (data.includeDetail) {
516
+ return new BadDataException(data.detailedMessage);
517
+ }
518
+
519
+ logger.debug(`SSRFProtection: ${data.cause}.`);
520
+
521
+ return new BadDataException(`${data.label} could not be reached.`);
522
+ }
523
+
464
524
  /*
465
525
  * Everything up to and including the LAST "@" is userinfo, never the host.
466
526
  *