@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,772 @@
1
+ import StatusPageSubscriberNotificationTemplateService from "../../../Server/Services/StatusPageSubscriberNotificationTemplateService";
2
+ import StatusPageSubscriberNotificationTemplateStatusPageService from "../../../Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService";
3
+ import StatusPageSubscriberNotificationTemplateStatusPage from "../../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplateStatusPage";
4
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
5
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
6
+ import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
7
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
8
+ import SubscriberTemplateIncidentRecordAccess from "../../../Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess";
9
+ import StatusPageSubscriberNotificationTemplate from "../../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplate";
10
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
11
+ import PermissionScope from "../../../Types/Database/AccessControl/PermissionScope";
12
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
13
+ import ObjectID from "../../../Types/ObjectID";
14
+ import Permission, {
15
+ UserPermission,
16
+ UserTenantAccessPermission,
17
+ } from "../../../Types/Permission";
18
+ import StatusPageSubscriberNotificationEventType from "../../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
19
+ import StatusPageSubscriberNotificationMethod from "../../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
20
+ import UserType from "../../../Types/UserType";
21
+ import {
22
+ afterEach,
23
+ beforeEach,
24
+ describe,
25
+ expect,
26
+ jest,
27
+ test,
28
+ } from "@jest/globals";
29
+
30
+ /*
31
+ * A custom subscriber notification template can place {{incidentLabels}}
32
+ * and any {{customFields.<key>}}: values from the team's incident records
33
+ * that the status page does not show. The status page roles may write
34
+ * templates, and add a Slack, Teams or webhook subscriber pointing at an
35
+ * address they own, but may not read incidents. So a template that places
36
+ * incident records is refused unless whoever writes it may read them, for
37
+ * every incident of the project. Placeholders a template already holds stay
38
+ * allowed on an update.
39
+ */
40
+
41
+ type Model = StatusPageSubscriberNotificationTemplate;
42
+
43
+ const PROJECT_ID: ObjectID = new ObjectID(
44
+ "11111111-1111-4111-8111-111111111111",
45
+ );
46
+ const TEMPLATE_ID: ObjectID = new ObjectID(
47
+ "22222222-2222-4222-8222-222222222222",
48
+ );
49
+ const LABEL_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
50
+
51
+ const LEAKING_SLACK_TEMPLATE: string =
52
+ "{{incidentTitle}} {{customFields.root_cause}} {{customFields.customer_account}}";
53
+
54
+ type OnBeforeCreateFunction = (
55
+ createBy: CreateBy<Model>,
56
+ ) => Promise<OnCreate<Model>>;
57
+
58
+ type OnBeforeUpdateFunction = (
59
+ updateBy: UpdateBy<Model>,
60
+ ) => Promise<OnUpdate<Model>>;
61
+
62
+ function row(
63
+ permission: Permission,
64
+ extra: Partial<UserPermission> = {},
65
+ ): UserPermission {
66
+ return {
67
+ _type: "UserPermission",
68
+ permission: permission,
69
+ labelIds: [],
70
+ isBlockPermission: false,
71
+ ...extra,
72
+ };
73
+ }
74
+
75
+ function propsWith(
76
+ rows: Array<UserPermission>,
77
+ extra: Partial<DatabaseCommonInteractionProps> = {},
78
+ ): DatabaseCommonInteractionProps {
79
+ const tenantPermission: UserTenantAccessPermission = {
80
+ projectId: PROJECT_ID,
81
+ _type: "UserTenantAccessPermission",
82
+ permissions: rows,
83
+ };
84
+
85
+ return {
86
+ tenantId: PROJECT_ID,
87
+ userId: ObjectID.generate(),
88
+ userType: UserType.User,
89
+ userTenantAccessPermission: {
90
+ [PROJECT_ID.toString()]: tenantPermission,
91
+ },
92
+ ...extra,
93
+ };
94
+ }
95
+
96
+ const STATUS_PAGE_MEMBER: Array<UserPermission> = [
97
+ row(Permission.StatusPageMember),
98
+ ];
99
+
100
+ function template(data: Partial<Model> = {}): Model {
101
+ const model: Model = new StatusPageSubscriberNotificationTemplate();
102
+ model.projectId = PROJECT_ID;
103
+ model.templateName = "Site 03 Slack";
104
+ model.eventType =
105
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentCreated;
106
+ model.notificationMethod = StatusPageSubscriberNotificationMethod.Slack;
107
+ model.templateBody = "{{incidentTitle}}";
108
+ Object.assign(model, data);
109
+ return model;
110
+ }
111
+
112
+ function create(
113
+ data: Partial<Model>,
114
+ props: DatabaseCommonInteractionProps,
115
+ ): Promise<OnCreate<Model>> {
116
+ return (
117
+ StatusPageSubscriberNotificationTemplateService as unknown as {
118
+ onBeforeCreate: OnBeforeCreateFunction;
119
+ }
120
+ ).onBeforeCreate({ data: template(data), props: props });
121
+ }
122
+
123
+ function update(
124
+ data: Partial<Model>,
125
+ props: DatabaseCommonInteractionProps,
126
+ ): Promise<OnUpdate<Model>> {
127
+ return (
128
+ StatusPageSubscriberNotificationTemplateService as unknown as {
129
+ onBeforeUpdate: OnBeforeUpdateFunction;
130
+ }
131
+ ).onBeforeUpdate({
132
+ query: { _id: TEMPLATE_ID.toString() },
133
+ data: data as unknown as UpdateBy<Model>["data"],
134
+ props: props,
135
+ limit: 1,
136
+ skip: 0,
137
+ });
138
+ }
139
+
140
+ let stored: Array<Model> = [];
141
+
142
+ beforeEach(() => {
143
+ stored = [];
144
+ jest
145
+ .spyOn(StatusPageSubscriberNotificationTemplateService, "findBy")
146
+ .mockImplementation((async () => {
147
+ return stored;
148
+ }) as never);
149
+ });
150
+
151
+ afterEach(() => {
152
+ jest.restoreAllMocks();
153
+ });
154
+
155
+ describe("creating a template that places incident records", () => {
156
+ test("the table's own access control lets a Status Page Member write one", () => {
157
+ // Why the check is needed: nothing else stands in the way.
158
+ expect(() => {
159
+ ModelPermission.checkCreatePermissions(
160
+ StatusPageSubscriberNotificationTemplate,
161
+ template({ templateBody: LEAKING_SLACK_TEMPLATE }),
162
+ propsWith(STATUS_PAGE_MEMBER),
163
+ );
164
+ }).not.toThrow();
165
+ });
166
+
167
+ test.each([
168
+ ["Status Page Member", [row(Permission.StatusPageMember)]],
169
+ ["Status Page Admin", [row(Permission.StatusPageAdmin)]],
170
+ [
171
+ "Create Status Page Subscriber Notification Template",
172
+ [row(Permission.CreateStatusPageSubscriberNotificationTemplate)],
173
+ ],
174
+ ] as Array<[string, Array<UserPermission>]>)(
175
+ "is refused for a %s, who cannot read incidents",
176
+ async (_role: string, rows: Array<UserPermission>) => {
177
+ await expect(
178
+ create({ templateBody: LEAKING_SLACK_TEMPLATE }, propsWith(rows)),
179
+ ).rejects.toThrow(NotAuthorizedException);
180
+ },
181
+ );
182
+
183
+ test("the refusal names the placeholders and the permissions that would allow them", async () => {
184
+ await expect(
185
+ create(
186
+ { templateBody: LEAKING_SLACK_TEMPLATE },
187
+ propsWith(STATUS_PAGE_MEMBER),
188
+ ),
189
+ ).rejects.toThrow(
190
+ /Placing \{\{customFields\.customer_account\}\}, \{\{customFields\.root_cause\}\} in a subscriber notification template needs permission to read incidents .*Project Member.*Incident Viewer/,
191
+ );
192
+ });
193
+
194
+ test.each([
195
+ ["the labels", "Labels: {{incidentLabels}}"],
196
+ ["a guessed key", "{{customFields.password}}"],
197
+ ["spaces inside the braces", "{{ customFields.root_cause }}"],
198
+ ])("is refused for %s too", async (_what: string, templateBody: string) => {
199
+ await expect(
200
+ create({ templateBody: templateBody }, propsWith(STATUS_PAGE_MEMBER)),
201
+ ).rejects.toThrow(NotAuthorizedException);
202
+ });
203
+
204
+ test("is refused in an email subject too", async () => {
205
+ await expect(
206
+ create(
207
+ {
208
+ notificationMethod: StatusPageSubscriberNotificationMethod.Email,
209
+ templateBody: "<p>{{incidentTitle}}</p>",
210
+ emailSubject: "[{{customFields.root_cause}}] {{incidentTitle}}",
211
+ },
212
+ propsWith(STATUS_PAGE_MEMBER),
213
+ ),
214
+ ).rejects.toThrow(NotAuthorizedException);
215
+ });
216
+
217
+ /*
218
+ * An update may change the event type without touching the text, so a
219
+ * template for any event is checked.
220
+ */
221
+ test("is refused for a template of an event that does not fill them", async () => {
222
+ await expect(
223
+ create(
224
+ {
225
+ eventType:
226
+ StatusPageSubscriberNotificationEventType.SubscriberAnnouncementCreated,
227
+ templateBody: "{{customFields.root_cause}}",
228
+ },
229
+ propsWith(STATUS_PAGE_MEMBER),
230
+ ),
231
+ ).rejects.toThrow(NotAuthorizedException);
232
+ });
233
+
234
+ test("a Status Page Member can still write one that places only what the status page shows", async () => {
235
+ await expect(
236
+ create(
237
+ {
238
+ templateBody:
239
+ "{{incidentTitle}} {{incidentDescription}} {{incidentSeverity}} {{resourcesAffected}} {{affectedStatusPages}} {{statusPageName}} {{unsubscribeUrl}}",
240
+ },
241
+ propsWith(STATUS_PAGE_MEMBER),
242
+ ),
243
+ ).resolves.toBeDefined();
244
+ });
245
+
246
+ test.each([
247
+ ["Project Owner", [row(Permission.ProjectOwner)]],
248
+ ["Project Admin", [row(Permission.ProjectAdmin)]],
249
+ ["Project Member", [row(Permission.ProjectMember)]],
250
+ [
251
+ "Status Page Member who is also an Incident Viewer",
252
+ [row(Permission.StatusPageMember), row(Permission.IncidentViewer)],
253
+ ],
254
+ [
255
+ "Status Page Member who may read incidents and their custom fields",
256
+ [
257
+ row(Permission.StatusPageMember),
258
+ row(Permission.ReadProjectIncident),
259
+ row(Permission.ReadIncidentCustomField),
260
+ ],
261
+ ],
262
+ [
263
+ "Incident Viewer scoped to All, labels and all",
264
+ [
265
+ row(Permission.StatusPageMember),
266
+ row(Permission.IncidentViewer, {
267
+ scope: PermissionScope.All,
268
+ labelIds: [LABEL_ID],
269
+ }),
270
+ ],
271
+ ],
272
+ [
273
+ "Project Owner whose row carries a stray Owned scope",
274
+ [row(Permission.ProjectOwner, { scope: PermissionScope.Owned })],
275
+ ],
276
+ ] as Array<[string, Array<UserPermission>]>)(
277
+ "is allowed for a %s",
278
+ async (_role: string, rows: Array<UserPermission>) => {
279
+ await expect(
280
+ create(
281
+ {
282
+ templateBody: `${LEAKING_SLACK_TEMPLATE} {{incidentLabels}}`,
283
+ },
284
+ propsWith(rows),
285
+ ),
286
+ ).resolves.toBeDefined();
287
+ },
288
+ );
289
+
290
+ test.each([
291
+ [
292
+ "who may read incidents but not the custom field definitions",
293
+ [row(Permission.StatusPageMember), row(Permission.ReadProjectIncident)],
294
+ ],
295
+ [
296
+ "who may read every operational resource, which reaches no column",
297
+ [
298
+ row(Permission.StatusPageMember),
299
+ row(Permission.ReadAllOperationalResources),
300
+ row(Permission.ReadIncidentCustomField),
301
+ ],
302
+ ],
303
+ [
304
+ "whose incident access is limited to some labels",
305
+ [
306
+ row(Permission.StatusPageMember),
307
+ row(Permission.IncidentViewer, { labelIds: [LABEL_ID] }),
308
+ ],
309
+ ],
310
+ [
311
+ "whose incident access is limited to some labels, scoped as such",
312
+ [
313
+ row(Permission.StatusPageMember),
314
+ row(Permission.IncidentViewer, {
315
+ scope: PermissionScope.Labels,
316
+ labelIds: [LABEL_ID],
317
+ }),
318
+ ],
319
+ ],
320
+ [
321
+ "whose incident access is limited to incidents they own",
322
+ [
323
+ row(Permission.StatusPageMember),
324
+ row(Permission.IncidentViewer, { scope: PermissionScope.Owned }),
325
+ ],
326
+ ],
327
+ [
328
+ "who is a Project Member blocked from some incidents",
329
+ [
330
+ row(Permission.ProjectMember),
331
+ row(Permission.IncidentViewer, {
332
+ isBlockPermission: true,
333
+ labelIds: [LABEL_ID],
334
+ }),
335
+ ],
336
+ ],
337
+ ] as Array<[string, Array<UserPermission>]>)(
338
+ "is refused for someone %s",
339
+ async (_who: string, rows: Array<UserPermission>) => {
340
+ await expect(
341
+ create({ templateBody: LEAKING_SLACK_TEMPLATE }, propsWith(rows)),
342
+ ).rejects.toThrow(NotAuthorizedException);
343
+ },
344
+ );
345
+
346
+ test("the labels need the labels column, not the custom fields", async () => {
347
+ const readsIncidentsOnly: Array<UserPermission> = [
348
+ row(Permission.StatusPageMember),
349
+ row(Permission.ReadProjectIncident),
350
+ ];
351
+
352
+ await expect(
353
+ create(
354
+ { templateBody: "{{incidentLabels}}" },
355
+ propsWith(readsIncidentsOnly),
356
+ ),
357
+ ).resolves.toBeDefined();
358
+ await expect(
359
+ create(
360
+ { templateBody: "{{customFields.root_cause}}" },
361
+ propsWith(readsIncidentsOnly),
362
+ ),
363
+ ).rejects.toThrow(/read incident custom fields/);
364
+ });
365
+
366
+ test("root and master admin writes are not checked", async () => {
367
+ await expect(
368
+ create({ templateBody: LEAKING_SLACK_TEMPLATE }, { isRoot: true }),
369
+ ).resolves.toBeDefined();
370
+ await expect(
371
+ create(
372
+ { templateBody: LEAKING_SLACK_TEMPLATE },
373
+ propsWith([], { isMasterAdmin: true }),
374
+ ),
375
+ ).resolves.toBeDefined();
376
+ });
377
+
378
+ test("an API key with only status page permissions is refused like a user", async () => {
379
+ await expect(
380
+ create(
381
+ { templateBody: LEAKING_SLACK_TEMPLATE },
382
+ propsWith(STATUS_PAGE_MEMBER, {
383
+ userType: UserType.API,
384
+ userId: undefined,
385
+ }),
386
+ ),
387
+ ).rejects.toThrow(NotAuthorizedException);
388
+ });
389
+ });
390
+
391
+ describe("updating a template", () => {
392
+ test("a Status Page Member cannot add a custom field to it", async () => {
393
+ stored = [template({ templateBody: "{{incidentTitle}}" })];
394
+
395
+ await expect(
396
+ update(
397
+ { templateBody: "{{incidentTitle}} {{customFields.root_cause}}" },
398
+ propsWith(STATUS_PAGE_MEMBER),
399
+ ),
400
+ ).rejects.toThrow(
401
+ /Placing \{\{customFields\.root_cause\}\} in a subscriber notification template/,
402
+ );
403
+ });
404
+
405
+ test("the templates it would change are read as root, in the caller's project", async () => {
406
+ stored = [template({ templateBody: "{{incidentTitle}}" })];
407
+
408
+ await expect(
409
+ update(
410
+ { templateBody: "{{customFields.root_cause}}" },
411
+ propsWith(STATUS_PAGE_MEMBER),
412
+ ),
413
+ ).rejects.toThrow(NotAuthorizedException);
414
+
415
+ const findBy: jest.Mock =
416
+ StatusPageSubscriberNotificationTemplateService.findBy as unknown as jest.Mock;
417
+ const args: {
418
+ query: Record<string, unknown>;
419
+ props: DatabaseCommonInteractionProps;
420
+ } = findBy.mock.calls[0]![0] as {
421
+ query: Record<string, unknown>;
422
+ props: DatabaseCommonInteractionProps;
423
+ };
424
+
425
+ expect(args.query).toEqual({
426
+ _id: TEMPLATE_ID.toString(),
427
+ projectId: PROJECT_ID,
428
+ });
429
+ expect(args.props.isRoot).toBe(true);
430
+ });
431
+
432
+ test("but can edit a template that already places one: fix the text, move it, keep it", async () => {
433
+ stored = [
434
+ template({
435
+ templateBody: "Root cause: {{customFields.root_cause}}",
436
+ emailSubject: "{{incidentLabels}}",
437
+ }),
438
+ ];
439
+
440
+ await expect(
441
+ update(
442
+ {
443
+ templateBody:
444
+ "Root cause (fixed typo): {{customFields.root_cause}} {{incidentLabels}}",
445
+ emailSubject: "{{incidentTitle}}",
446
+ },
447
+ propsWith(STATUS_PAGE_MEMBER),
448
+ ),
449
+ ).resolves.toBeDefined();
450
+ });
451
+
452
+ test("or rename it, or change anything else, without the text being read", async () => {
453
+ stored = [template({ templateBody: "{{customFields.root_cause}}" })];
454
+
455
+ await expect(
456
+ update({ templateName: "Renamed" }, propsWith(STATUS_PAGE_MEMBER)),
457
+ ).resolves.toBeDefined();
458
+ await expect(
459
+ update(
460
+ { templateBody: "{{incidentTitle}}" },
461
+ propsWith(STATUS_PAGE_MEMBER),
462
+ ),
463
+ ).resolves.toBeDefined();
464
+ expect(
465
+ StatusPageSubscriberNotificationTemplateService.findBy,
466
+ ).not.toHaveBeenCalled();
467
+ });
468
+
469
+ test("an update of several templates is refused if it adds one to any of them", async () => {
470
+ stored = [
471
+ template({ templateBody: "{{customFields.root_cause}}" }),
472
+ template({ templateBody: "{{incidentTitle}}" }),
473
+ ];
474
+
475
+ await expect(
476
+ update(
477
+ { templateBody: "{{customFields.root_cause}}" },
478
+ propsWith(STATUS_PAGE_MEMBER),
479
+ ),
480
+ ).rejects.toThrow(NotAuthorizedException);
481
+ });
482
+
483
+ test("someone who may read incidents can add one", async () => {
484
+ stored = [template({ templateBody: "{{incidentTitle}}" })];
485
+
486
+ await expect(
487
+ update(
488
+ { templateBody: LEAKING_SLACK_TEMPLATE },
489
+ propsWith([row(Permission.ProjectMember)]),
490
+ ),
491
+ ).resolves.toBeDefined();
492
+ });
493
+
494
+ test("root updates are not checked, nor read", async () => {
495
+ await expect(
496
+ update({ templateBody: LEAKING_SLACK_TEMPLATE }, { isRoot: true }),
497
+ ).resolves.toBeDefined();
498
+ expect(
499
+ StatusPageSubscriberNotificationTemplateService.findBy,
500
+ ).not.toHaveBeenCalled();
501
+ });
502
+ });
503
+
504
+ /*
505
+ * Changing where a template is sent - its channel or its event type - sends
506
+ * everything it places somewhere new: an admin's email template placing an
507
+ * internal custom field, switched to Slack, would post it to whatever Slack
508
+ * subscriber a status page role adds. So it counts as placing everything the
509
+ * template holds.
510
+ */
511
+ describe("pointing a template somewhere else", () => {
512
+ test("a Status Page Member cannot switch the channel of a template that places incident records", async () => {
513
+ stored = [
514
+ template({
515
+ notificationMethod: StatusPageSubscriberNotificationMethod.Email,
516
+ templateBody: "Root cause: {{customFields.internal_root_cause}}",
517
+ }),
518
+ ];
519
+
520
+ await expect(
521
+ update(
522
+ { notificationMethod: StatusPageSubscriberNotificationMethod.Slack },
523
+ propsWith(STATUS_PAGE_MEMBER),
524
+ ),
525
+ ).rejects.toThrow(
526
+ /Placing \{\{customFields\.internal_root_cause\}\} in a subscriber notification template/,
527
+ );
528
+ });
529
+
530
+ test("nor its event type", async () => {
531
+ stored = [template({ emailSubject: "{{incidentLabels}}" })];
532
+
533
+ await expect(
534
+ update(
535
+ {
536
+ eventType:
537
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentStateChanged,
538
+ },
539
+ propsWith(STATUS_PAGE_MEMBER),
540
+ ),
541
+ ).rejects.toThrow(NotAuthorizedException);
542
+ });
543
+
544
+ test("the channel and event sent back unchanged, with a typo fixed, still go through", async () => {
545
+ stored = [
546
+ template({
547
+ notificationMethod: StatusPageSubscriberNotificationMethod.Email,
548
+ templateBody: "Root cause: {{customFields.internal_root_cause}}",
549
+ }),
550
+ ];
551
+
552
+ await expect(
553
+ update(
554
+ {
555
+ notificationMethod: StatusPageSubscriberNotificationMethod.Email,
556
+ eventType:
557
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentCreated,
558
+ templateBody:
559
+ "Root cause (typo fixed): {{customFields.internal_root_cause}}",
560
+ },
561
+ propsWith(STATUS_PAGE_MEMBER),
562
+ ),
563
+ ).resolves.toBeDefined();
564
+ });
565
+
566
+ test("a template that places only what the status page shows can be pointed anywhere", async () => {
567
+ stored = [template({ templateBody: "{{incidentTitle}}" })];
568
+
569
+ await expect(
570
+ update(
571
+ { notificationMethod: StatusPageSubscriberNotificationMethod.Webhook },
572
+ propsWith(STATUS_PAGE_MEMBER),
573
+ ),
574
+ ).resolves.toBeDefined();
575
+ });
576
+
577
+ test("the new text is what counts when the same write replaces it", async () => {
578
+ stored = [template({ templateBody: "{{customFields.root_cause}}" })];
579
+
580
+ await expect(
581
+ update(
582
+ {
583
+ notificationMethod: StatusPageSubscriberNotificationMethod.Webhook,
584
+ templateBody: "{{incidentTitle}}",
585
+ },
586
+ propsWith(STATUS_PAGE_MEMBER),
587
+ ),
588
+ ).resolves.toBeDefined();
589
+ });
590
+
591
+ test("someone who may read incidents can switch it", async () => {
592
+ stored = [template({ templateBody: "{{customFields.root_cause}}" })];
593
+
594
+ await expect(
595
+ update(
596
+ { notificationMethod: StatusPageSubscriberNotificationMethod.Webhook },
597
+ propsWith([row(Permission.ProjectMember)]),
598
+ ),
599
+ ).resolves.toBeDefined();
600
+ });
601
+ });
602
+
603
+ /*
604
+ * Linking a template to a status page sends that page's subscribers what it
605
+ * places. A status page role may link templates and add its own Slack,
606
+ * Teams or webhook subscriber to the page, so linking a template that places
607
+ * incident records - or moving a link to another page - is checked the same.
608
+ */
609
+ describe("linking a template to a status page", () => {
610
+ type LinkModel = StatusPageSubscriberNotificationTemplateStatusPage;
611
+
612
+ const LINK_ID: ObjectID = new ObjectID(
613
+ "44444444-4444-4444-8444-444444444444",
614
+ );
615
+ const STATUS_PAGE_ID: ObjectID = new ObjectID(
616
+ "55555555-5555-4555-8555-555555555555",
617
+ );
618
+
619
+ function createLink(
620
+ props: DatabaseCommonInteractionProps,
621
+ ): Promise<OnCreate<LinkModel>> {
622
+ const link: LinkModel =
623
+ new StatusPageSubscriberNotificationTemplateStatusPage();
624
+ link.projectId = PROJECT_ID;
625
+ link.statusPageId = STATUS_PAGE_ID;
626
+ link.statusPageSubscriberNotificationTemplateId = TEMPLATE_ID;
627
+
628
+ return (
629
+ StatusPageSubscriberNotificationTemplateStatusPageService as unknown as {
630
+ onBeforeCreate: (
631
+ createBy: CreateBy<LinkModel>,
632
+ ) => Promise<OnCreate<LinkModel>>;
633
+ }
634
+ ).onBeforeCreate({ data: link, props: props });
635
+ }
636
+
637
+ function updateLink(
638
+ data: Partial<LinkModel>,
639
+ props: DatabaseCommonInteractionProps,
640
+ ): Promise<OnUpdate<LinkModel>> {
641
+ return (
642
+ StatusPageSubscriberNotificationTemplateStatusPageService as unknown as {
643
+ onBeforeUpdate: (
644
+ updateBy: UpdateBy<LinkModel>,
645
+ ) => Promise<OnUpdate<LinkModel>>;
646
+ }
647
+ ).onBeforeUpdate({
648
+ query: { _id: LINK_ID.toString() },
649
+ data: data as unknown as UpdateBy<LinkModel>["data"],
650
+ props: props,
651
+ limit: 1,
652
+ skip: 0,
653
+ });
654
+ }
655
+
656
+ beforeEach(() => {
657
+ const link: LinkModel =
658
+ new StatusPageSubscriberNotificationTemplateStatusPage();
659
+ link.statusPageSubscriberNotificationTemplateId = TEMPLATE_ID;
660
+
661
+ jest
662
+ .spyOn(
663
+ StatusPageSubscriberNotificationTemplateStatusPageService,
664
+ "findBy",
665
+ )
666
+ .mockResolvedValue([link] as never);
667
+ });
668
+
669
+ test("a Status Page Member cannot link a template that places incident records", async () => {
670
+ stored = [
671
+ template({ templateBody: "{{customFields.internal_root_cause}}" }),
672
+ ];
673
+
674
+ await expect(createLink(propsWith(STATUS_PAGE_MEMBER))).rejects.toThrow(
675
+ /Placing \{\{customFields\.internal_root_cause\}\} in a subscriber notification template/,
676
+ );
677
+
678
+ // The template is read as root, by id, in the caller's project.
679
+ const args: {
680
+ query: Record<string, unknown>;
681
+ props: DatabaseCommonInteractionProps;
682
+ } = (
683
+ StatusPageSubscriberNotificationTemplateService.findBy as unknown as jest.Mock
684
+ ).mock.calls[0]![0] as {
685
+ query: Record<string, unknown>;
686
+ props: DatabaseCommonInteractionProps;
687
+ };
688
+ expect(args.query["projectId"]).toEqual(PROJECT_ID);
689
+ expect(args.props.isRoot).toBe(true);
690
+ });
691
+
692
+ test("but can link one that places only what the status page shows", async () => {
693
+ stored = [template({ templateBody: "{{incidentTitle}}" })];
694
+
695
+ await expect(
696
+ createLink(propsWith(STATUS_PAGE_MEMBER)),
697
+ ).resolves.toBeDefined();
698
+ });
699
+
700
+ test("cannot move a link to another status page, or point it at another template, that places them", async () => {
701
+ stored = [template({ templateBody: "{{incidentLabels}}" })];
702
+
703
+ await expect(
704
+ updateLink(
705
+ { statusPageId: ObjectID.generate() },
706
+ propsWith(STATUS_PAGE_MEMBER),
707
+ ),
708
+ ).rejects.toThrow(NotAuthorizedException);
709
+ await expect(
710
+ updateLink(
711
+ { statusPageSubscriberNotificationTemplateId: TEMPLATE_ID },
712
+ propsWith(STATUS_PAGE_MEMBER),
713
+ ),
714
+ ).rejects.toThrow(NotAuthorizedException);
715
+ });
716
+
717
+ test("someone who may read incidents can link it, and root is not checked", async () => {
718
+ stored = [template({ templateBody: "{{customFields.root_cause}}" })];
719
+
720
+ await expect(
721
+ createLink(propsWith([row(Permission.ProjectMember)])),
722
+ ).resolves.toBeDefined();
723
+ await expect(createLink({ isRoot: true })).resolves.toBeDefined();
724
+ });
725
+ });
726
+
727
+ describe("SubscriberTemplateIncidentRecordAccess.getRequirements", () => {
728
+ function permissionsFor(placeholders: Array<string>): Array<Array<string>> {
729
+ return SubscriberTemplateIncidentRecordAccess.getRequirements(
730
+ placeholders,
731
+ ).map((requirement: { permissions: Array<Permission> }) => {
732
+ return requirement.permissions;
733
+ });
734
+ }
735
+
736
+ test("reads the permissions from the models, so they follow the models", () => {
737
+ const [incidents, values, definitions] = permissionsFor([
738
+ "customFields.root_cause",
739
+ ]);
740
+
741
+ expect(incidents).toEqual(
742
+ expect.arrayContaining([
743
+ Permission.IncidentViewer,
744
+ Permission.ReadProjectIncident,
745
+ Permission.ReadAllOperationalResources,
746
+ ]),
747
+ );
748
+ expect(values).toEqual(
749
+ expect.arrayContaining([
750
+ Permission.IncidentViewer,
751
+ Permission.ReadProjectIncident,
752
+ ]),
753
+ );
754
+ expect(definitions).toEqual(
755
+ expect.arrayContaining([Permission.ReadIncidentCustomField]),
756
+ );
757
+
758
+ // No status page role reads any of them.
759
+ for (const permissions of [incidents, values, definitions]) {
760
+ expect(permissions).not.toContain(Permission.StatusPageAdmin);
761
+ expect(permissions).not.toContain(Permission.StatusPageMember);
762
+ expect(permissions).not.toContain(Permission.StatusPageViewer);
763
+ }
764
+ });
765
+
766
+ test("labels need incidents and the labels column only", () => {
767
+ expect(permissionsFor(["incidentLabels"])).toHaveLength(2);
768
+ expect(permissionsFor(["customFields.a", "incidentLabels"])).toHaveLength(
769
+ 4,
770
+ );
771
+ });
772
+ });