@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
@@ -0,0 +1,133 @@
1
+ import LIMIT_MAX from "../../../Types/Database/LimitMax";
2
+ import OneUptimeDate from "../../../Types/Date";
3
+
4
+ /*
5
+ * How long the incident and episode subscriber jobs may spend sending, and
6
+ * how long a notification may sit In progress before it is taken to have been
7
+ * interrupted.
8
+ *
9
+ * A job sends each notification - an incident created, a state change, a
10
+ * public note, a postmortem, or an episode's - by awaiting every message at a
11
+ * bounded concurrency, so a send takes as long as its slowest subscribers
12
+ * rather than returning the moment the messages are queued. These numbers
13
+ * keep that bounded, and tie the job's timeout, a notification's send window
14
+ * and the sweeper's threshold together so they cannot drift apart:
15
+ *
16
+ * - SEND_CONCURRENCY messages are in flight at once, per notification.
17
+ * - SUBSCRIBERS_PER_READ subscribers are read from a status page at a time:
18
+ * LIMIT_MAX, the cap one read used to stop at silently. A page with more is
19
+ * read in batches (SubscriberNotificationFanOut).
20
+ * - A message still unanswered after SEND_TIMEOUT_IN_MS is counted failed and
21
+ * its slot freed. That is longer than any sender's own retries can take -
22
+ * the Notification service tries SMTP three times with a 60 second connect
23
+ * timeout each (about 3 minutes 5 seconds), and the Slack and webhook
24
+ * senders retry three times with 2, 4 and 8 second backoffs - so it only
25
+ * cuts off a request that hangs. The request itself is not cancelled.
26
+ * - A notification starts no new message once SEND_WINDOW_IN_MS has passed
27
+ * since it was claimed. What it has not reached is recorded as not sent,
28
+ * and the notification settles as Failed.
29
+ *
30
+ * The math, for one full read of a status page: 10,000 subscribers at 20 in
31
+ * flight is 500 rounds, and 20 minutes over 500 rounds allows 2.4 seconds a
32
+ * message on average. An email, SMS or webhook through the Notification
33
+ * service normally answers in well under a second, so a 10,000-subscriber
34
+ * page fits with room to spare, and at half a second a message one
35
+ * notification reaches about 48,000 subscribers in its window.
36
+ *
37
+ * - One notification can therefore take at most NOTIFICATION_MAX_IN_MS: its
38
+ * send window, plus the timeout of the messages still in flight when the
39
+ * window closes, plus a minute for the feed item and the status write.
40
+ * - JOB_TIMEOUT_IN_MS is each job's RunCron timeoutInMS. A run claims a new
41
+ * notification only while that notification could still finish inside the
42
+ * job's timeout (LATEST_CLAIM_IN_MS into the run), so a run never outlives
43
+ * its timeout: a timed-out queue job is only stopped being waited for,
44
+ * never cancelled, and would otherwise carry on sending untracked. The
45
+ * notifications it leaves Pending are claimed by the runs that follow, one
46
+ * a minute, which can run alongside it: a claim is atomic
47
+ * (SubscriberNotificationClaim), so each notification is sent once. How
48
+ * many runs of a job may be sending at once, across every worker, is
49
+ * bounded too (SubscriberNotificationRunLimit), so slow sends cannot fill
50
+ * the Worker queue every other cron shares.
51
+ * - STUCK_AFTER_IN_MS is when the sweeper
52
+ * (StatusPageSubscriber:TimeoutStuckNotifications) takes a notification
53
+ * still In progress to have been interrupted - its worker crashed, was
54
+ * redeployed or lost its database connection - and marks it Failed. A
55
+ * running send is never In progress that long, so the margin over the job
56
+ * timeout only has to cover clock skew between the worker and the database.
57
+ */
58
+ export default class SubscriberNotificationTiming {
59
+ public static readonly SEND_CONCURRENCY: number = 20;
60
+
61
+ public static readonly SUBSCRIBERS_PER_READ: number = LIMIT_MAX;
62
+
63
+ public static readonly SEND_TIMEOUT_IN_MS: number =
64
+ OneUptimeDate.convertMinutesToMilliseconds(4);
65
+
66
+ public static readonly SEND_WINDOW_IN_MS: number =
67
+ OneUptimeDate.convertMinutesToMilliseconds(20);
68
+
69
+ public static readonly SETTLE_ALLOWANCE_IN_MS: number =
70
+ OneUptimeDate.convertMinutesToMilliseconds(1);
71
+
72
+ public static readonly NOTIFICATION_MAX_IN_MS: number =
73
+ SubscriberNotificationTiming.SEND_WINDOW_IN_MS +
74
+ SubscriberNotificationTiming.SEND_TIMEOUT_IN_MS +
75
+ SubscriberNotificationTiming.SETTLE_ALLOWANCE_IN_MS;
76
+
77
+ public static readonly JOB_TIMEOUT_IN_MS: number =
78
+ OneUptimeDate.convertMinutesToMilliseconds(30);
79
+
80
+ public static readonly LATEST_CLAIM_IN_MS: number =
81
+ SubscriberNotificationTiming.JOB_TIMEOUT_IN_MS -
82
+ SubscriberNotificationTiming.NOTIFICATION_MAX_IN_MS;
83
+
84
+ public static readonly STUCK_AFTER_IN_MS: number =
85
+ SubscriberNotificationTiming.JOB_TIMEOUT_IN_MS +
86
+ OneUptimeDate.convertMinutesToMilliseconds(10);
87
+
88
+ // The clock of one job run, started when the run starts.
89
+ public static startRun(now?: number): SubscriberNotificationRunClock {
90
+ return new SubscriberNotificationRunClock(now ?? Date.now());
91
+ }
92
+
93
+ // The send window of one notification, started when it is claimed.
94
+ public static startSendWindow(
95
+ now?: number,
96
+ ): SubscriberNotificationSendWindow {
97
+ return new SubscriberNotificationSendWindow(
98
+ (now ?? Date.now()) + SubscriberNotificationTiming.SEND_WINDOW_IN_MS,
99
+ );
100
+ }
101
+ }
102
+
103
+ export class SubscriberNotificationRunClock {
104
+ private readonly startedAt: number;
105
+
106
+ public constructor(startedAt: number) {
107
+ this.startedAt = startedAt;
108
+ }
109
+
110
+ /*
111
+ * Whether this run may still claim a notification: one claimed now could
112
+ * finish before the run's timeout.
113
+ */
114
+ public canClaimAnotherNotification(now?: number): boolean {
115
+ return (
116
+ (now ?? Date.now()) - this.startedAt <=
117
+ SubscriberNotificationTiming.LATEST_CLAIM_IN_MS
118
+ );
119
+ }
120
+ }
121
+
122
+ export class SubscriberNotificationSendWindow {
123
+ public readonly closesAt: number;
124
+
125
+ public constructor(closesAt: number) {
126
+ this.closesAt = closesAt;
127
+ }
128
+
129
+ // Whether a new message may still be started.
130
+ public isOpen(now?: number): boolean {
131
+ return (now ?? Date.now()) < this.closesAt;
132
+ }
133
+ }
@@ -0,0 +1,215 @@
1
+ import Incident from "../../../Models/DatabaseModels/Incident";
2
+ import IncidentCustomField from "../../../Models/DatabaseModels/IncidentCustomField";
3
+ import BaseModel, {
4
+ DatabaseBaseModelType,
5
+ } from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
6
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
7
+ import DatabaseCommonInteractionPropsUtil, {
8
+ PermissionType,
9
+ } from "../../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
10
+ import { CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX } from "../../../Types/CustomField/CustomFieldVariableKey";
11
+ import PermissionScope from "../../../Types/Database/AccessControl/PermissionScope";
12
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
13
+ import Permission, {
14
+ PermissionHelper,
15
+ UserPermission,
16
+ } from "../../../Types/Permission";
17
+
18
+ /*
19
+ * WHO MAY PLACE INCIDENT RECORDS IN A SUBSCRIBER TEMPLATE.
20
+ *
21
+ * A custom subscriber notification template can place {{incidentLabels}} and
22
+ * any {{customFields.<key>}} - values from the team's incident records, which
23
+ * the status page does not show, marked for subscribers or not
24
+ * (SubscriberNotificationTemplateVariables.getIncidentRecordPlaceholders).
25
+ * Templates are written by the status page roles (StatusPageAdmin,
26
+ * StatusPageMember), which cannot read incidents; and a Slack, Teams or
27
+ * webhook subscriber can be added by the same roles, pointing at an address
28
+ * they own. Without this check such a role could send itself every
29
+ * incident's custom fields - guessing keys, which come predictably from
30
+ * field names, many at once, since a placeholder with no value is left as
31
+ * written.
32
+ *
33
+ * So a template may place them only if whoever writes it may read them
34
+ * directly: incidents, the column the value comes from (Incident.customFields
35
+ * or Incident.labels) and, for custom fields, the field definitions. And for
36
+ * every incident of the project, since a template reaches every incident
37
+ * that reaches its status pages: a grant limited to labels or to owned
38
+ * incidents does not count, and a block on any of those permissions refuses.
39
+ *
40
+ * The permissions are read from the models' own access control, so they
41
+ * follow any change to it. Root and master admin writes are not checked.
42
+ */
43
+
44
+ interface AccessRequirement {
45
+ // What the caller must be able to read, for the refusal.
46
+ description: string;
47
+ permissions: Array<Permission>;
48
+ }
49
+
50
+ export default class SubscriberTemplateIncidentRecordAccess {
51
+ /**
52
+ * Refuses a write that places these placeholders (from
53
+ * getIncidentRecordPlaceholders) unless the caller may read what they
54
+ * read, for every incident of the project. Does nothing when there are
55
+ * none, or for root and master admin.
56
+ */
57
+ public static assertCanPlace(data: {
58
+ placeholders: Array<string>;
59
+ props: DatabaseCommonInteractionProps;
60
+ }): void {
61
+ if (
62
+ data.placeholders.length === 0 ||
63
+ data.props.isRoot ||
64
+ data.props.isMasterAdmin
65
+ ) {
66
+ return;
67
+ }
68
+
69
+ const unrestricted: Array<Permission> =
70
+ SubscriberTemplateIncidentRecordAccess.getUnrestrictedPermissions(
71
+ data.props,
72
+ );
73
+ const blocked: Array<Permission> =
74
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
75
+ data.props,
76
+ PermissionType.Block,
77
+ ).map((row: UserPermission): Permission => {
78
+ return row.permission;
79
+ });
80
+
81
+ for (const requirement of SubscriberTemplateIncidentRecordAccess.getRequirements(
82
+ data.placeholders,
83
+ )) {
84
+ const isGranted: boolean = PermissionHelper.doesPermissionsIntersect(
85
+ unrestricted,
86
+ requirement.permissions,
87
+ );
88
+ const isBlocked: boolean = PermissionHelper.doesPermissionsIntersect(
89
+ blocked,
90
+ requirement.permissions,
91
+ );
92
+
93
+ if (isGranted && !isBlocked) {
94
+ continue;
95
+ }
96
+
97
+ const placeholders: string = data.placeholders
98
+ .map((name: string): string => {
99
+ return `{{${name}}}`;
100
+ })
101
+ .join(", ");
102
+
103
+ throw new NotAuthorizedException(
104
+ `Placing ${placeholders} in a subscriber notification template needs permission to ${requirement.description} for every incident in the project: these values come from your team's incident records, which the status page does not show. You need one of these permissions, not limited to some labels or to incidents you own: ${PermissionHelper.getPermissionTitles(
105
+ requirement.permissions,
106
+ ).join(", ")}.`,
107
+ );
108
+ }
109
+ }
110
+
111
+ /*
112
+ * What these placeholders need: reading incidents, and the column each
113
+ * one reads - Incident.labels for {{incidentLabels}}; Incident.customFields
114
+ * and the field definitions for {{customFields.<key>}}.
115
+ */
116
+ public static getRequirements(
117
+ placeholders: Array<string>,
118
+ ): Array<AccessRequirement> {
119
+ const requirements: Array<AccessRequirement> = [
120
+ {
121
+ description: "read incidents",
122
+ permissions: this.getTableReadPermissions(Incident),
123
+ },
124
+ ];
125
+
126
+ if (
127
+ placeholders.some((name: string): boolean => {
128
+ return name.startsWith(CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX);
129
+ })
130
+ ) {
131
+ requirements.push(
132
+ {
133
+ description: "read incident custom field values",
134
+ permissions: this.getColumnReadPermissions(Incident, "customFields"),
135
+ },
136
+ {
137
+ description: "read incident custom fields",
138
+ permissions: this.getTableReadPermissions(IncidentCustomField),
139
+ },
140
+ );
141
+ }
142
+
143
+ if (
144
+ placeholders.some((name: string): boolean => {
145
+ return !name.startsWith(CUSTOM_FIELD_TEMPLATE_VARIABLE_PREFIX);
146
+ })
147
+ ) {
148
+ requirements.push({
149
+ description: "read incident labels",
150
+ permissions: this.getColumnReadPermissions(Incident, "labels"),
151
+ });
152
+ }
153
+
154
+ return requirements;
155
+ }
156
+
157
+ /*
158
+ * The caller's granted permissions that reach every record, read as the
159
+ * read checks read them (PermissionHelper.getNonAccessControlPermissions,
160
+ * OwnedScopePermission): a row scoped to All, or a Labels (or legacy
161
+ * unscoped) row with no labels. A row limited to labels does not, nor one
162
+ * limited to owned records - except for a project-wide role that cannot
163
+ * be scoped (Project Owner, Project Admin), whose stray Owned scope the
164
+ * read checks ignore too.
165
+ */
166
+ private static getUnrestrictedPermissions(
167
+ props: DatabaseCommonInteractionProps,
168
+ ): Array<Permission> {
169
+ return DatabaseCommonInteractionPropsUtil.getUserPermissions(
170
+ props,
171
+ PermissionType.Allow,
172
+ )
173
+ .filter((row: UserPermission): boolean => {
174
+ if (row.scope === PermissionScope.All) {
175
+ return true;
176
+ }
177
+
178
+ if (row.scope === PermissionScope.Owned) {
179
+ return !PermissionHelper.isScopeApplicable(row.permission);
180
+ }
181
+
182
+ return !row.labelIds || row.labelIds.length === 0;
183
+ })
184
+ .map((row: UserPermission): Permission => {
185
+ return row.permission;
186
+ });
187
+ }
188
+
189
+ // As the table check reads them, the operational resource wildcard included.
190
+ private static getTableReadPermissions(
191
+ modelType: DatabaseBaseModelType,
192
+ ): Array<Permission> {
193
+ const model: BaseModel = new modelType();
194
+ const permissions: Array<Permission> = [...model.readRecordPermissions];
195
+
196
+ if (
197
+ model.isOperationalResource &&
198
+ !permissions.includes(Permission.ReadAllOperationalResources)
199
+ ) {
200
+ permissions.push(Permission.ReadAllOperationalResources);
201
+ }
202
+
203
+ return permissions;
204
+ }
205
+
206
+ // As the select check reads them: no wildcard reaches a column.
207
+ private static getColumnReadPermissions(
208
+ modelType: DatabaseBaseModelType,
209
+ columnName: string,
210
+ ): Array<Permission> {
211
+ return [
212
+ ...(new modelType().getColumnAccessControlFor(columnName)?.read || []),
213
+ ];
214
+ }
215
+ }
@@ -1,5 +1,6 @@
1
1
  import StatusPageResource from "../../Models/DatabaseModels/StatusPageResource";
