@oneuptime/common 12.0.7 → 12.0.9

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 (873) hide show
  1. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
  2. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
  3. package/Models/DatabaseModels/AIConversation.ts +32 -0
  4. package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
  5. package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
  6. package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
  7. package/Models/DatabaseModels/Index.ts +12 -4
  8. package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +184 -9
  9. package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
  10. package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
  11. package/Models/DatabaseModels/NetworkDevice.ts +141 -0
  12. package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
  13. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
  14. package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
  16. package/Models/DatabaseModels/Project.ts +78 -0
  17. package/Models/DatabaseModels/UserCall.ts +42 -0
  18. package/Models/DatabaseModels/UserEmail.ts +44 -0
  19. package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
  20. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
  21. package/Models/DatabaseModels/UserPush.ts +49 -0
  22. package/Models/DatabaseModels/UserSMS.ts +43 -0
  23. package/Models/DatabaseModels/UserTelegram.ts +59 -0
  24. package/Models/DatabaseModels/UserWebhook.ts +52 -0
  25. package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
  26. package/Models/DatabaseModels/WorkflowLog.ts +38 -0
  27. package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
  28. package/Server/API/AIChatAPI.ts +315 -1
  29. package/Server/API/DashboardAPI.ts +217 -1
  30. package/Server/API/OnCallReadinessAPI.ts +841 -0
  31. package/Server/API/TeamComplianceAPI.ts +69 -17
  32. package/Server/API/TelemetryAPI.ts +220 -12
  33. package/Server/API/UserAPI.ts +16 -1
  34. package/Server/EnvironmentConfig.ts +52 -0
  35. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
  47. package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
  48. package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
  49. package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
  50. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
  51. package/Server/Infrastructure/Queue.ts +78 -13
  52. package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
  53. package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
  54. package/Server/Services/AIService.ts +7 -0
  55. package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
  56. package/Server/Services/AlertSeverityService.ts +63 -0
  57. package/Server/Services/DashboardService.ts +9 -10
  58. package/Server/Services/DatabaseService.ts +32 -2
  59. package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
  60. package/Server/Services/IncidentSeverityService.ts +76 -0
  61. package/Server/Services/Index.ts +12 -4
  62. package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
  63. package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +7 -4
  64. package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
  65. package/Server/Services/LogAggregationService.ts +45 -8
  66. package/Server/Services/MetricAggregationService.ts +121 -0
  67. package/Server/Services/MetricService.ts +7 -7
  68. package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
  69. package/Server/Services/NetworkDeviceLinkService.ts +84 -0
  70. package/Server/Services/NetworkDeviceService.ts +140 -0
  71. package/Server/Services/NetworkSiteService.ts +77 -25
  72. package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
  73. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
  74. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
  75. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
  76. package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
  77. package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
  78. package/Server/Services/OnCallReadinessService.ts +2803 -0
  79. package/Server/Services/OnCallSetupReminderService.ts +955 -0
  80. package/Server/Services/ProfileAggregationService.ts +123 -0
  81. package/Server/Services/StatusPageService.ts +9 -10
  82. package/Server/Services/TeamComplianceService.ts +429 -252
  83. package/Server/Services/UserCallService.ts +26 -1
  84. package/Server/Services/UserEmailService.ts +26 -1
  85. package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
  86. package/Server/Services/UserNotificationRuleService.ts +3812 -333
  87. package/Server/Services/UserOnCallLogService.ts +561 -48
  88. package/Server/Services/UserPushService.ts +29 -0
  89. package/Server/Services/UserService.ts +11 -0
  90. package/Server/Services/UserSmsService.ts +26 -1
  91. package/Server/Services/UserTelegramService.ts +24 -1
  92. package/Server/Services/UserWebhookService.ts +28 -1
  93. package/Server/Services/UserWhatsAppService.ts +24 -1
  94. package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
  95. package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
  96. package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
  97. package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
  98. package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
  99. package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
  100. package/Server/Types/Workflow/Components/API/Get.ts +1 -1
  101. package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
  102. package/Server/Types/Workflow/Components/API/Post.ts +1 -1
  103. package/Server/Types/Workflow/Components/API/Put.ts +1 -1
  104. package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
  105. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
  106. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
  107. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
  108. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
  109. package/Server/Types/Workflow/Components/Email.ts +25 -7
  110. package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
  111. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
  112. package/Server/Types/Workflow/TriggerCode.ts +12 -0
  113. package/Server/Types/Workflow/Workflow.ts +5 -0
  114. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
  115. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
  116. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
  117. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
  118. package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
  119. package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
  120. package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
  121. package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
  122. package/Server/Utils/AI/Toolbox/Index.ts +48 -0
  123. package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
  124. package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
  125. package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
  126. package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
  127. package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
  128. package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
  129. package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
  130. package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
  131. package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
  132. package/Server/Utils/ClientIp.ts +221 -0
  133. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
  134. package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
  135. package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
  136. package/Server/Utils/Express.ts +12 -17
  137. package/Server/Utils/LLM/LLMService.ts +85 -8
  138. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
  139. package/Server/Utils/SSRFProtection.ts +98 -23
  140. package/Server/Utils/StartServer.ts +12 -3
  141. package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
  142. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
  143. package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
  144. package/Server/Utils/VM/VMAPI.ts +56 -8
  145. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
  146. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
  147. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
  148. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
  149. package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
  150. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
  151. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
  152. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
  153. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
  154. package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
  155. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
  156. package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
  157. package/Tests/Models/InventoryItemModel.test.ts +174 -0
  158. package/Tests/Models/InventoryItemNaming.test.ts +302 -0
  159. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
  160. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
  161. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
  162. package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
  163. package/Tests/Server/API/Helpers.ts +24 -15
  164. package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
  165. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
  166. package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
  167. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
  168. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
  169. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
  170. package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
  171. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
  172. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
  173. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
  174. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
  175. package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
  176. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
  177. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +248 -0
  178. package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
  179. package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
  180. package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
  181. package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
  182. package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
  183. package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
  184. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
  185. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
  186. package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
  187. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
  188. package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
  189. package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
  190. package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
  191. package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
  192. package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
  193. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
  194. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
  195. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
  196. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
  197. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
  198. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
  199. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
  200. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
  201. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
  202. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
  203. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
  204. package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
  205. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
  206. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
  207. package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
  208. package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
  209. package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
  210. package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
  211. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
  212. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
  213. package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
  214. package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
  215. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
  216. package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
  217. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
  218. package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
  219. package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
  220. package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
  221. package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
  222. package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
  223. package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
  224. package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
  225. package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
  226. package/Tests/Server/Utils/ClientIp.test.ts +438 -0
  227. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
  228. package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
  229. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
  230. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
  231. package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
  232. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
  233. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
  234. package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
  235. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
  236. package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
  237. package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
  238. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
  239. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
  240. package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
  241. package/Tests/Types/IP/IP.test.ts +263 -0
  242. package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
  243. package/Tests/Types/IP/IPv6.test.ts +12 -1
  244. package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
  245. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
  246. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
  247. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
  248. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
  249. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
  250. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
  251. package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
  252. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
  253. package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
  254. package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
  255. package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
  256. package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
  257. package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
  258. package/Tests/Types/Workflow/Templates.test.ts +1218 -0
  259. package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
  260. package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
  261. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
  262. package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
  263. package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
  264. package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
  265. package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
  266. package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
  267. package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
  268. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
  269. package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
  270. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
  271. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
  272. package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
  273. package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
  274. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
  275. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
  276. package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
  277. package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
  278. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
  279. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
  280. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
  281. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
  282. package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
  283. package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
  284. package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
  285. package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
  286. package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
  287. package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
  288. package/Tests/UI/EsbuildConfig.test.ts +607 -0
  289. package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
  290. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  291. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
  292. package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
  293. package/Tests/UI/Utils/Platform.test.ts +147 -0
  294. package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
  295. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
  296. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
  297. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
  298. package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
  299. package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
  300. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
  301. package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
  302. package/Tests/__mocks__/bullmq.js +55 -0
  303. package/Types/AI/AIChatMessageStatus.ts +8 -1
  304. package/Types/AI/AIChatTypes.ts +12 -0
  305. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
  306. package/Types/Exception/ExceptionCode.ts +2 -0
  307. package/Types/Exception/ServiceUnavailableException.ts +8 -0
  308. package/Types/Exception/TooManyRequestsException.ts +8 -0
  309. package/Types/IP/IP.ts +93 -47
  310. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +80 -3
  311. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
  312. package/Types/OnCallDutyPolicy/Layer.ts +203 -149
  313. package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
  314. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
  315. package/Types/Permission.ts +193 -0
  316. package/Types/SerializableObjectDictionary.ts +133 -39
  317. package/Types/Telemetry/EntityRelationshipType.ts +1 -1
  318. package/Types/Telemetry/EntitySource.ts +40 -0
  319. package/Types/Telemetry/EntityType.ts +28 -1
  320. package/Types/Telemetry/EntityTypeGroups.ts +65 -0
  321. package/Types/Workflow/Component.ts +29 -0
  322. package/Types/Workflow/Components/API.ts +35 -0
  323. package/Types/Workflow/Components/BaseModel.ts +77 -27
  324. package/Types/Workflow/Components/Discord.ts +1 -0
  325. package/Types/Workflow/Components/Email.ts +12 -3
  326. package/Types/Workflow/Components/JavaScript.ts +7 -0
  327. package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
  328. package/Types/Workflow/Components/Slack.ts +1 -0
  329. package/Types/Workflow/Components/Telegram.ts +1 -0
  330. package/Types/Workflow/StepTrace.ts +181 -0
  331. package/Types/Workflow/TemplateSyntax.ts +543 -0
  332. package/Types/Workflow/Templates.ts +2368 -0
  333. package/UI/Components/Calendar/Calendar.css +43 -0
  334. package/UI/Components/Calendar/Calendar.tsx +8 -0
  335. package/UI/Components/Card/Card.tsx +2 -2
  336. package/UI/Components/Checkbox/Checkbox.tsx +16 -0
  337. package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
  338. package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
  339. package/UI/Components/Dictionary/Dictionary.tsx +71 -15
  340. package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
  341. package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
  342. package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
  343. package/UI/Components/Forms/Types/Field.ts +7 -0
  344. package/UI/Components/Forms/Validation.ts +63 -1
  345. package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
  346. package/UI/Components/Input/Input.tsx +32 -3
  347. package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
  348. package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
  349. package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
  350. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
  351. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
  352. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
  353. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
  354. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
  355. package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
  356. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
  357. package/UI/Components/LogsViewer/types.ts +23 -0
  358. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
  359. package/UI/Components/Modal/Modal.tsx +37 -4
  360. package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
  361. package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
  362. package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
  363. package/UI/Components/Table/Table.tsx +49 -16
  364. package/UI/Components/Table/TableBody.tsx +53 -28
  365. package/UI/Components/Table/TableHeader.tsx +13 -0
  366. package/UI/Components/Table/TableRow.tsx +58 -26
  367. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
  368. package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
  369. package/UI/Components/Workflow/Component.tsx +28 -26
  370. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
  371. package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
  372. package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
  373. package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
  374. package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
  375. package/UI/Components/Workflow/GraphLint.ts +628 -0
  376. package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
  377. package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
  378. package/UI/Components/Workflow/ModelSchema.ts +278 -0
  379. package/UI/Components/Workflow/RunForm.tsx +41 -7
  380. package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
  381. package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
  382. package/UI/Components/Workflow/UseRunWatch.ts +212 -0
  383. package/UI/Components/Workflow/Utils.ts +93 -1
  384. package/UI/Components/Workflow/VariableModal.tsx +6 -2
  385. package/UI/Components/Workflow/Workflow.tsx +126 -7
  386. package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
  387. package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
  388. package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
  389. package/UI/Types/LayeredDismissal.ts +31 -0
  390. package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
  391. package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
  392. package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
  393. package/UI/Utils/Platform.ts +149 -0
  394. package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
  395. package/UI/esbuild-config.js +22 -1
  396. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
  397. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
  398. package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
  399. package/Utils/Telemetry/CrossSignalScope.ts +502 -0
  400. package/Utils/Telemetry/EntityKey.ts +71 -5
  401. package/Utils/Telemetry/EntityRelationship.ts +1 -1
  402. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
  403. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
  404. package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
  405. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  406. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
  407. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
  408. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
  409. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  410. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
  411. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  412. package/build/dist/Models/DatabaseModels/Index.js +12 -4
  413. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  414. package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +212 -29
  415. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
  416. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
  417. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
  418. package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +52 -25
  419. package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
  420. package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
  421. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  422. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
  423. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
  424. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
  425. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
  426. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
  427. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
  428. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
  429. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
  430. package/build/dist/Models/DatabaseModels/Project.js +80 -0
  431. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  432. package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
  433. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  434. package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
  435. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  436. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
  437. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  438. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
  439. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  440. package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
  441. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  442. package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
  443. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  444. package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
  445. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  446. package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
  447. package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
  448. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
  449. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  450. package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
  451. package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
  452. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
  453. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  454. package/build/dist/Server/API/AIChatAPI.js +237 -1
  455. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  456. package/build/dist/Server/API/DashboardAPI.js +165 -13
  457. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  458. package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
  459. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
  460. package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
  461. package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
  462. package/build/dist/Server/API/TelemetryAPI.js +129 -18
  463. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  464. package/build/dist/Server/API/UserAPI.js +16 -1
  465. package/build/dist/Server/API/UserAPI.js.map +1 -1
  466. package/build/dist/Server/EnvironmentConfig.js +45 -0
  467. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  468. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
  469. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  470. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
  471. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
  472. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
  473. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
  474. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
  475. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
  476. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
  477. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
  478. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
  479. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
  480. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
  481. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
  482. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
  483. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
  484. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
  485. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
  486. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
  487. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
  488. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
  489. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
  490. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
  491. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
  492. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
  493. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
  494. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
  495. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
  496. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  497. package/build/dist/Server/Infrastructure/Queue.js +72 -13
  498. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  499. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
  500. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  501. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
  502. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
  503. package/build/dist/Server/Services/AIService.js +1 -0
  504. package/build/dist/Server/Services/AIService.js.map +1 -1
  505. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
  506. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  507. package/build/dist/Server/Services/AlertSeverityService.js +54 -0
  508. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  509. package/build/dist/Server/Services/DashboardService.js +10 -9
  510. package/build/dist/Server/Services/DashboardService.js.map +1 -1
  511. package/build/dist/Server/Services/DatabaseService.js +24 -2
  512. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  513. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
  514. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  515. package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
  516. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  517. package/build/dist/Server/Services/Index.js +12 -4
  518. package/build/dist/Server/Services/Index.js.map +1 -1
  519. package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
  520. package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
  521. package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +9 -6
  522. package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
  523. package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -23
  524. package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
  525. package/build/dist/Server/Services/LogAggregationService.js +27 -8
  526. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  527. package/build/dist/Server/Services/MetricAggregationService.js +80 -0
  528. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  529. package/build/dist/Server/Services/MetricService.js +6 -6
  530. package/build/dist/Server/Services/MetricService.js.map +1 -1
  531. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
  532. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
  533. package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
  534. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
  535. package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
  536. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  537. package/build/dist/Server/Services/NetworkSiteService.js +53 -13
  538. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  539. package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
  540. package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
  541. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
  542. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  543. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
  544. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  545. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
  546. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  547. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
  548. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  549. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
  550. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
  551. package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
  552. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
  553. package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
  554. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
  555. package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
  556. package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
  557. package/build/dist/Server/Services/StatusPageService.js +11 -10
  558. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  559. package/build/dist/Server/Services/TeamComplianceService.js +312 -160
  560. package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
  561. package/build/dist/Server/Services/UserCallService.js +24 -1
  562. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  563. package/build/dist/Server/Services/UserEmailService.js +24 -1
  564. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  565. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
  566. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
  567. package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
  568. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  569. package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
  570. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  571. package/build/dist/Server/Services/UserPushService.js +26 -0
  572. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  573. package/build/dist/Server/Services/UserService.js +10 -0
  574. package/build/dist/Server/Services/UserService.js.map +1 -1
  575. package/build/dist/Server/Services/UserSmsService.js +24 -1
  576. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  577. package/build/dist/Server/Services/UserTelegramService.js +22 -1
  578. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  579. package/build/dist/Server/Services/UserWebhookService.js +25 -1
  580. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  581. package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
  582. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  583. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
  584. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  585. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
  586. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  587. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
  588. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
  589. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
  590. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
  591. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
  592. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  593. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
  594. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  595. package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
  596. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  597. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
  598. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  599. package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
  600. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  601. package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
  602. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  603. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
  604. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  605. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
  606. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  607. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
  608. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  609. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
  610. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
  611. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
  612. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  613. package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
  614. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  615. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
  616. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  617. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
  618. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  619. package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
  620. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
  621. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  622. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
  623. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  624. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
  625. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  626. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
  627. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  628. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
  629. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  630. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
  631. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
  632. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
  633. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  634. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
  635. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  636. package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
  637. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  638. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
  639. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
  640. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
  641. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
  642. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
  643. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
  644. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
  645. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
  646. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
  647. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
  648. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
  649. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
  650. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
  651. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
  652. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
  653. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
  654. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
  655. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
  656. package/build/dist/Server/Utils/ClientIp.js +137 -0
  657. package/build/dist/Server/Utils/ClientIp.js.map +1 -0
  658. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
  659. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  660. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
  661. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
  662. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
  663. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
  664. package/build/dist/Server/Utils/Express.js +12 -12
  665. package/build/dist/Server/Utils/Express.js.map +1 -1
  666. package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
  667. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  668. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
  669. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  670. package/build/dist/Server/Utils/SSRFProtection.js +82 -21
  671. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  672. package/build/dist/Server/Utils/StartServer.js +12 -4
  673. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  674. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
  675. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  676. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
  677. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
  678. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
  679. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  680. package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
  681. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  682. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
  683. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  684. package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
  685. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
  686. package/build/dist/Types/AI/AIChatTypes.js +12 -0
  687. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  688. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
  689. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
  690. package/build/dist/Types/Exception/ExceptionCode.js +2 -0
  691. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  692. package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
  693. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
  694. package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
  695. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
  696. package/build/dist/Types/IP/IP.js +87 -43
  697. package/build/dist/Types/IP/IP.js.map +1 -1
  698. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
  699. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
  700. package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
  701. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  702. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
  703. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
  704. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
  705. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  706. package/build/dist/Types/Permission.js +174 -0
  707. package/build/dist/Types/Permission.js.map +1 -1
  708. package/build/dist/Types/SerializableObjectDictionary.js +133 -39
  709. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  710. package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
  711. package/build/dist/Types/Telemetry/EntitySource.js +39 -0
  712. package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
  713. package/build/dist/Types/Telemetry/EntityType.js +28 -1
  714. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  715. package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
  716. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
  717. package/build/dist/Types/Workflow/Component.js +21 -0
  718. package/build/dist/Types/Workflow/Component.js.map +1 -1
  719. package/build/dist/Types/Workflow/Components/API.js +35 -0
  720. package/build/dist/Types/Workflow/Components/API.js.map +1 -1
  721. package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
  722. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  723. package/build/dist/Types/Workflow/Components/Discord.js +1 -0
  724. package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
  725. package/build/dist/Types/Workflow/Components/Email.js +12 -3
  726. package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
  727. package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
  728. package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
  729. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
  730. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
  731. package/build/dist/Types/Workflow/Components/Slack.js +1 -0
  732. package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
  733. package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
  734. package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
  735. package/build/dist/Types/Workflow/StepTrace.js +104 -0
  736. package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
  737. package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
  738. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
  739. package/build/dist/Types/Workflow/Templates.js +2111 -0
  740. package/build/dist/Types/Workflow/Templates.js.map +1 -0
  741. package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
  742. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  743. package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
  744. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  745. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
  746. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
  747. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
  748. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
  749. package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
  750. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  751. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  752. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
  753. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  754. package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
  755. package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
  756. package/build/dist/UI/Components/Forms/Validation.js +44 -0
  757. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  758. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
  759. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  760. package/build/dist/UI/Components/Input/Input.js +13 -4
  761. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  762. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
  763. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
  764. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
  765. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
  766. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
  767. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  768. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
  769. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  770. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
  771. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  772. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
  773. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  774. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
  775. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  776. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
  777. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  778. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
  779. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  780. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
  781. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  782. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  783. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
  784. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  785. package/build/dist/UI/Components/Modal/Modal.js +31 -5
  786. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  787. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
  788. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  789. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
  790. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
  791. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
  792. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
  793. package/build/dist/UI/Components/Table/Table.js +27 -15
  794. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  795. package/build/dist/UI/Components/Table/TableBody.js +24 -18
  796. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  797. package/build/dist/UI/Components/Table/TableHeader.js +9 -1
  798. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  799. package/build/dist/UI/Components/Table/TableRow.js +29 -21
  800. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  801. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
  802. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  803. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
  804. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  805. package/build/dist/UI/Components/Workflow/Component.js +20 -19
  806. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  807. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
  808. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  809. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +35 -7
  810. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  811. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
  812. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
  813. package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
  814. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  815. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
  816. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  817. package/build/dist/UI/Components/Workflow/GraphLint.js +425 -0
  818. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
  819. package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
  820. package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
  821. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +320 -0
  822. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
  823. package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
  824. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
  825. package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
  826. package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
  827. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
  828. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
  829. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
  830. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
  831. package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
  832. package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
  833. package/build/dist/UI/Components/Workflow/Utils.js +73 -1
  834. package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
  835. package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
  836. package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
  837. package/build/dist/UI/Components/Workflow/Workflow.js +92 -7
  838. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  839. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
  840. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
  841. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
  842. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
  843. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
  844. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
  845. package/build/dist/UI/Types/LayeredDismissal.js +21 -0
  846. package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
  847. package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
  848. package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
  849. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
  850. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
  851. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
  852. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  853. package/build/dist/UI/Utils/Platform.js +118 -0
  854. package/build/dist/UI/Utils/Platform.js.map +1 -0
  855. package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
  856. package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
  857. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
  858. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
  859. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
  860. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
  861. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
  862. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  863. package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
  864. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
  865. package/build/dist/Utils/Telemetry/EntityKey.js +57 -5
  866. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  867. package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
  868. package/jest.config.json +1 -0
  869. package/package.json +1 -1
  870. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
  871. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
  872. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
  873. package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