2
2
  import Dictionary from "../../Types/Dictionary";
3
+ import SafeHtml from "../../Types/SafeHtml";
3
4
 
4
5
  export interface GetResourcesGroupedByGroupNameOptions {
5
6
  resources: Array<StatusPageResource>;
@@ -16,6 +17,12 @@ export const HTML_RESOURCE_GROUP_SEPARATOR: string = "<br/>";
16
17
  */
17
18
  export const PLAIN_TEXT_RESOURCE_GROUP_SEPARATOR: string = "; ";
18
19
 
20
+ type FormatNameFunction = (name: string) => string;
21
+
22
+ const asWritten: FormatNameFunction = (name: string): string => {
23
+ return name;
24
+ };
25
+
19
26
  export default class StatusPageResourceUtil {
20
27
  /**
21
28
  * Formats an array of StatusPageResource items into an HTML string grouped by their resource group.
@@ -24,8 +31,18 @@ export default class StatusPageResourceUtil {
24
31
  * If resources are grouped, returns one group per line, like:
25
32
  * "EU: Infrastructure, Website<br/>UK: Infrastructure, API"
26
33
  *
34
+ * The result is HTML: every resource name, group name and the default
35
+ * value is HTML-escaped before it is joined, and the only markup is the
36
+ * "<br/>" between groups. A project member names resources and groups, and
37
+ * the default subscriber emails put this list into their raw-HTML slot, so
38
+ * a name such as `<a href="https://evil.example">Checkout</a>` would
39
+ * otherwise arrive as a live link. That is also what makes it safe to hand
40
+ * a custom email template as SafeHtml.
41
+ *
27
42
  * Use this only where the value is rendered as HTML (email bodies). Use
28
- * getResourcesGroupedByGroupNameAsPlainText everywhere else.
43
+ * getResourcesGroupedByGroupNameAsPlainText everywhere else: it is not
44
+ * escaped, because SMS, Slack, Teams, subjects and webhooks show text as
45
+ * written.
29
46
  *
30
47
  * @param resources - Array of StatusPageResource items with displayName, statusPageGroupId, and optionally statusPageGroup.name
31
48
  * @param defaultValue - Value to return if no resources (defaults to "")
@@ -35,11 +52,12 @@ export default class StatusPageResourceUtil {
35
52
  resources: Array<StatusPageResource>,
36
53
  defaultValue: string = "",
37
54
  ): string {
38
- return StatusPageResourceUtil.formatResourcesGroupedByGroupName(
39
- resources,
40
- defaultValue,
41
- HTML_RESOURCE_GROUP_SEPARATOR,
42
- );
55
+ return StatusPageResourceUtil.formatResourcesGroupedByGroupName({
56
+ resources: resources,
57
+ defaultValue: defaultValue,
58
+ groupSeparator: HTML_RESOURCE_GROUP_SEPARATOR,
59
+ formatName: SafeHtml.escape,
60
+ });
43
61
  }
44
62
 
45
63
  /**
@@ -55,18 +73,28 @@ export default class StatusPageResourceUtil {
55
73
  resources: Array<StatusPageResource>,
56
74
  defaultValue: string = "",
57
75
  ): string {
58
- return StatusPageResourceUtil.formatResourcesGroupedByGroupName(
59
- resources,
60
- defaultValue,
61
- PLAIN_TEXT_RESOURCE_GROUP_SEPARATOR,
62
- );
76
+ return StatusPageResourceUtil.formatResourcesGroupedByGroupName({
77
+ resources: resources,
78
+ defaultValue: defaultValue,
79
+ groupSeparator: PLAIN_TEXT_RESOURCE_GROUP_SEPARATOR,
80
+ formatName: asWritten,
81
+ });
63
82
  }
64
83
 
65
- private static formatResourcesGroupedByGroupName(
66
- resources: Array<StatusPageResource>,
67
- defaultValue: string,
68
- groupSeparator: string,
69
- ): string {
84
+ /*
85
+ * Groups are keyed by the name as written, so two groups are merged only
86
+ * when their names really are the same; formatName (the HTML escaping, for
87
+ * the HTML form) is applied to each piece of text as it is written out.
88
+ */
89
+ private static formatResourcesGroupedByGroupName(data: {
90
+ resources: Array<StatusPageResource>;
91
+ defaultValue: string;
92
+ groupSeparator: string;
93
+ formatName: FormatNameFunction;
94
+ }): string {
95
+ const { resources, groupSeparator, formatName } = data;
96
+ const defaultValue: string = formatName(data.defaultValue);
97
+
70
98
  if (!resources || resources.length === 0) {
71
99
  return defaultValue;
72
100
  }
@@ -83,9 +111,10 @@ export default class StatusPageResourceUtil {
83
111
  .map((r: StatusPageResource) => {
84
112
  return r.displayName;
85
113
  })
86
- .filter((name: string | undefined) => {
87
- return name;
114
+ .filter((name: string | undefined): name is string => {
115
+ return Boolean(name);
88
116
  })
117
+ .map(formatName)
89
118
  .join(", ") || ""
90
119
  );
91
120
  }
@@ -119,13 +148,15 @@ export default class StatusPageResourceUtil {
119
148
  // Add grouped resources
120
149
  for (const groupName in resourcesByGroup) {
121
150
  const groupResources: Array<string> = resourcesByGroup[groupName]!;
122
- formattedGroups.push(`${groupName}: ${groupResources.join(", ")}`);
151
+ formattedGroups.push(
152
+ `${formatName(groupName)}: ${groupResources.map(formatName).join(", ")}`,
153
+ );
123
154
  }
124
155
 
125
156
  // Add ungrouped resources on separate lines (without "Other" label)
126
157
  if (ungroupedResources.length > 0) {
127
158
  for (const resourceName of ungroupedResources) {
128
- formattedGroups.push(resourceName);
159
+ formattedGroups.push(formatName(resourceName));
129
160
  }
130
161
  }
131
162
 
@@ -15,6 +15,7 @@ export default class VMUtil {
15
15
  allowPrivateNetworkRequests?: boolean | undefined;
16
16
  privateNetworkAccessIsAllowed?: boolean | undefined;
17
17
  privateNetworkHint?: string | undefined;
18
+ includeResolutionDetailInError?: boolean | undefined;
18
19
  };
19
20
  }): Promise<ReturnResult> {
20
21
  return VMRunner.runCodeInSandbox(data);
@@ -117,6 +117,14 @@ export default class VMRunner {
117
117
  * knows which process's environment that is; the guard does not.
118
118
  */
119
119
  privateNetworkHint?: string | undefined;
120
+ /*
121
+ * Whether a refusal decided by DNS may tell the script what DNS
122
+ * answered (see SSRFProtection). Absent ⇒ the guard's deployment
123
+ * default, which is right for the workflow caller on the API server.
124
+ * The Probe passes false: a shared probe must not let one tenant's
125
+ * script map the network it sits in.
126
+ */
127
+ includeResolutionDetailInError?: boolean | undefined;
120
128
  };
121
129
  }): Promise<ReturnResult> {
122
130
  const { code, options } = data;
@@ -548,6 +556,8 @@ export default class VMRunner {
548
556
  */
549
557
  targetLabel: "Request URL",
550
558
  privateNetworkHint: options.privateNetworkHint,
559
+ includeResolutionDetailInError:
560
+ options.includeResolutionDetailInError,
551
561
  });
552
562
  const canonicalUrl: string = validatedTarget.url.toString();
553
563
  const pinnedLookup: EgressLookupFunction =
@@ -65,6 +65,12 @@ export interface OAuth2TokenHttpRequest {
65
65
  // Form fields, sent as application/x-www-form-urlencoded.
66
66
  body: Dictionary<string>;
67
67
  timeoutInMs: number;
68
+ /*
69
+ * Optional: cancels the request when aborted. timeoutInMs is how long the
70
+ * socket may sit idle, so a server that trickles its answer can outlast it;
71
+ * a caller that needs a wall-clock bound aborts this.
72
+ */
73
+ signal?: AbortSignal | undefined;
68
74
  }
69
75
 
70
76
  export interface OAuth2TokenHttpResponse {
@@ -676,6 +682,10 @@ export default class OAuth2TokenClient {
676
682
  httpsAgent: httpsAgent,
677
683
  };
678
684
 
685
+ if (request.signal) {
686
+ axiosOptions.signal = request.signal;
687
+ }
688
+
679
689
  try {
680
690
  const response: AxiosResponse<string> = await axios(axiosOptions);
681
691
 
@@ -4,7 +4,8 @@
4
4
 
5
5
  Password reset and email verification links carry their token in the URL
6
6
  *path* — /accounts/reset-password/<token>, /accounts/verify-email/<token>,
7
- /reset-password/<token> on a status page. Every analytics and advertising
7
+ /reset-password/<token> on a status page — and so does the unsubscribe
8
+ link in every status page notification, /unsubscribe/<subscriberId>-<token>. Every analytics and advertising
8
9
  tag on the page reports the page URL as a matter of course, so the token
9
10
  left the browser to whoever the container happened to be loading. Measured
10
11
  on production before this partial existed, a single visit to
@@ -51,12 +52,14 @@
51
52
  var STORAGE_KEY = "oneuptime-sensitive-url-token";
52
53
 
53
54
  /*
54
- * Route segments whose NEXT path segment is a single-use bearer token.
55
- * Matched against the second-to-last segment, so this covers
55
+ * Route segments whose NEXT path segment is a bearer token. Matched
56
+ * against the second-to-last segment, so this covers
56
57
  * /accounts/reset-password/<token>, /reset-password/<token> and
57
- * /status-page/<id>/reset-password/<token> alike.
58
+ * /status-page/<id>/reset-password/<token> alike. "unsubscribe" is a
59
+ * status page subscriber's unsubscribe link, whose last segment is
60
+ * <subscriberId>-<token> (Common/Types/StatusPage/StatusPageSubscriberUnsubscribe).
58
61
  */
59
- var TOKEN_ROUTES = ["reset-password", "verify-email"];
62
+ var TOKEN_ROUTES = ["reset-password", "verify-email", "unsubscribe"];
60
63
 
61
64
  function segmentsOf(pathname) {
62
65
  var parts = String(pathname || "").split("/");