@@ -0,0 +1,1394 @@
1
+ import DatabaseConfig from "../../../Server/DatabaseConfig";
2
+ import AuditLogService from "../../../Server/Services/AuditLogService";
3
+ import MailService from "../../../Server/Services/MailService";
4
+ import ProjectService from "../../../Server/Services/ProjectService";
5
+ import TeamMemberService from "../../../Server/Services/TeamMemberService";
6
+ import UserCallService from "../../../Server/Services/UserCallService";
7
+ import UserEmailService from "../../../Server/Services/UserEmailService";
8
+ import UserNotificationRuleAdminService from "../../../Server/Services/UserNotificationRuleAdminService";
9
+ import UserNotificationRuleService, {
10
+ ExecuteNotificationRuleOptions,
11
+ } from "../../../Server/Services/UserNotificationRuleService";
12
+ import UserOnCallLogService from "../../../Server/Services/UserOnCallLogService";
13
+ import UserOnCallLogTimelineService from "../../../Server/Services/UserOnCallLogTimelineService";
14
+ import UserPushService from "../../../Server/Services/UserPushService";
15
+ import UserService from "../../../Server/Services/UserService";
16
+ import UserSmsService from "../../../Server/Services/UserSmsService";
17
+ import UserTelegramService from "../../../Server/Services/UserTelegramService";
18
+ import UserWebhookService from "../../../Server/Services/UserWebhookService";
19
+ import UserWhatsAppService from "../../../Server/Services/UserWhatsAppService";
20
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
21
+ import FindOneByID from "../../../Server/Types/Database/FindOneByID";
22
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
23
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
24
+ import logger from "../../../Server/Utils/Logger";
25
+ import Project from "../../../Models/DatabaseModels/Project";
26
+ import TeamMember from "../../../Models/DatabaseModels/TeamMember";
27
+ import User from "../../../Models/DatabaseModels/User";
28
+ import UserNotificationRule from "../../../Models/DatabaseModels/UserNotificationRule";
29
+ import UserOnCallLogTimeline from "../../../Models/DatabaseModels/UserOnCallLogTimeline";
30
+ import URL from "../../../Types/API/URL";
31
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
32
+ import Email from "../../../Types/Email";
33
+ import Exception from "../../../Types/Exception/Exception";
34
+ import Name from "../../../Types/Name";
35
+ import NotificationRuleType from "../../../Types/NotificationRule/NotificationRuleType";
36
+ import ObjectID from "../../../Types/ObjectID";
37
+ import UserNotificationEventType from "../../../Types/UserNotification/UserNotificationEventType";
38
+ import UserNotificationStatus from "../../../Types/UserNotification/UserNotificationStatus";
39
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
40
+
41
+ /*
42
+ * Phase 3 opens UserNotificationRule to project administrators so that somebody
43
+ * other than the owner can repair a broken on-call configuration. Every test in
44
+ * this file exists because opening that door, done carelessly, converts a
45
+ * support feature into a paging-hijack primitive.
46
+ *
47
+ * The shape of the attack is worth stating once, because it is what every
48
+ * assertion below is really about. A rule row has TWO halves that nothing in
49
+ * the ORM, the permission layer or any screen ever compares:
50
+ *
51
+ * userId -> whose on-call pages select this row
52
+ * user<X>Id -> which address the page is delivered to
53
+ *
54
+ * Set the first to the victim and the second to something the attacker
55
+ * controls, and the victim's pages arrive at the attacker's endpoint. The
56
+ * victim's rules page still says they are covered. The on-call log still says a
57
+ * page went out. Nothing anywhere reports a problem — the page simply reaches
58
+ * the wrong human, during the incident where it mattered.
59
+ *
60
+ * So the tests here are adversarial by construction. The happy paths are
61
+ * present only to prove the guards do not simply reject everything (a guard
62
+ * that blocks legitimate repair gets deleted by the next engineer, and then
63
+ * there is no guard at all); the weight is on the inputs an attacker would
64
+ * actually send:
65
+ *
66
+ * R1 a target user id from ANOTHER project, presented by a caller who is a
67
+ * legitimate administrator of THIS one.
68
+ * R3 a method FK pointing at somebody else's row — on create, on update, in
69
+ * the FK slot, in the relation slot, and on all seven channels, because
70
+ * a guard covering six of seven covers none.
71
+ * R3 on update specifically: a body that LIES about the rule's owner, to
72
+ * check that the owner is re-read from the database rather than believed.
73
+ * R6 an audit trail and an owner notification keyed on the server-resolved
74
+ * actor versus the PERSISTED owner, never on anything in the body.
75
+ * D-i-D a row that is already hijacked, to check it is inert at delivery time
76
+ * even though no write path should have produced it.
77
+ *
78
+ * No database is touched: every service the guards consult is a jest.spyOn
79
+ * stub, and the protected hooks are reached through a structural cast, the same
80
+ * way the neighbouring characterisation tests reach theirs.
81
+ */
82
+
83
+ const PROJECT_ID: ObjectID = new ObjectID(
84
+ "11111111-1111-4111-8111-111111111111",
85
+ );
86
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
87
+ "1111aaaa-1111-4111-8111-111111111111",
88
+ );
89
+ const ADMIN_USER_ID: ObjectID = new ObjectID(
90
+ "22222222-2222-4222-8222-222222222222",
91
+ );
92
+ const VICTIM_USER_ID: ObjectID = new ObjectID(
93
+ "33333333-3333-4333-8333-333333333333",
94
+ );
95
+ const OUTSIDER_USER_ID: ObjectID = new ObjectID(
96
+ "44444444-4444-4444-8444-444444444444",
97
+ );
98
+ const METHOD_ID: ObjectID = new ObjectID(
99
+ "55555555-5555-4555-8555-555555555555",
100
+ );
101
+ const RULE_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
102
+ const TEAM_MEMBER_ID: ObjectID = new ObjectID(
103
+ "77777777-7777-4777-8777-777777777777",
104
+ );
105
+ const LOG_ID: ObjectID = new ObjectID("88888888-8888-4888-8888-888888888888");
106
+ const TIMELINE_ID: ObjectID = new ObjectID(
107
+ "99999999-9999-4999-8999-999999999999",
108
+ );
109
+ const INCIDENT_ID: ObjectID = new ObjectID(
110
+ "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
111
+ );
112
+
113
+ /*
114
+ * The seven channels, each with the FK column a rule carries it in, the
115
+ * relation slot that is the same write spelled differently, the service the
116
+ * guard must consult, and the word the rejection message has to contain.
117
+ *
118
+ * Driving every ownership test off this one table is the point: the guard's
119
+ * only real failure mode is forgetting a channel, and a table-driven test
120
+ * cannot forget one without the count assertion below noticing.
121
+ */
122
+ interface ChannelFixture {
123
+ idColumn: string;
124
+ relationColumn: string;
125
+ label: string;
126
+ service: { findOneById: (...args: Array<never>) => unknown };
127
+ relationProperty: string;
128
+ }
129
+
130
+ const CHANNELS: Array<ChannelFixture> = [
131
+ {
132
+ idColumn: "userEmailId",
133
+ relationColumn: "userEmail",
134
+ label: "Email",
135
+ service: UserEmailService as unknown as ChannelFixture["service"],
136
+ relationProperty: "userEmail",
137
+ },
138
+ {
139
+ idColumn: "userSmsId",
140
+ relationColumn: "userSms",
141
+ label: "SMS",
142
+ service: UserSmsService as unknown as ChannelFixture["service"],
143
+ relationProperty: "userSms",
144
+ },
145
+ {
146
+ idColumn: "userCallId",
147
+ relationColumn: "userCall",
148
+ label: "Call",
149
+ service: UserCallService as unknown as ChannelFixture["service"],
150
+ relationProperty: "userCall",
151
+ },
152
+ {
153
+ idColumn: "userWhatsAppId",
154
+ relationColumn: "userWhatsApp",
155
+ label: "WhatsApp",
156
+ service: UserWhatsAppService as unknown as ChannelFixture["service"],
157
+ relationProperty: "userWhatsApp",
158
+ },
159
+ {
160
+ idColumn: "userTelegramId",
161
+ relationColumn: "userTelegram",
162
+ label: "Telegram",
163
+ service: UserTelegramService as unknown as ChannelFixture["service"],
164
+ relationProperty: "userTelegram",
165
+ },
166
+ {
167
+ idColumn: "userPushId",
168
+ relationColumn: "userPush",
169
+ label: "Push",
170
+ service: UserPushService as unknown as ChannelFixture["service"],
171
+ relationProperty: "userPush",
172
+ },
173
+ {
174
+ idColumn: "userWebhookId",
175
+ relationColumn: "userWebhook",
176
+ label: "Webhook",
177
+ service: UserWebhookService as unknown as ChannelFixture["service"],
178
+ relationProperty: "userWebhook",
179
+ },
180
+ ];
181
+
182
+ /*
183
+ * The four hooks under test are protected. They are ordinary prototype methods
184
+ * at runtime; TypeScript just will not let a test name them, so they are
185
+ * reached through a structural cast.
186
+ */
187
+ interface RuleServiceHooks {
188
+ onBeforeCreate: (
189
+ createBy: CreateBy<UserNotificationRule>,
190
+ ) => Promise<OnCreate<UserNotificationRule>>;
191
+ onCreateSuccess: (
192
+ onCreate: OnCreate<UserNotificationRule>,
193
+ createdItem: UserNotificationRule,
194
+ ) => Promise<UserNotificationRule>;
195
+ onBeforeUpdate: (
196
+ updateBy: UpdateBy<UserNotificationRule>,
197
+ ) => Promise<OnUpdate<UserNotificationRule>>;
198
+ onUpdateSuccess: (
199
+ onUpdate: OnUpdate<UserNotificationRule>,
200
+ updatedItemIds: Array<ObjectID>,
201
+ ) => Promise<OnUpdate<UserNotificationRule>>;
202
+ }
203
+
204
+ function hooks(): RuleServiceHooks {
205
+ return UserNotificationRuleService as unknown as RuleServiceHooks;
206
+ }
207
+
208
+ interface DeliveryHalf {
209
+ deliverNotificationForRule: (
210
+ notificationRuleItem: UserNotificationRule,
211
+ options: ExecuteNotificationRuleOptions,
212
+ ) => Promise<boolean>;
213
+ }
214
+
215
+ function deliveryHalf(): DeliveryHalf {
216
+ return UserNotificationRuleService as unknown as DeliveryHalf;
217
+ }
218
+
219
+ function memberProps(
220
+ overrides: Partial<DatabaseCommonInteractionProps> = {},
221
+ ): DatabaseCommonInteractionProps {
222
+ return {
223
+ userId: ADMIN_USER_ID,
224
+ tenantId: PROJECT_ID,
225
+ ...overrides,
226
+ };
227
+ }
228
+
229
+ /*
230
+ * A create payload as the API hands it to the hook: a real model instance,
231
+ * because the guard reads relation slots as well as FK columns and an object
232
+ * literal would not carry the model's own accessors.
233
+ */
234
+ function createBy(data: {
235
+ userId?: ObjectID | undefined;
236
+ props?: DatabaseCommonInteractionProps | undefined;
237
+ channels?: Record<string, unknown> | undefined;
238
+ isOptOut?: boolean | undefined;
239
+ }): CreateBy<UserNotificationRule> {
240
+ const rule: UserNotificationRule = new UserNotificationRule();
241
+ rule.projectId = PROJECT_ID;
242
+ rule.ruleType = NotificationRuleType.ON_CALL_EXECUTED_INCIDENT;
243
+ rule.notifyAfterMinutes = 0;
244
+
245
+ if (data.userId) {
246
+ rule.userId = data.userId;
247
+ }
248
+
249
+ if (data.isOptOut) {
250
+ rule.isOptOut = true;
251
+ }
252
+
253
+ Object.assign(rule, data.channels || {});
254
+
255
+ return {
256
+ data: rule,
257
+ props: data.props || memberProps(),
258
+ };
259
+ }
260
+
261
+ function persistedRule(
262
+ overrides: Record<string, unknown> = {},
263
+ ): UserNotificationRule {
264
+ return {
265
+ id: RULE_ID,
266
+ _id: RULE_ID.toString(),
267
+ projectId: PROJECT_ID,
268
+ userId: VICTIM_USER_ID,
269
+ ...overrides,
270
+ } as unknown as UserNotificationRule;
271
+ }
272
+
273
+ /*
274
+ * The owner notification is fire-and-forget by design - recordAdminRuleChange
275
+ * hands it a promise with a .catch and does not wait, so that a slow or broken
276
+ * mail server can never surface as a failed write. That makes "was the mail
277
+ * sent" a question about a promise chain the caller deliberately did not await,
278
+ * so the assertions have to let the microtask queue drain first. A macrotask
279
+ * boundary is the only reliable way to do that.
280
+ */
281
+ function flushAsync(): Promise<void> {
282
+ return new Promise<void>((resolve: () => void): void => {
283
+ setTimeout(resolve, 0);
284
+ });
285
+ }
286
+
287
+ /* A method row as its service hands it back: id plus the owner column. */
288
+ function methodOwnedBy(ownerUserId: ObjectID): unknown {
289
+ return {
290
+ id: METHOD_ID,
291
+ _id: METHOD_ID.toString(),
292
+ userId: ownerUserId,
293
+ };
294
+ }
295
+
296
+ function executeOptions(): ExecuteNotificationRuleOptions {
297
+ return {
298
+ projectId: PROJECT_ID,
299
+ userNotificationEventType: UserNotificationEventType.IncidentCreated,
300
+ triggeredByIncidentId: INCIDENT_ID,
301
+ onCallPolicyId: undefined,
302
+ userNotificationLogId: LOG_ID,
303
+ };
304
+ }
305
+
306
+ interface Stubs {
307
+ teamMemberFind: jest.SpyInstance;
308
+ ruleFind: jest.SpyInstance;
309
+ ruleFindOneById: jest.SpyInstance;
310
+ deliver: jest.SpyInstance;
311
+ claim: jest.SpyInstance;
312
+ timelineCreate: jest.SpyInstance;
313
+ mail: jest.SpyInstance;
314
+ userFind: jest.SpyInstance;
315
+ projectFind: jest.SpyInstance;
316
+ dashboardUrl: jest.SpyInstance;
317
+ auditCreate: jest.SpyInstance;
318
+ auditUpdate: jest.SpyInstance;
319
+ methodFinds: Map<string, jest.SpyInstance>;
320
+ }
321
+
322
+ describe("UserNotificationRule administrative write guards", () => {
323
+ let stubs: Stubs;
324
+ let timelineRows: Array<UserOnCallLogTimeline>;
325
+
326
+ beforeEach(() => {
327
+ timelineRows = [];
328
+
329
+ jest.spyOn(logger, "error").mockImplementation((): void => {
330
+ return undefined;
331
+ });
332
+ jest.spyOn(logger, "info").mockImplementation((): void => {
333
+ return undefined;
334
+ });
335
+ jest.spyOn(logger, "warn").mockImplementation((): void => {
336
+ return undefined;
337
+ });
338
+
339
+ const methodFinds: Map<string, jest.SpyInstance> = new Map<
340
+ string,
341
+ jest.SpyInstance
342
+ >();
343
+
344
+ for (const channel of CHANNELS) {
345
+ /*
346
+ * Default: every method row belongs to the victim. Tests that care make
347
+ * the one channel they are about disagree, so a guard that consults the
348
+ * WRONG service still fails the test rather than accidentally passing.
349
+ */
350
+ methodFinds.set(
351
+ channel.idColumn,
352
+ jest
353
+ .spyOn(channel.service, "findOneById")
354
+ .mockResolvedValue(methodOwnedBy(VICTIM_USER_ID) as never),
355
+ );
356
+ }
357
+
358
+ stubs = {
359
+ teamMemberFind: jest
360
+ .spyOn(TeamMemberService, "findOneBy")
361
+ .mockResolvedValue({
362
+ id: TEAM_MEMBER_ID,
363
+ _id: TEAM_MEMBER_ID.toString(),
364
+ } as unknown as TeamMember as never),
365
+ ruleFind: jest
366
+ .spyOn(UserNotificationRuleService, "findBy")
367
+ .mockResolvedValue([persistedRule()] as never),
368
+ ruleFindOneById: jest
369
+ .spyOn(UserNotificationRuleService, "findOneById")
370
+ .mockResolvedValue(null as never),
371
+ deliver: jest
372
+ .spyOn(deliveryHalf(), "deliverNotificationForRule")
373
+ .mockResolvedValue(true as never),
374
+ claim: jest
375
+ .spyOn(UserOnCallLogService, "claimNotificationRuleExecution")
376
+ .mockResolvedValue(true as never),
377
+ timelineCreate: jest
378
+ .spyOn(UserOnCallLogTimelineService, "create")
379
+ .mockImplementation(
380
+ (
381
+ createByArg: CreateBy<UserOnCallLogTimeline>,
382
+ ): Promise<UserOnCallLogTimeline> => {
383
+ timelineRows.push(createByArg.data);
384
+
385
+ return Promise.resolve({
386
+ id: TIMELINE_ID,
387
+ } as unknown as UserOnCallLogTimeline);
388
+ },
389
+ ) as unknown as jest.SpyInstance,
390
+ mail: jest
391
+ .spyOn(MailService, "sendMail")
392
+ .mockResolvedValue(undefined as never),
393
+ userFind: jest
394
+ .spyOn(UserService, "findOneById")
395
+ .mockImplementation(
396
+ (findBy: FindOneByID<User>): Promise<User | null> => {
397
+ const id: ObjectID = findBy.id;
398
+
399
+ return Promise.resolve({
400
+ id: id,
401
+ _id: id.toString(),
402
+ name: new Name(
403
+ id.toString() === ADMIN_USER_ID.toString()
404
+ ? "Ada Admin"
405
+ : "Vic",
406
+ ),
407
+ email: new Email(
408
+ id.toString() === ADMIN_USER_ID.toString()
409
+ ? "ada@example.com"
410
+ : "vic@example.com",
411
+ ),
412
+ } as unknown as User);
413
+ },
414
+ ) as unknown as jest.SpyInstance,
415
+ projectFind: jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
416
+ id: PROJECT_ID,
417
+ name: "Acme",
418
+ } as unknown as Project as never),
419
+ dashboardUrl: jest
420
+ .spyOn(DatabaseConfig, "getDashboardUrl")
421
+ .mockResolvedValue(URL.fromString("https://oneuptime.test") as never),
422
+ auditCreate: jest
423
+ .spyOn(AuditLogService, "recordCreate")
424
+ .mockResolvedValue(undefined as never),
425
+ auditUpdate: jest
426
+ .spyOn(AuditLogService, "recordUpdate")
427
+ .mockResolvedValue(undefined as never),
428
+ methodFinds: methodFinds,
429
+ };
430
+ });
431
+
432
+ afterEach(() => {
433
+ jest.restoreAllMocks();
434
+ });
435
+
436
+ /*
437
+ * ---------------------------------------------------------------------
438
+ * R1 - the target of an on-behalf-of write must be in THIS project.
439
+ * ---------------------------------------------------------------------
440
+ */
441
+ describe("R1 - the rule's owner must be a member of the acting project", () => {
442
+ test("a user id from another project is refused even though the caller is a real admin here", async () => {
443
+ /*
444
+ * The whole attack in one call. The caller genuinely holds an
445
+ * administrative permission on PROJECT_ID, so the permission layer waves
446
+ * them through; the id they name belongs to a user of a project they have
447
+ * nothing to do with. Only a roster check can tell the two apart, and the
448
+ * roster query is what returns nothing here.
449
+ */
450
+ stubs.teamMemberFind.mockResolvedValue(null as never);
451
+
452
+ await expect(
453
+ hooks().onBeforeCreate(
454
+ createBy({
455
+ userId: OUTSIDER_USER_ID,
456
+ channels: { userEmailId: METHOD_ID },
457
+ }),
458
+ ),
459
+ ).rejects.toThrow(
460
+ `Cannot create a notification rule for user ${OUTSIDER_USER_ID.toString()} because they are not a member of this project.`,
461
+ );
462
+ });
463
+
464
+ test("the roster is queried for the acting tenant, with root props and an accepted invitation", async () => {
465
+ await hooks().onBeforeCreate(
466
+ createBy({
467
+ userId: VICTIM_USER_ID,
468
+ channels: { userEmailId: METHOD_ID },
469
+ }),
470
+ );
471
+
472
+ const query: {
473
+ query: {
474
+ userId: ObjectID;
475
+ projectId: ObjectID;
476
+ hasAcceptedInvitation: boolean;
477
+ };
478
+ props: { isRoot: boolean };
479
+ } = stubs.teamMemberFind.mock.calls[0]![0] as {
480
+ query: {
481
+ userId: ObjectID;
482
+ projectId: ObjectID;
483
+ hasAcceptedInvitation: boolean;
484
+ };
485
+ props: { isRoot: boolean };
486
+ };
487
+
488
+ expect(query.query.userId.toString()).toBe(VICTIM_USER_ID.toString());
489
+ /*
490
+ * props.tenantId, NOT a projectId out of the body. A body-supplied
491
+ * project is the attacker naming the roster they want to be checked
492
+ * against, which is the same as no check.
493
+ */
494
+ expect(query.query.projectId.toString()).toBe(PROJECT_ID.toString());
495
+ expect(query.query.hasAcceptedInvitation).toBe(true);
496
+ /*
497
+ * isRoot, because the question is about the database's state. Asking it
498
+ * through the caller's own scope would let a caller who cannot READ the
499
+ * roster get "no rows" back and, under any lenient reading, pass.
500
+ */
501
+ expect(query.props.isRoot).toBe(true);
502
+ });
503
+
504
+ test("a request with no tenant scope cannot write for another user at all", async () => {
505
+ await expect(
506
+ hooks().onBeforeCreate(
507
+ createBy({
508
+ userId: VICTIM_USER_ID,
509
+ props: memberProps({ tenantId: undefined }),
510
+ channels: { userEmailId: METHOD_ID },
511
+ }),
512
+ ),
513
+ ).rejects.toThrow(
514
+ "A project is required to create a notification rule for another user.",
515
+ );
516
+
517
+ expect(stubs.teamMemberFind).not.toHaveBeenCalled();
518
+ });
519
+
520
+ test("a tenant id from a DIFFERENT project scopes the roster query to that project, not to the rule's projectId", async () => {
521
+ /*
522
+ * The rule body says projectId = PROJECT_ID while the session is scoped
523
+ * to OTHER_PROJECT_ID. Membership must be judged by the session, so the
524
+ * query has to name OTHER_PROJECT_ID.
525
+ */
526
+ stubs.teamMemberFind.mockResolvedValue(null as never);
527
+
528
+ await expect(
529
+ hooks().onBeforeCreate(
530
+ createBy({
531
+ userId: VICTIM_USER_ID,
532
+ props: memberProps({ tenantId: OTHER_PROJECT_ID }),
533
+ channels: { userEmailId: METHOD_ID },
534
+ }),
535
+ ),
536
+ ).rejects.toThrow("are not a member of this project");
537
+
538
+ const query: { query: { projectId: ObjectID } } = stubs.teamMemberFind
539
+ .mock.calls[0]![0] as { query: { projectId: ObjectID } };
540
+
541
+ expect(query.query.projectId.toString()).toBe(
542
+ OTHER_PROJECT_ID.toString(),
543
+ );
544
+ });
545
+
546
+ test("a self-write does not consult the roster - a member configuring themselves is not an admin action", async () => {
547
+ stubs.methodFinds
548
+ .get("userEmailId")!
549
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
550
+
551
+ await hooks().onBeforeCreate(
552
+ createBy({
553
+ userId: ADMIN_USER_ID,
554
+ channels: { userEmailId: METHOD_ID },
555
+ }),
556
+ );
557
+
558
+ expect(stubs.teamMemberFind).not.toHaveBeenCalled();
559
+ });
560
+
561
+ test("an omitted ownership column is a self-write, because CreatePermission stamps it after this hook runs", async () => {
562
+ /*
563
+ * Reading data.userId alone here would see `undefined` on every ordinary
564
+ * self-service create - the value is stamped later - and would send every
565
+ * one of them through the on-behalf-of branch.
566
+ */
567
+ stubs.methodFinds
568
+ .get("userEmailId")!
569
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
570
+
571
+ await hooks().onBeforeCreate(
572
+ createBy({
573
+ channels: { userEmailId: METHOD_ID },
574
+ }),
575
+ );
576
+
577
+ expect(stubs.teamMemberFind).not.toHaveBeenCalled();
578
+ /*
579
+ * ...but the method it names is still checked, against the actor as the
580
+ * implied owner.
581
+ */
582
+ expect(stubs.methodFinds.get("userEmailId")).toHaveBeenCalled();
583
+ });
584
+
585
+ test("an opt-out row for another user is still roster-checked - it has no method, but it still silences somebody", async () => {
586
+ stubs.teamMemberFind.mockResolvedValue(null as never);
587
+
588
+ await expect(
589
+ hooks().onBeforeCreate(
590
+ createBy({
591
+ userId: OUTSIDER_USER_ID,
592
+ isOptOut: true,
593
+ }),
594
+ ),
595
+ ).rejects.toThrow("are not a member of this project");
596
+ });
597
+
598
+ test("an actor-less caller writing for a named user is treated as on-behalf-of, not as a self-write", async () => {
599
+ /*
600
+ * An API key has no user identity. If the widened create list ever admits
601
+ * one, "actor equals owner" is trivially false and must NOT be read as
602
+ * "nobody else involved, carry on".
603
+ */
604
+ stubs.teamMemberFind.mockResolvedValue(null as never);
605
+
606
+ await expect(
607
+ hooks().onBeforeCreate(
608
+ createBy({
609
+ userId: VICTIM_USER_ID,
610
+ props: { tenantId: PROJECT_ID },
611
+ channels: { userEmailId: METHOD_ID },
612
+ }),
613
+ ),
614
+ ).rejects.toThrow("are not a member of this project");
615
+ });
616
+
617
+ test("a rule with neither an owner nor an actor is refused rather than written unowned", async () => {
618
+ await expect(
619
+ hooks().onBeforeCreate(
620
+ createBy({
621
+ props: { tenantId: PROJECT_ID },
622
+ channels: { userEmailId: METHOD_ID },
623
+ }),
624
+ ),
625
+ ).rejects.toThrow("A notification rule must belong to a user.");
626
+ });
627
+
628
+ test("root writes skip both guards - internal seeding builds both halves from one userId", async () => {
629
+ await hooks().onBeforeCreate(
630
+ createBy({
631
+ userId: VICTIM_USER_ID,
632
+ props: { isRoot: true },
633
+ channels: { userEmailId: METHOD_ID },
634
+ }),
635
+ );
636
+
637
+ expect(stubs.teamMemberFind).not.toHaveBeenCalled();
638
+ expect(stubs.methodFinds.get("userEmailId")).not.toHaveBeenCalled();
639
+ });
640
+ });
641
+
642
+ /*
643
+ * ---------------------------------------------------------------------
644
+ * R3 on create - the method a rule names must belong to the rule's owner.
645
+ * ---------------------------------------------------------------------
646
+ */
647
+ describe("R3 on create - method ownership, all seven channels", () => {
648
+ test.each(
649
+ CHANNELS.map((channel: ChannelFixture): [string, ChannelFixture] => {
650
+ return [channel.label, channel];
651
+ }),
652
+ )(
653
+ "%s: a rule for the victim may not point at a method owned by the admin",
654
+ async (_label: string, channel: ChannelFixture) => {
655
+ /*
656
+ * THE hijack. userId names the victim so the victim's pages select the
657
+ * row; the method FK names something the admin controls so the pages
658
+ * arrive there instead.
659
+ */
660
+ stubs.methodFinds
661
+ .get(channel.idColumn)!
662
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
663
+
664
+ await expect(
665
+ hooks().onBeforeCreate(
666
+ createBy({
667
+ userId: VICTIM_USER_ID,
668
+ channels: { [channel.idColumn]: METHOD_ID },
669
+ }),
670
+ ),
671
+ ).rejects.toThrow(
672
+ `The ${channel.label} notification method referenced by this rule belongs to a different user.`,
673
+ );
674
+ },
675
+ );
676
+
677
+ test.each(
678
+ CHANNELS.map((channel: ChannelFixture): [string, ChannelFixture] => {
679
+ return [channel.label, channel];
680
+ }),
681
+ )(
682
+ "%s: the same rule is accepted when the method belongs to the rule's owner",
683
+ async (_label: string, channel: ChannelFixture) => {
684
+ await expect(
685
+ hooks().onBeforeCreate(
686
+ createBy({
687
+ userId: VICTIM_USER_ID,
688
+ channels: { [channel.idColumn]: METHOD_ID },
689
+ }),
690
+ ),
691
+ ).resolves.toBeDefined();
692
+
693
+ expect(stubs.methodFinds.get(channel.idColumn)).toHaveBeenCalled();
694
+ },
695
+ );
696
+
697
+ test.each(
698
+ CHANNELS.map((channel: ChannelFixture): [string, ChannelFixture] => {
699
+ return [channel.label, channel];
700
+ }),
701
+ )(
702
+ "%s: the relation slot is the same write spelled differently and is caught too",
703
+ async (_label: string, channel: ChannelFixture) => {
704
+ stubs.methodFinds
705
+ .get(channel.idColumn)!
706
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
707
+
708
+ await expect(
709
+ hooks().onBeforeCreate(
710
+ createBy({
711
+ userId: VICTIM_USER_ID,
712
+ channels: {
713
+ [channel.relationColumn]: { _id: METHOD_ID.toString() },
714
+ },
715
+ }),
716
+ ),
717
+ ).rejects.toThrow(
718
+ `The ${channel.label} notification method referenced by this rule belongs to a different user.`,
719
+ );
720
+ },
721
+ );
722
+
723
+ test("the guard covers exactly the seven notification method columns the model has", () => {
724
+ /*
725
+ * The count is the assertion. An eighth channel added to the model
726
+ * without an entry in the guard's table is a channel with no ownership
727
+ * check at all, and the attacker only needs one.
728
+ */
729
+ const covered: Array<string> =
730
+ UserNotificationRuleAdminService.getNotificationMethodIdColumns();
731
+
732
+ expect(covered.sort()).toEqual(
733
+ CHANNELS.map((channel: ChannelFixture): string => {
734
+ return channel.idColumn;
735
+ }).sort(),
736
+ );
737
+ });
738
+
739
+ test("a method row that does not exist is refused, not ignored", async () => {
740
+ /*
741
+ * "Not found" and "not yours" are the same answer: in both cases the
742
+ * caller named a row they have no business naming. A guard that compares
743
+ * `undefined` against `undefined` opens on exactly the input it exists to
744
+ * reject.
745
+ */
746
+ stubs.methodFinds.get("userEmailId")!.mockResolvedValue(null as never);
747
+
748
+ await expect(
749
+ hooks().onBeforeCreate(
750
+ createBy({
751
+ userId: VICTIM_USER_ID,
752
+ channels: { userEmailId: METHOD_ID },
753
+ }),
754
+ ),
755
+ ).rejects.toThrow(
756
+ "The Email notification method referenced by this rule does not exist.",
757
+ );
758
+ });
759
+
760
+ test("a method row with no owner at all is refused", async () => {
761
+ stubs.methodFinds
762
+ .get("userEmailId")!
763
+ .mockResolvedValue({ id: METHOD_ID } as never);
764
+
765
+ await expect(
766
+ hooks().onBeforeCreate(
767
+ createBy({
768
+ userId: VICTIM_USER_ID,
769
+ channels: { userEmailId: METHOD_ID },
770
+ }),
771
+ ),
772
+ ).rejects.toThrow(
773
+ "The Email notification method referenced by this rule does not exist.",
774
+ );
775
+ });
776
+
777
+ test("the mirror hijack is refused too: my own rule may not point at your address", async () => {
778
+ /*
779
+ * userId = me, userEmailId = yours. This does not steal my pages, it
780
+ * COPIES them to you - and it needed no widened permission, so it was
781
+ * writable long before this phase. The guard runs on self-writes for
782
+ * exactly this reason.
783
+ */
784
+ stubs.methodFinds
785
+ .get("userEmailId")!
786
+ .mockResolvedValue(methodOwnedBy(VICTIM_USER_ID) as never);
787
+
788
+ await expect(
789
+ hooks().onBeforeCreate(
790
+ createBy({
791
+ userId: ADMIN_USER_ID,
792
+ channels: { userEmailId: METHOD_ID },
793
+ }),
794
+ ),
795
+ ).rejects.toThrow(
796
+ "The Email notification method referenced by this rule belongs to a different user.",
797
+ );
798
+ });
799
+
800
+ test("the method's owner is read with root props, so the caller's own scope cannot shape the answer", async () => {
801
+ await hooks().onBeforeCreate(
802
+ createBy({
803
+ userId: VICTIM_USER_ID,
804
+ channels: { userEmailId: METHOD_ID },
805
+ }),
806
+ );
807
+
808
+ const call: { id: ObjectID; props: { isRoot: boolean } } =
809
+ stubs.methodFinds.get("userEmailId")!.mock.calls[0]![0] as {
810
+ id: ObjectID;
811
+ props: { isRoot: boolean };
812
+ };
813
+
814
+ expect(call.id.toString()).toBe(METHOD_ID.toString());
815
+ expect(call.props.isRoot).toBe(true);
816
+ });
817
+
818
+ test("a rule naming several methods is refused when any one of them is foreign", async () => {
819
+ stubs.methodFinds
820
+ .get("userWebhookId")!
821
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
822
+
823
+ await expect(
824
+ hooks().onBeforeCreate(
825
+ createBy({
826
+ userId: VICTIM_USER_ID,
827
+ channels: {
828
+ userEmailId: METHOD_ID,
829
+ userWebhookId: METHOD_ID,
830
+ },
831
+ }),
832
+ ),
833
+ ).rejects.toThrow("Webhook notification method");
834
+ });
835
+ });
836
+
837
+ /*
838
+ * ---------------------------------------------------------------------
839
+ * R3 on update - the owner is re-read from the database, never believed.
840
+ * ---------------------------------------------------------------------
841
+ */
842
+ describe("R3 on update - the rule's owner comes from the database", () => {
843
+ test("a body that claims the rule is the caller's own does not stop the guard", async () => {
844
+ /*
845
+ * The highest-value assertion in this file. The attacker sends
846
+ * `{ userId: <me>, userWebhookId: <my webhook> }` against a row that
847
+ * actually belongs to the victim. Believing the body would make the two
848
+ * halves agree and the guard would pass; re-reading the row makes them
849
+ * disagree and it fails.
850
+ */
851
+ stubs.ruleFind.mockResolvedValue([
852
+ persistedRule({ userId: VICTIM_USER_ID }),
853
+ ] as never);
854
+ stubs.methodFinds
855
+ .get("userWebhookId")!
856
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
857
+
858
+ await expect(
859
+ hooks().onBeforeUpdate({
860
+ query: { _id: RULE_ID.toString() },
861
+ data: {
862
+ userId: ADMIN_USER_ID,
863
+ userWebhookId: METHOD_ID,
864
+ },
865
+ limit: 1,
866
+ skip: 0,
867
+ props: memberProps(),
868
+ } as unknown as UpdateBy<UserNotificationRule>),
869
+ ).rejects.toThrow(
870
+ "The Webhook notification method referenced by this rule belongs to a different user.",
871
+ );
872
+ });
873
+
874
+ test.each(
875
+ CHANNELS.map((channel: ChannelFixture): [string, ChannelFixture] => {
876
+ return [channel.label, channel];
877
+ }),
878
+ )(
879
+ "%s: repointing a victim's rule at the admin's own method is refused",
880
+ async (_label: string, channel: ChannelFixture) => {
881
+ stubs.methodFinds
882
+ .get(channel.idColumn)!
883
+ .mockResolvedValue(methodOwnedBy(ADMIN_USER_ID) as never);
884
+
885
+ await expect(
886
+ hooks().onBeforeUpdate({
887
+ query: { _id: RULE_ID.toString() },
888
+ data: { [channel.idColumn]: METHOD_ID },
889
+ limit: 1,
890
+ skip: 0,
891
+ props: memberProps(),
892
+ } as unknown as UpdateBy<UserNotificationRule>),
893
+ ).rejects.toThrow(
894
+ `The ${channel.label} notification method referenced by this rule belongs to a different user.`,
895
+ );
896
+ },
897
+ );
898
+
899
+ test("repointing at a method the rule's owner actually owns is allowed - admins must be able to repair", async () => {
900
+ await expect(
901
+ hooks().onBeforeUpdate({
902
+ query: { _id: RULE_ID.toString() },
903
+ data: { userEmailId: METHOD_ID },
904
+ limit: 1,
905
+ skip: 0,
906
+ props: memberProps(),
907
+ } as unknown as UpdateBy<UserNotificationRule>),
908
+ ).resolves.toBeDefined();
909
+ });
910
+
911
+ test("the affected rows are read with root props, so a caller who cannot see a row cannot edit it unchecked", async () => {
912
+ await hooks().onBeforeUpdate({
913
+ query: { _id: RULE_ID.toString() },
914
+ data: { userEmailId: METHOD_ID },
915
+ limit: 1,
916
+ skip: 0,
917
+ props: memberProps(),
918
+ } as unknown as UpdateBy<UserNotificationRule>);
919
+
920
+ const call: { props: { isRoot: boolean }; select: { userId: boolean } } =
921
+ stubs.ruleFind.mock.calls[0]![0] as {
922
+ props: { isRoot: boolean };
923
+ select: { userId: boolean };
924
+ };
925
+
926
+ expect(call.props.isRoot).toBe(true);
927
+ expect(call.select.userId).toBe(true);
928
+ });
929
+
930
+ test("every distinct owner in a multi-row update is validated, not just the first", async () => {
931
+ /*
932
+ * One request, two rules, two different owners, one method. The method
933
+ * can belong to at most one of them, so the update must be refused - and
934
+ * it is only refused if the loop keeps going past the row that passed.
935
+ */
936
+ stubs.ruleFind.mockResolvedValue([
937
+ persistedRule({ userId: VICTIM_USER_ID }),
938
+ persistedRule({ userId: OUTSIDER_USER_ID }),
939
+ ] as never);
940
+
941
+ await expect(
942
+ hooks().onBeforeUpdate({
943
+ query: { ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT },
944
+ data: { userEmailId: METHOD_ID },
945
+ limit: 10,
946
+ skip: 0,
947
+ props: memberProps(),
948
+ } as unknown as UpdateBy<UserNotificationRule>),
949
+ ).rejects.toThrow("belongs to a different user");
950
+ });
951
+
952
+ test("an update naming no method at all skips the ownership lookups entirely", async () => {
953
+ await hooks().onBeforeUpdate({
954
+ query: { _id: RULE_ID.toString() },
955
+ data: { notifyAfterMinutes: 5 },
956
+ limit: 1,
957
+ skip: 0,
958
+ props: memberProps(),
959
+ } as unknown as UpdateBy<UserNotificationRule>);
960
+
961
+ for (const channel of CHANNELS) {
962
+ expect(stubs.methodFinds.get(channel.idColumn)).not.toHaveBeenCalled();
963
+ }
964
+ });
965
+ });
966
+
967
+ /*
968
+ * ---------------------------------------------------------------------
969
+ * R6 - audit and notify, keyed on the server's actor vs the persisted owner.
970
+ * ---------------------------------------------------------------------
971
+ */
972
+ describe("R6 - audit trail and owner notification", () => {
973
+ test("creating a rule for somebody else audits the actor against the PERSISTED owner", async () => {
974
+ const onCreate: OnCreate<UserNotificationRule> = {
975
+ createBy: createBy({ userId: VICTIM_USER_ID }),
976
+ carryForward: null,
977
+ };
978
+
979
+ await hooks().onCreateSuccess(
980
+ onCreate,
981
+ persistedRule({ userId: VICTIM_USER_ID }),
982
+ );
983
+
984
+ expect(stubs.auditCreate).toHaveBeenCalledTimes(1);
985
+
986
+ const logged: string = (
987
+ logger.info as unknown as jest.SpyInstance
988
+ ).mock.calls
989
+ .flat()
990
+ .map((value: unknown): string => {
991
+ return JSON.stringify(value);
992
+ })
993
+ .join(" ");
994
+
995
+ expect(logged).toContain(VICTIM_USER_ID.toString());
996
+ expect(logged).toContain(ADMIN_USER_ID.toString());
997
+ });
998
+
999
+ test("the owner compared against is the row that was written, not the one the body asked for", async () => {
1000
+ /*
1001
+ * The body claims the rule is the admin's own; the row that was actually
1002
+ * persisted belongs to the victim. Reading the body would conclude "self
1003
+ * write, nothing to report" and the victim would never hear that their
1004
+ * paging changed - which is precisely the notification an attacker most
1005
+ * wants suppressed.
1006
+ */
1007
+ const onCreate: OnCreate<UserNotificationRule> = {
1008
+ createBy: createBy({ userId: ADMIN_USER_ID }),
1009
+ carryForward: null,
1010
+ };
1011
+
1012
+ await hooks().onCreateSuccess(
1013
+ onCreate,
1014
+ persistedRule({ userId: VICTIM_USER_ID }),
1015
+ );
1016
+
1017
+ await flushAsync();
1018
+
1019
+ expect(stubs.auditCreate).toHaveBeenCalledTimes(1);
1020
+ expect(stubs.mail).toHaveBeenCalledTimes(1);
1021
+ });
1022
+
1023
+ test("the owner is emailed, and the mail names the actor by address", async () => {
1024
+ await hooks().onCreateSuccess(
1025
+ {
1026
+ createBy: createBy({ userId: VICTIM_USER_ID }),
1027
+ carryForward: null,
1028
+ },
1029
+ persistedRule({ userId: VICTIM_USER_ID }),
1030
+ );
1031
+
1032
+ await flushAsync();
1033
+
1034
+ expect(stubs.mail).toHaveBeenCalledTimes(1);
1035
+
1036
+ const mailArg: { toEmail: Email; vars: { message: string } } = stubs.mail
1037
+ .mock.calls[0]![0] as { toEmail: Email; vars: { message: string } };
1038
+
1039
+ expect(mailArg.toEmail.toString()).toBe("vic@example.com");
1040
+ /*
1041
+ * Display names are user-editable and not unique, so "Ada changed your
1042
+ * rules" is not something the reader can act on. An address is.
1043
+ */
1044
+ expect(mailArg.vars.message).toContain("ada@example.com");
1045
+ });
1046
+
1047
+ test("a self-write is neither audited nor announced", async () => {
1048
+ await hooks().onCreateSuccess(
1049
+ {
1050
+ createBy: createBy({ userId: ADMIN_USER_ID }),
1051
+ carryForward: null,
1052
+ },
1053
+ persistedRule({ userId: ADMIN_USER_ID }),
1054
+ );
1055
+
1056
+ await flushAsync();
1057
+
1058
+ expect(stubs.auditCreate).not.toHaveBeenCalled();
1059
+ expect(stubs.mail).not.toHaveBeenCalled();
1060
+ });
1061
+
1062
+ test("a failing mail cannot break the write - the row is already committed", async () => {
1063
+ stubs.mail.mockRejectedValue(new Error("smtp is down") as never);
1064
+
1065
+ await expect(
1066
+ hooks().onCreateSuccess(
1067
+ {
1068
+ createBy: createBy({ userId: VICTIM_USER_ID }),
1069
+ carryForward: null,
1070
+ },
1071
+ persistedRule({ userId: VICTIM_USER_ID }),
1072
+ ),
1073
+ ).resolves.toBeDefined();
1074
+
1075
+ await flushAsync();
1076
+ });
1077
+
1078
+ test("a failing audit sink cannot break the write either", async () => {
1079
+ stubs.auditCreate.mockRejectedValue(
1080
+ new Error("clickhouse is down") as never,
1081
+ );
1082
+
1083
+ await expect(
1084
+ hooks().onCreateSuccess(
1085
+ {
1086
+ createBy: createBy({ userId: VICTIM_USER_ID }),
1087
+ carryForward: null,
1088
+ },
1089
+ persistedRule({ userId: VICTIM_USER_ID }),
1090
+ ),
1091
+ ).resolves.toBeDefined();
1092
+ });
1093
+
1094
+ test("updating somebody else's rule audits it against the owner as the DATABASE had it", async () => {
1095
+ const before: UserNotificationRule = persistedRule({
1096
+ userId: VICTIM_USER_ID,
1097
+ });
1098
+
1099
+ await hooks().onUpdateSuccess(
1100
+ {
1101
+ updateBy: {
1102
+ query: { _id: RULE_ID.toString() },
1103
+ data: { userId: ADMIN_USER_ID, notifyAfterMinutes: 5 },
1104
+ limit: 1,
1105
+ skip: 0,
1106
+ props: memberProps(),
1107
+ },
1108
+ carryForward: { affectedRules: [before] },
1109
+ } as unknown as OnUpdate<UserNotificationRule>,
1110
+ [RULE_ID],
1111
+ );
1112
+
1113
+ expect(stubs.auditUpdate).toHaveBeenCalledTimes(1);
1114
+
1115
+ const auditArg: { before: UserNotificationRule; itemId: ObjectID } = stubs
1116
+ .auditUpdate.mock.calls[0]![0] as {
1117
+ before: UserNotificationRule;
1118
+ itemId: ObjectID;
1119
+ };
1120
+
1121
+ expect(auditArg.before.userId?.toString()).toBe(
1122
+ VICTIM_USER_ID.toString(),
1123
+ );
1124
+ expect(auditArg.itemId.toString()).toBe(RULE_ID.toString());
1125
+ });
1126
+
1127
+ test("one request touching many of one person's rules audits each but mails them once", async () => {
1128
+ /*
1129
+ * Twenty copies of "an admin changed your rules" is how a warning becomes
1130
+ * a filter rule. The audit stays per row because that is what an
1131
+ * investigator reconstructs from.
1132
+ */
1133
+ await hooks().onUpdateSuccess(
1134
+ {
1135
+ updateBy: {
1136
+ query: { userId: VICTIM_USER_ID },
1137
+ data: { notifyAfterMinutes: 5 },
1138
+ limit: 10,
1139
+ skip: 0,
1140
+ props: memberProps(),
1141
+ },
1142
+ carryForward: {
1143
+ affectedRules: [
1144
+ persistedRule({ userId: VICTIM_USER_ID }),
1145
+ persistedRule({ userId: VICTIM_USER_ID }),
1146
+ persistedRule({ userId: VICTIM_USER_ID }),
1147
+ ],
1148
+ },
1149
+ } as unknown as OnUpdate<UserNotificationRule>,
1150
+ [RULE_ID],
1151
+ );
1152
+
1153
+ await flushAsync();
1154
+
1155
+ expect(stubs.auditUpdate).toHaveBeenCalledTimes(3);
1156
+ expect(stubs.mail).toHaveBeenCalledTimes(1);
1157
+ });
1158
+
1159
+ test("two owners in one update each get their own notification", async () => {
1160
+ await hooks().onUpdateSuccess(
1161
+ {
1162
+ updateBy: {
1163
+ query: {},
1164
+ data: { notifyAfterMinutes: 5 },
1165
+ limit: 10,
1166
+ skip: 0,
1167
+ props: memberProps(),
1168
+ },
1169
+ carryForward: {
1170
+ affectedRules: [
1171
+ persistedRule({ userId: VICTIM_USER_ID }),
1172
+ persistedRule({ userId: OUTSIDER_USER_ID }),
1173
+ ],
1174
+ },
1175
+ } as unknown as OnUpdate<UserNotificationRule>,
1176
+ [RULE_ID],
1177
+ );
1178
+
1179
+ await flushAsync();
1180
+
1181
+ expect(stubs.mail).toHaveBeenCalledTimes(2);
1182
+ });
1183
+
1184
+ test("a rule the actor owns is skipped even when other rules in the same update are not", async () => {
1185
+ await hooks().onUpdateSuccess(
1186
+ {
1187
+ updateBy: {
1188
+ query: {},
1189
+ data: { notifyAfterMinutes: 5 },
1190
+ limit: 10,
1191
+ skip: 0,
1192
+ props: memberProps(),
1193
+ },
1194
+ carryForward: {
1195
+ affectedRules: [
1196
+ persistedRule({ userId: ADMIN_USER_ID }),
1197
+ persistedRule({ userId: VICTIM_USER_ID }),
1198
+ ],
1199
+ },
1200
+ } as unknown as OnUpdate<UserNotificationRule>,
1201
+ [RULE_ID],
1202
+ );
1203
+
1204
+ await flushAsync();
1205
+
1206
+ expect(stubs.auditUpdate).toHaveBeenCalledTimes(1);
1207
+ expect(stubs.mail).toHaveBeenCalledTimes(1);
1208
+ });
1209
+
1210
+ test("an actor-less write (root, internal jobs) reports nothing", async () => {
1211
+ await hooks().onUpdateSuccess(
1212
+ {
1213
+ updateBy: {
1214
+ query: {},
1215
+ data: { notifyAfterMinutes: 5 },
1216
+ limit: 10,
1217
+ skip: 0,
1218
+ props: { isRoot: true },
1219
+ },
1220
+ carryForward: {
1221
+ affectedRules: [persistedRule({ userId: VICTIM_USER_ID })],
1222
+ },
1223
+ } as unknown as OnUpdate<UserNotificationRule>,
1224
+ [RULE_ID],
1225
+ );
1226
+
1227
+ await flushAsync();
1228
+
1229
+ expect(stubs.auditUpdate).not.toHaveBeenCalled();
1230
+ expect(stubs.mail).not.toHaveBeenCalled();
1231
+ });
1232
+ });
1233
+
1234
+ /*
1235
+ * ---------------------------------------------------------------------
1236
+ * Defence in depth - a hijacked row is inert at delivery time.
1237
+ * ---------------------------------------------------------------------
1238
+ */
1239
+ describe("delivery refuses a rule whose method belongs to somebody else", () => {
1240
+ test("the rule load asks every method relation for its owner", async () => {
1241
+ stubs.ruleFindOneById.mockResolvedValue(
1242
+ persistedRule({ userId: VICTIM_USER_ID }) as never,
1243
+ );
1244
+
1245
+ await UserNotificationRuleService.executeNotificationRuleItem(
1246
+ RULE_ID,
1247
+ executeOptions(),
1248
+ );
1249
+
1250
+ const select: Record<string, { userId?: boolean }> = (
1251
+ stubs.ruleFindOneById.mock.calls[0]![0] as {
1252
+ select: Record<string, { userId?: boolean }>;
1253
+ }
1254
+ ).select;
1255
+
1256
+ for (const channel of CHANNELS) {
1257
+ expect(select[channel.relationProperty]?.userId).toBe(true);
1258
+ }
1259
+ });
1260
+
1261
+ test.each(
1262
+ CHANNELS.map((channel: ChannelFixture): [string, ChannelFixture] => {
1263
+ return [channel.label, channel];
1264
+ }),
1265
+ )(
1266
+ "%s: a rule whose method belongs to another user sends nothing",
1267
+ async (_label: string, channel: ChannelFixture) => {
1268
+ stubs.ruleFindOneById.mockResolvedValue(
1269
+ persistedRule({
1270
+ userId: VICTIM_USER_ID,
1271
+ [channel.relationProperty]: {
1272
+ id: METHOD_ID,
1273
+ userId: ADMIN_USER_ID,
1274
+ },
1275
+ } as Partial<UserNotificationRule>) as never,
1276
+ );
1277
+
1278
+ await UserNotificationRuleService.executeNotificationRuleItem(
1279
+ RULE_ID,
1280
+ executeOptions(),
1281
+ );
1282
+
1283
+ expect(stubs.deliver).not.toHaveBeenCalled();
1284
+
1285
+ expect(timelineRows).toHaveLength(1);
1286
+ expect(timelineRows[0]!.status).toBe(UserNotificationStatus.Error);
1287
+ expect(timelineRows[0]!.statusMessage).toContain(channel.label);
1288
+ expect(timelineRows[0]!.statusMessage).toContain(
1289
+ "belongs to a different user",
1290
+ );
1291
+ /*
1292
+ * The timeline row is stamped with the RULE's owner - the person whose
1293
+ * page was refused - so it appears on the log they will be looking at.
1294
+ */
1295
+ expect(timelineRows[0]!.userId?.toString()).toBe(
1296
+ VICTIM_USER_ID.toString(),
1297
+ );
1298
+ },
1299
+ );
1300
+
1301
+ test("one foreign method poisons the whole rule, including its honest channels", async () => {
1302
+ /*
1303
+ * A row with a foreign FK on it is not a row with one bad field; it is a
1304
+ * row somebody wrote to redirect a page. Delivering its other channels
1305
+ * would let it keep working well enough to escape notice.
1306
+ */
1307
+ stubs.ruleFindOneById.mockResolvedValue(
1308
+ persistedRule({
1309
+ userId: VICTIM_USER_ID,
1310
+ userEmail: { id: METHOD_ID, userId: VICTIM_USER_ID },
1311
+ userWebhook: { id: METHOD_ID, userId: ADMIN_USER_ID },
1312
+ } as unknown as Partial<UserNotificationRule>) as never,
1313
+ );
1314
+
1315
+ await UserNotificationRuleService.executeNotificationRuleItem(
1316
+ RULE_ID,
1317
+ executeOptions(),
1318
+ );
1319
+
1320
+ expect(stubs.deliver).not.toHaveBeenCalled();
1321
+ expect(timelineRows[0]!.statusMessage).toContain("Webhook");
1322
+ });
1323
+
1324
+ test("a rule whose methods all belong to its owner is delivered normally", async () => {
1325
+ stubs.ruleFindOneById.mockResolvedValue(
1326
+ persistedRule({
1327
+ userId: VICTIM_USER_ID,
1328
+ userEmail: { id: METHOD_ID, userId: VICTIM_USER_ID },
1329
+ } as unknown as Partial<UserNotificationRule>) as never,
1330
+ );
1331
+
1332
+ await UserNotificationRuleService.executeNotificationRuleItem(
1333
+ RULE_ID,
1334
+ executeOptions(),
1335
+ );
1336
+
1337
+ expect(stubs.deliver).toHaveBeenCalledTimes(1);
1338
+ expect(timelineRows).toHaveLength(0);
1339
+ });
1340
+
1341
+ test("an unloaded owner column is NOT read as a mismatch - unknown must never drop a page", async () => {
1342
+ /*
1343
+ * The single most dangerous way to get this check wrong. `undefined`
1344
+ * means the column was not selected, not that it disagrees; treating the
1345
+ * two the same would turn a security guard into a page-dropping machine
1346
+ * on every caller that does not select userId - the exact failure this
1347
+ * whole epic exists to eliminate.
1348
+ */
1349
+ stubs.ruleFindOneById.mockResolvedValue(
1350
+ persistedRule({
1351
+ userId: VICTIM_USER_ID,
1352
+ userEmail: { id: METHOD_ID },
1353
+ } as unknown as Partial<UserNotificationRule>) as never,
1354
+ );
1355
+
1356
+ await UserNotificationRuleService.executeNotificationRuleItem(
1357
+ RULE_ID,
1358
+ executeOptions(),
1359
+ );
1360
+
1361
+ expect(stubs.deliver).toHaveBeenCalledTimes(1);
1362
+ expect(timelineRows).toHaveLength(0);
1363
+ });
1364
+
1365
+ test("a rule with no owner at all is still delivered - there is nothing to compare it against", async () => {
1366
+ stubs.ruleFindOneById.mockResolvedValue(
1367
+ persistedRule({
1368
+ userId: undefined,
1369
+ userEmail: { id: METHOD_ID, userId: ADMIN_USER_ID },
1370
+ } as unknown as Partial<UserNotificationRule>) as never,
1371
+ );
1372
+
1373
+ await UserNotificationRuleService.executeNotificationRuleItem(
1374
+ RULE_ID,
1375
+ executeOptions(),
1376
+ );
1377
+
1378
+ expect(stubs.deliver).toHaveBeenCalledTimes(1);
1379
+ });
1380
+
1381
+ test("the claim is still taken first, and a missing rule still throws", async () => {
1382
+ stubs.ruleFindOneById.mockResolvedValue(null as never);
1383
+
1384
+ await expect(
1385
+ UserNotificationRuleService.executeNotificationRuleItem(
1386
+ RULE_ID,
1387
+ executeOptions(),
1388
+ ),
1389
+ ).rejects.toThrow(Exception);
1390
+
1391
+ expect(stubs.claim).toHaveBeenCalledTimes(1);
1392
+ });
1393
+ });
1394
+ });