@oneuptime/common 12.0.6 → 12.0.7

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 (424) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +1 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
  3. package/Models/DatabaseModels/DeletedProject.ts +595 -0
  4. package/Models/DatabaseModels/Index.ts +2 -0
  5. package/Models/DatabaseModels/Monitor.ts +111 -0
  6. package/Models/DatabaseModels/Project.ts +3 -3
  7. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
  8. package/Server/API/FileAPI.ts +15 -2
  9. package/Server/API/MicrosoftTeamsAPI.ts +6 -1
  10. package/Server/API/ProjectAPI.ts +214 -0
  11. package/Server/API/StatusPageAPI.ts +39 -6
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIAgentService.ts +20 -0
  17. package/Server/Services/DashboardDomainService.ts +73 -28
  18. package/Server/Services/DatabaseService.ts +1 -0
  19. package/Server/Services/DeletedProjectService.ts +138 -0
  20. package/Server/Services/FileService.ts +61 -0
  21. package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
  22. package/Server/Services/IncidentPublicNoteService.ts +18 -0
  23. package/Server/Services/Index.ts +2 -0
  24. package/Server/Services/MonitorService.ts +311 -0
  25. package/Server/Services/ProbeService.ts +20 -0
  26. package/Server/Services/ProjectService.ts +297 -1
  27. package/Server/Services/PushNotificationService.ts +71 -0
  28. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
  29. package/Server/Services/StatusPageAnnouncementService.ts +53 -0
  30. package/Server/Services/StatusPageDomainService.ts +73 -28
  31. package/Server/Services/TeamMemberService.ts +117 -12
  32. package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
  33. package/Server/Services/UserWebhookService.ts +34 -109
  34. package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
  35. package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
  36. package/Server/Types/Database/DeleteById.ts +2 -0
  37. package/Server/Types/Database/DeleteOneBy.ts +6 -0
  38. package/Server/Types/Domain.ts +41 -0
  39. package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
  40. package/Server/Types/Workflow/Components/API/Get.ts +2 -1
  41. package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
  42. package/Server/Types/Workflow/Components/API/Post.ts +2 -1
  43. package/Server/Types/Workflow/Components/API/Put.ts +2 -1
  44. package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
  45. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
  46. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
  47. package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
  48. package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
  49. package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
  50. package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
  51. package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
  52. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
  53. package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
  54. package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
  55. package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
  56. package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
  57. package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
  58. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
  59. package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
  60. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  61. package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
  62. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
  63. package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
  64. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
  65. package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
  66. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
  67. package/Server/Utils/DataSource/EgressGuard.ts +176 -16
  68. package/Server/Utils/DataSource/HttpFetch.ts +6 -45
  69. package/Server/Utils/Execute.ts +8 -0
  70. package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
  71. package/Server/Utils/LLM/LLMService.ts +57 -12
  72. package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
  73. package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
  74. package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
  75. package/Server/Utils/Monitor/MonitorResource.ts +36 -0
  76. package/Server/Utils/Response.ts +142 -6
  77. package/Server/Utils/SSRFProtection.ts +273 -60
  78. package/Server/Utils/StartServer.ts +8 -0
  79. package/Server/Utils/VM/VMRunner.ts +88 -1
  80. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
  81. package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
  82. package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
  83. package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
  84. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
  85. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
  86. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
  87. package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
  88. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
  89. package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
  90. package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
  91. package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
  92. package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
  93. package/Tests/Server/API/ProjectAPI.test.ts +299 -1
  94. package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
  95. package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
  96. package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
  97. package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
  98. package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
  99. package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
  100. package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
  101. package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
  102. package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
  103. package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
  104. package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
  105. package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
  106. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
  107. package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
  108. package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
  109. package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
  110. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
  111. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
  112. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
  113. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
  114. package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
  115. package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
  116. package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
  117. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
  118. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
  119. package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
  120. package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
  121. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
  122. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
  123. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
  124. package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
  125. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
  126. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
  127. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
  128. package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
  129. package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
  130. package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
  131. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
  132. package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
  133. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
  134. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
  139. package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
  140. package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
  141. package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
  142. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
  143. package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
  144. package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
  145. package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
  146. package/Tests/Types/JSONFunctions.test.ts +252 -1
  147. package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
  148. package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
  149. package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
  150. package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
  151. package/Tests/UI/Components/404.test.tsx +1 -1
  152. package/Tests/UI/Components/Alert.test.tsx +1 -1
  153. package/Tests/UI/Components/Badge.test.tsx +1 -1
  154. package/Tests/UI/Components/BasicForm.test.tsx +39 -10
  155. package/Tests/UI/Components/Button.test.tsx +1 -1
  156. package/Tests/UI/Components/Card.test.tsx +1 -1
  157. package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
  158. package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
  159. package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
  160. package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
  161. package/Tests/UI/Components/Dropdown.test.tsx +1 -1
  162. package/Tests/UI/Components/FilePicker.test.tsx +1 -1
  163. package/Tests/UI/Components/HiddenText.test.tsx +1 -1
  164. package/Tests/UI/Components/Input.test.tsx +1 -1
  165. package/Tests/UI/Components/Loader.test.tsx +1 -1
  166. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
  167. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  168. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
  169. package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
  170. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
  171. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
  172. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
  173. package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
  174. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
  175. package/Tests/UI/Components/NavBar.test.tsx +1 -1
  176. package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
  177. package/Tests/UI/Components/Pagination.test.tsx +1 -1
  178. package/Tests/UI/Components/Pill.test.tsx +1 -1
  179. package/Tests/UI/Components/Probe.test.tsx +1 -1
  180. package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
  181. package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
  182. package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
  183. package/Tests/UI/Components/SideOver.test.tsx +500 -6
  184. package/Tests/UI/Components/Tabs.test.tsx +1 -1
  185. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
  186. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
  187. package/Tests/UI/Components/TextArea.test.tsx +1 -1
  188. package/Tests/UI/Components/Toast.test.tsx +1 -1
  189. package/Tests/UI/Components/Toggle.test.tsx +1 -1
  190. package/Tests/UI/Components/TopSection.test.tsx +1 -1
  191. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
  192. package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
  193. package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
  194. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
  195. package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
  196. package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
  197. package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
  198. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
  199. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
  200. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
  201. package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
  202. package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
  203. package/Tests/Utils/Realtime.test.ts +78 -0
  204. package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
  205. package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
  206. package/Types/AI/FixPullRequestCiStatus.ts +1 -1
  207. package/Types/AI/FixVerificationStatus.ts +2 -2
  208. package/Types/API/Hostname.ts +92 -4
  209. package/Types/API/URL.ts +43 -25
  210. package/Types/Billing/BalanceAdjustmentType.ts +14 -0
  211. package/Types/Billing/ProjectBalanceType.ts +13 -0
  212. package/Types/Domain.ts +26 -0
  213. package/Types/File/MimeType.ts +11 -0
  214. package/Types/JSONFunctions.ts +15 -0
  215. package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
  216. package/Types/Workflow/Components/BaseModel.ts +92 -28
  217. package/Typings/Index.d.ts +12 -0
  218. package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
  219. package/UI/Components/Modal/ConfirmModal.tsx +10 -5
  220. package/UI/Components/Modal/Modal.tsx +27 -47
  221. package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
  222. package/UI/Components/SideOver/SideOver.tsx +195 -68
  223. package/UI/Utils/PageScrollLock.ts +66 -0
  224. package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
  225. package/Utils/Dashboard/VariableInterpolation.ts +19 -1
  226. package/Utils/LegacySerializedArray.ts +60 -0
  227. package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
  228. package/Utils/Telemetry/SavedViewFilters.ts +88 -0
  229. package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
  230. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  231. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
  232. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
  233. package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
  234. package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
  235. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  236. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  237. package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
  238. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  239. package/build/dist/Models/DatabaseModels/Project.js +3 -3
  240. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  241. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  242. package/build/dist/Server/API/FileAPI.js +14 -2
  243. package/build/dist/Server/API/FileAPI.js.map +1 -1
  244. package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
  245. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  246. package/build/dist/Server/API/ProjectAPI.js +157 -4
  247. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  248. package/build/dist/Server/API/StatusPageAPI.js +27 -3
  249. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
  256. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  257. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  258. package/build/dist/Server/Services/AIAgentService.js +17 -0
  259. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  260. package/build/dist/Server/Services/DashboardDomainService.js +55 -18
  261. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +1 -0
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/DeletedProjectService.js +124 -0
  265. package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
  266. package/build/dist/Server/Services/FileService.js +52 -0
  267. package/build/dist/Server/Services/FileService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
  269. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  270. package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
  271. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  272. package/build/dist/Server/Services/Index.js +2 -0
  273. package/build/dist/Server/Services/Index.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorService.js +232 -0
  275. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  276. package/build/dist/Server/Services/ProbeService.js +17 -0
  277. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  278. package/build/dist/Server/Services/ProjectService.js +228 -6
  279. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  280. package/build/dist/Server/Services/PushNotificationService.js +56 -0
  281. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  282. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
  283. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  284. package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
  285. package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
  286. package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
  287. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  288. package/build/dist/Server/Services/TeamMemberService.js +97 -6
  289. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  290. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
  291. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  292. package/build/dist/Server/Services/UserWebhookService.js +31 -87
  293. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  294. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
  295. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  296. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
  297. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  298. package/build/dist/Server/Types/Domain.js +37 -0
  299. package/build/dist/Server/Types/Domain.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
  301. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
  303. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
  305. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  306. package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
  307. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  308. package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
  309. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  310. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
  311. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  312. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
  313. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  314. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
  315. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  316. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
  317. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
  318. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
  319. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
  320. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
  321. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
  322. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
  323. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
  324. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
  325. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
  326. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
  327. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
  328. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
  329. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
  330. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
  331. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
  332. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
  333. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
  334. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
  335. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
  336. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
  337. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
  338. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
  339. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  340. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
  341. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
  342. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  343. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
  344. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
  345. package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
  346. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
  347. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
  348. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
  349. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
  350. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  351. package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
  352. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  353. package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
  354. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  355. package/build/dist/Server/Utils/Execute.js +2 -2
  356. package/build/dist/Server/Utils/Execute.js.map +1 -1
  357. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
  358. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
  359. package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
  360. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  361. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
  362. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  363. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
  364. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
  365. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
  366. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  367. package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
  368. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  369. package/build/dist/Server/Utils/Response.js +112 -5
  370. package/build/dist/Server/Utils/Response.js.map +1 -1
  371. package/build/dist/Server/Utils/SSRFProtection.js +219 -53
  372. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  373. package/build/dist/Server/Utils/StartServer.js +7 -0
  374. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  375. package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
  376. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  377. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
  378. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  379. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
  380. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  381. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  382. package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
  383. package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
  384. package/build/dist/Types/API/Hostname.js +73 -4
  385. package/build/dist/Types/API/Hostname.js.map +1 -1
  386. package/build/dist/Types/API/URL.js +42 -24
  387. package/build/dist/Types/API/URL.js.map +1 -1
  388. package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
  389. package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
  390. package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
  391. package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
  392. package/build/dist/Types/Domain.js +22 -0
  393. package/build/dist/Types/Domain.js.map +1 -1
  394. package/build/dist/Types/File/MimeType.js +11 -0
  395. package/build/dist/Types/File/MimeType.js.map +1 -1
  396. package/build/dist/Types/JSONFunctions.js +14 -0
  397. package/build/dist/Types/JSONFunctions.js.map +1 -1
  398. package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
  399. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  400. package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
  401. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
  402. package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
  403. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  404. package/build/dist/UI/Components/Modal/Modal.js +24 -34
  405. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  406. package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
  407. package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
  408. package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
  409. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  410. package/build/dist/UI/Utils/PageScrollLock.js +52 -0
  411. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
  412. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
  413. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
  414. package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
  415. package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
  416. package/build/dist/Utils/LegacySerializedArray.js +51 -0
  417. package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
  418. package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
  419. package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
  420. package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
  421. package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
  422. package/jest.config.json +3 -0
  423. package/package.json +1 -1
  424. package/tsconfig.json +3 -1
@@ -1,18 +1,17 @@
1
1
  import CreateBy from "../Types/Database/CreateBy";
2
2
  import DeleteBy from "../Types/Database/DeleteBy";
3
- import { OnCreate, OnDelete } from "../Types/Database/Hooks";
3
+ import UpdateBy from "../Types/Database/UpdateBy";
4
+ import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
4
5
  import GreenlockUtil from "../Utils/Greenlock/Greenlock";
5
6
  import logger, { LogAttributes } from "../Utils/Logger";
6
7
  import DatabaseService from "./DatabaseService";
7
8
  import DomainService from "./DomainService";
8
9
  import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
9
10
  import HTTPResponse from "../../Types/API/HTTPResponse";
10
- import URL from "../../Types/API/URL";
11
11
  import LIMIT_MAX from "../../Types/Database/LimitMax";
12
12
  import BadDataException from "../../Types/Exception/BadDataException";
13
13
  import { JSONObject } from "../../Types/JSON";
14
14
  import ObjectID from "../../Types/ObjectID";
15
- import API from "../../Utils/API";
16
15
  import AcmeCertificate from "../../Models/DatabaseModels/AcmeCertificate";
17
16
  import DomainModel from "../../Models/DatabaseModels/Domain";
18
17
  import StatusPageDomain from "../../Models/DatabaseModels/StatusPageDomain";
@@ -22,11 +21,60 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
22
21
  import { StatusPageCNameRecord } from "../EnvironmentConfig";
23
22
  import Domain from "../Types/Domain";
24
23
 
24
+ const STATUS_PAGE_DOMAIN_EGRESS_LABEL: string = "Status page domain";
25
+
25
26
  export class Service extends DatabaseService<StatusPageDomain> {
26
27
  public constructor() {
27
28
  super(StatusPageDomain);
28
29
  }
29
30
 
31
+ /*
32
+ * Normalize a submitted subdomain and reject anything that is not a plain
33
+ * DNS label chain.
34
+ *
35
+ * fullDomain is built as `${subdomain}.${baseDomain}` and then interpolated
36
+ * into "https://" + fullDomain + "/status-page-api/..." — and URL parsing
37
+ * treats everything before the first "/" as the host. Without this check a
38
+ * subdomain of "169.254.169.254/latest/meta-data/#" hands an attacker the
39
+ * host, the port and the path of a request the certificate cron then makes
40
+ * unattended, every 15 minutes, from inside the cluster.
41
+ */
42
+ private static normalizeAndValidateSubdomain(
43
+ subdomain: string | undefined,
44
+ ): string {
45
+ const normalized: string = subdomain?.trim().toLowerCase() || "";
46
+
47
+ // "@" is the documented way to ask for the root domain.
48
+ if (normalized === "" || normalized === "@") {
49
+ return "";
50
+ }
51
+
52
+ if (!Domain.isValidSubdomain(normalized)) {
53
+ throw new BadDataException(
54
+ `Subdomain ${normalized} is not valid. Use a plain subdomain such as "status" — schemes, ports, paths and "/" are not allowed.`,
55
+ );
56
+ }
57
+
58
+ return normalized;
59
+ }
60
+
61
+ @CaptureSpan()
62
+ protected override async onBeforeUpdate(
63
+ updateBy: UpdateBy<StatusPageDomain>,
64
+ ): Promise<OnUpdate<StatusPageDomain>> {
65
+ /*
66
+ * Create-time validation alone leaves the value editable afterwards —
67
+ * subdomain is ProjectMember-updatable.
68
+ */
69
+ if (updateBy.data.subdomain !== undefined) {
70
+ updateBy.data.subdomain = Service.normalizeAndValidateSubdomain(
71
+ updateBy.data.subdomain as string,
72
+ );
73
+ }
74
+
75
+ return { updateBy, carryForward: null };
76
+ }
77
+
30
78
  @CaptureSpan()
31
79
  protected override async onBeforeCreate(
32
80
  createBy: CreateBy<StatusPageDomain>,
@@ -48,14 +96,11 @@ export class Service extends DatabaseService<StatusPageDomain> {
48
96
  );
49
97
  }
50
98
 
51
- let normalizedSubdomain: string =
52
- createBy.data.subdomain?.trim().toLowerCase() || "";
53
-
54
- if (normalizedSubdomain === "@") {
55
- normalizedSubdomain = "";
56
- }
99
+ createBy.data.subdomain = Service.normalizeAndValidateSubdomain(
100
+ createBy.data.subdomain,
101
+ );
57
102
 
58
- createBy.data.subdomain = normalizedSubdomain;
103
+ const normalizedSubdomain: string = createBy.data.subdomain;
59
104
 
60
105
  if (domain) {
61
106
  const baseDomain: string =
@@ -231,13 +276,13 @@ export class Service extends DatabaseService<StatusPageDomain> {
231
276
  ): Promise<boolean> {
232
277
  try {
233
278
  const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
234
- await API.get({
235
- url: URL.fromString(
279
+ await Domain.getForDomainVerification({
280
+ url:
236
281
  "https://" +
237
- fulldomain +
238
- "/status-page-api/cname-verification/" +
239
- token,
240
- ),
282
+ fulldomain +
283
+ "/status-page-api/cname-verification/" +
284
+ token,
285
+ targetLabel: STATUS_PAGE_DOMAIN_EGRESS_LABEL,
241
286
  });
242
287
 
243
288
  if (result.isFailure()) {
@@ -320,13 +365,13 @@ export class Service extends DatabaseService<StatusPageDomain> {
320
365
 
321
366
  try {
322
367
  const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
323
- await API.get({
324
- url: URL.fromString(
368
+ await Domain.getForDomainVerification({
369
+ url:
325
370
  "http://" +
326
- fullDomain +
327
- "/status-page-api/cname-verification/" +
328
- token,
329
- ),
371
+ fullDomain +
372
+ "/status-page-api/cname-verification/" +
373
+ token,
374
+ targetLabel: STATUS_PAGE_DOMAIN_EGRESS_LABEL,
330
375
  });
331
376
 
332
377
  logger.debug("CNAME verification result", {
@@ -353,13 +398,13 @@ export class Service extends DatabaseService<StatusPageDomain> {
353
398
 
354
399
  try {
355
400
  const resultHttps: HTTPErrorResponse | HTTPResponse<JSONObject> =
356
- await API.get({
357
- url: URL.fromString(
401
+ await Domain.getForDomainVerification({
402
+ url:
358
403
  "https://" +
359
- fullDomain +
360
- "/status-page-api/cname-verification/" +
361
- token,
362
- ),
404
+ fullDomain +
405
+ "/status-page-api/cname-verification/" +
406
+ token,
407
+ targetLabel: STATUS_PAGE_DOMAIN_EGRESS_LABEL,
363
408
  });
364
409
 
365
410
  logger.debug("CNAME verification result for https", {
@@ -120,10 +120,40 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
120
120
  }
121
121
  }
122
122
 
123
- if (!createBy.props.isRoot) {
123
+ /*
124
+ * Only internal writes (isRoot) and a master admin acting from the Admin
125
+ * Dashboard may create a membership that is already accepted - that is the
126
+ * "accept the invitation automatically" checkbox on the admin invite forms.
127
+ *
128
+ * Everyone else invites, and the invited person accepts for themselves. A
129
+ * project admin who could accept on someone's behalf would be able to pull
130
+ * an account into their project - and into whatever the team's permissions
131
+ * grant - without that person ever agreeing to it.
132
+ */
133
+ const canCreateAcceptedInvitation: boolean = Boolean(
134
+ createBy.props.isRoot || createBy.props.isMasterAdmin,
135
+ );
136
+
137
+ if (!canCreateAcceptedInvitation) {
124
138
  createBy.data.hasAcceptedInvitation = false;
125
139
  }
126
140
 
141
+ const isInvitationAcceptedOnCreate: boolean = Boolean(
142
+ createBy.data.hasAcceptedInvitation,
143
+ );
144
+
145
+ /*
146
+ * The acceptance timestamp is stamped here rather than taken from the
147
+ * request, so it can never disagree with hasAcceptedInvitation - a row that
148
+ * says "Member" with no accepted-at date, or an accepted-at date on a row
149
+ * that is still only invited.
150
+ */
151
+ if (isInvitationAcceptedOnCreate) {
152
+ createBy.data.invitationAcceptedAt = OneUptimeDate.getCurrentDate();
153
+ } else {
154
+ delete createBy.data.invitationAcceptedAt;
155
+ }
156
+
127
157
  if (createBy.miscDataProps && createBy.miscDataProps["email"]) {
128
158
  const email: Email = new Email(createBy.miscDataProps["email"] as string);
129
159
 
@@ -221,10 +251,18 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
221
251
  .addQueryParam("email", email.toString(), true)
222
252
  .toString(),
223
253
  isNewUser: isNewUser.toString(),
254
+ /*
255
+ * An auto-accepted member has nothing left to accept, so the
256
+ * template drops the "sign in to accept your invitation" framing
257
+ * and tells them they are already in.
258
+ */
259
+ isInvitationAccepted: isInvitationAcceptedOnCreate.toString(),
224
260
  projectName: project.name!,
225
261
  homeUrl: new URL(httpProtocol, host).toString(),
226
262
  },
227
- subject: "You have been invited to " + project.name,
263
+ subject: isInvitationAcceptedOnCreate
264
+ ? "You have been added to " + project.name
265
+ : "You have been invited to " + project.name,
228
266
  },
229
267
  {
230
268
  projectId: createBy.data.projectId!,
@@ -283,6 +321,65 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
283
321
  );
284
322
  }
285
323
 
324
+ /*
325
+ * The per-project notification defaults a member gets the moment their
326
+ * membership becomes accepted, whether that happened by them accepting the
327
+ * invitation or by a master admin accepting it for them on create. Without
328
+ * these, an auto-accepted member is a member who is never notified about
329
+ * anything.
330
+ *
331
+ * Skipped for an unverified email: UserService adds the defaults for every
332
+ * accepted membership once the address is verified.
333
+ *
334
+ * Best effort. Both helpers below are idempotent, and by the time either
335
+ * caller runs, the membership row is already committed - failing the write
336
+ * that created it would report "invite failed" for a member who exists.
337
+ */
338
+ @CaptureSpan()
339
+ private async addDefaultNotificationSettingsAndRules(data: {
340
+ userId: ObjectID;
341
+ projectId: ObjectID;
342
+ user?: User | undefined;
343
+ }): Promise<void> {
344
+ try {
345
+ const user: User | null =
346
+ data.user ||
347
+ (await UserService.findOneById({
348
+ id: data.userId,
349
+ select: {
350
+ email: true,
351
+ isEmailVerified: true,
352
+ },
353
+ props: {
354
+ isRoot: true,
355
+ },
356
+ }));
357
+
358
+ if (!user || !user.isEmailVerified || !user.email) {
359
+ return;
360
+ }
361
+
362
+ await UserNotificationSettingService.addDefaultNotificationSettingsForUser(
363
+ data.userId,
364
+ data.projectId,
365
+ );
366
+
367
+ await UserNotificationRuleService.addDefaultNotificationRuleForUser(
368
+ data.projectId,
369
+ data.userId,
370
+ user.email,
371
+ );
372
+ } catch (err) {
373
+ logger.error(
374
+ err as Error,
375
+ {
376
+ projectId: data.projectId.toString(),
377
+ userId: data.userId.toString(),
378
+ } as LogAttributes,
379
+ );
380
+ }
381
+ }
382
+
286
383
  @CaptureSpan()
287
384
  protected override async onCreateSuccess(
288
385
  onCreate: OnCreate<TeamMember>,
@@ -297,6 +394,18 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
297
394
  onCreate.createBy.data.projectId!,
298
395
  );
299
396
 
397
+ /*
398
+ * A membership created already accepted never goes through the
399
+ * accept-invitation update, so it would otherwise miss the defaults that
400
+ * hook adds.
401
+ */
402
+ if (createdItem.hasAcceptedInvitation) {
403
+ await this.addDefaultNotificationSettingsAndRules({
404
+ userId: onCreate.createBy.data.userId!,
405
+ projectId: onCreate.createBy.data.projectId!,
406
+ });
407
+ }
408
+
300
409
  // Activation event for marketing funnels, attributed to the inviter.
301
410
  ProductAnalytics.captureForUser({
302
411
  userId: onCreate.createBy.props.userId,
@@ -339,16 +448,12 @@ export class TeamMemberService extends DatabaseService<TeamMember> {
339
448
  for (const item of items) {
340
449
  await this.refreshTokens(item.userId!, item.projectId!);
341
450
 
342
- if (updateBy.data.hasAcceptedInvitation && item.user?.isEmailVerified) {
343
- await UserNotificationSettingService.addDefaultNotificationSettingsForUser(
344
- item.userId!,
345
- item.projectId!,
346
- );
347
- await UserNotificationRuleService.addDefaultNotificationRuleForUser(
348
- item.projectId!,
349
- item.userId!,
350
- item.user?.email as Email,
351
- );
451
+ if (updateBy.data.hasAcceptedInvitation) {
452
+ await this.addDefaultNotificationSettingsAndRules({
453
+ userId: item.userId!,
454
+ projectId: item.projectId!,
455
+ user: item.user,
456
+ });
352
457
  }
353
458
  }
354
459
 
@@ -85,6 +85,7 @@ export class Service extends DatabaseService<Model> {
85
85
 
86
86
  let secretKeyObjectId: ObjectID;
87
87
  try {
88
+ ObjectID.validateUUID(secretKey);
88
89
  secretKeyObjectId = new ObjectID(secretKey);
89
90
  } catch {
90
91
  this.projectIdCache.set(cacheKey, null, NEGATIVE_TTL_MS);
@@ -1,6 +1,7 @@
1
1
  import CreateBy from "../Types/Database/CreateBy";
2
2
  import DeleteBy from "../Types/Database/DeleteBy";
3
- import { OnCreate, OnDelete } from "../Types/Database/Hooks";
3
+ import UpdateBy from "../Types/Database/UpdateBy";
4
+ import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
4
5
  import DatabaseService from "./DatabaseService";
5
6
  import UserNotificationRuleService from "./UserNotificationRuleService";
6
7
  import LIMIT_MAX from "../../Types/Database/LimitMax";
@@ -8,8 +9,19 @@ import BadDataException from "../../Types/Exception/BadDataException";
8
9
  import Model from "../../Models/DatabaseModels/UserWebhook";
9
10
  import URL from "../../Types/API/URL";
10
11
  import logger from "../Utils/Logger";
12
+ import SSRFProtection from "../Utils/SSRFProtection";
11
13
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
12
14
 
15
+ /*
16
+ * URL validation here used to be a hand-rolled copy of the SSRF blocklist that
17
+ * had drifted weaker than the original (it compared the host with the port
18
+ * still attached, and never resolved DNS). Everything that actually sends one
19
+ * of these webhooks goes through WebhookService, which calls
20
+ * SSRFProtection.validateWebhookTargetIsSafe — so the copy bought nothing and
21
+ * would have been read as "this is checked". Both hooks now call the same
22
+ * guard the sender does, which fails the write early instead of at delivery.
23
+ */
24
+
13
25
  export class Service extends DatabaseService<Model> {
14
26
  public constructor() {
15
27
  super(Model);
@@ -27,7 +39,7 @@ export class Service extends DatabaseService<Model> {
27
39
  throw new BadDataException("Webhook name is required");
28
40
  }
29
41
 
30
- this.validateWebhookUrl(createBy.data.webhookUrl);
42
+ await SSRFProtection.validateWebhookTargetIsSafe(createBy.data.webhookUrl);
31
43
 
32
44
  return {
33
45
  createBy,
@@ -35,6 +47,26 @@ export class Service extends DatabaseService<Model> {
35
47
  };
36
48
  }
37
49
 
50
+ @CaptureSpan()
51
+ protected override async onBeforeUpdate(
52
+ updateBy: UpdateBy<Model>,
53
+ ): Promise<OnUpdate<Model>> {
54
+ /*
55
+ * Create-time validation on its own is not a control: the URL is
56
+ * editable afterwards.
57
+ */
58
+ const webhookUrl: unknown = updateBy.data.webhookUrl;
59
+
60
+ if (typeof webhookUrl === "string" || webhookUrl instanceof URL) {
61
+ await SSRFProtection.validateWebhookTargetIsSafe(webhookUrl);
62
+ }
63
+
64
+ return {
65
+ updateBy,
66
+ carryForward: null,
67
+ };
68
+ }
69
+
38
70
  @CaptureSpan()
39
71
  protected override async onCreateSuccess(
40
72
  _onCreate: OnCreate<Model>,
@@ -96,113 +128,6 @@ export class Service extends DatabaseService<Model> {
96
128
  carryForward: null,
97
129
  };
98
130
  }
99
-
100
- private validateWebhookUrl(rawUrl: string): void {
101
- let parsed: URL;
102
- try {
103
- parsed = URL.fromString(rawUrl);
104
- } catch {
105
- throw new BadDataException("Webhook URL is not a valid URL");
106
- }
107
-
108
- const protocolValue: string = parsed.protocol.toString().toLowerCase();
109
- if (protocolValue !== "http://" && protocolValue !== "https://") {
110
- throw new BadDataException(
111
- "Webhook URL must use http or https protocol.",
112
- );
113
- }
114
-
115
- const hostname: string = parsed.hostname.hostname.toLowerCase();
116
-
117
- if (!hostname) {
118
- throw new BadDataException("Webhook URL must include a host.");
119
- }
120
-
121
- if (isBlockedHostnameLiteral(hostname)) {
122
- throw new BadDataException(
123
- "Webhook URL points to a private, loopback, or link-local address and is not allowed.",
124
- );
125
- }
126
- }
127
- }
128
-
129
- function isBlockedHostnameLiteral(hostname: string): boolean {
130
- if (
131
- hostname === "localhost" ||
132
- hostname.endsWith(".localhost") ||
133
- hostname === "metadata.google.internal"
134
- ) {
135
- return true;
136
- }
137
-
138
- // IPv4 literal check
139
- const ipv4Match: RegExpMatchArray | null = hostname.match(
140
- /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/,
141
- );
142
- if (ipv4Match) {
143
- const octets: Array<number> = [
144
- Number(ipv4Match[1]),
145
- Number(ipv4Match[2]),
146
- Number(ipv4Match[3]),
147
- Number(ipv4Match[4]),
148
- ];
149
-
150
- if (
151
- octets.some((o: number) => {
152
- return o < 0 || o > 255;
153
- })
154
- ) {
155
- return true;
156
- }
157
-
158
- // 0.0.0.0/8
159
- if (octets[0] === 0) {
160
- return true;
161
- }
162
- // 127.0.0.0/8 loopback
163
- if (octets[0] === 127) {
164
- return true;
165
- }
166
- // 10.0.0.0/8
167
- if (octets[0] === 10) {
168
- return true;
169
- }
170
- // 172.16.0.0/12
171
- if (octets[0] === 172 && (octets[1]! & 0xf0) === 16) {
172
- return true;
173
- }
174
- // 192.168.0.0/16
175
- if (octets[0] === 192 && octets[1] === 168) {
176
- return true;
177
- }
178
- // 169.254.0.0/16 link-local (incl. cloud metadata)
179
- if (octets[0] === 169 && octets[1] === 254) {
180
- return true;
181
- }
182
- // 100.64.0.0/10 carrier-grade NAT
183
- if (octets[0] === 100 && (octets[1]! & 0xc0) === 64) {
184
- return true;
185
- }
186
- return false;
187
- }
188
-
189
- // IPv6 literal — block loopback, link-local, unique-local
190
- if (hostname.includes(":")) {
191
- const stripped: string = hostname.replace(/^\[|\]$/g, "");
192
- if (stripped === "::1" || stripped === "::") {
193
- return true;
194
- }
195
- if (stripped.startsWith("fe80:") || stripped.startsWith("fe80::")) {
196
- return true;
197
- }
198
- if (IPV6_UNIQUE_LOCAL_REGEX.test(stripped)) {
199
- return true;
200
- }
201
- }
202
-
203
- return false;
204
131
  }
205
132
 
206
- const IPV6_UNIQUE_LOCAL_REGEX: RegExp = /^f[cd][0-9a-f]{2}:/;
207
-
208
133
  export default new Service();
@@ -309,6 +309,20 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
309
309
 
310
310
  // Check for errors in the response
311
311
  if (res.errors && res.errors.length > 0) {
312
+ /*
313
+ * Record the failure before throwing. Test sends used to throw
314
+ * straight out of here, so a failed test left no trace in
315
+ * Notification Logs and support had no way to see what Microsoft
316
+ * or Slack actually said.
317
+ */
318
+ await this.logTestSendFailures({
319
+ projectId: data.projectId,
320
+ workspaceType: res.workspaceType,
321
+ errors: res.errors,
322
+ message: messageSummary,
323
+ userId: data.testByUserId,
324
+ });
325
+
312
326
  const errorMessages: Array<string> = res.errors.map(
313
327
  (error: { channel: WorkspaceChannel; error: string }) => {
314
328
  return `Channel ${error.channel.name}: ${error.error}`;
@@ -408,6 +422,20 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
408
422
 
409
423
  // Check for errors in the response
410
424
  if (res.errors && res.errors.length > 0) {
425
+ /*
426
+ * Record the failure before throwing. Test sends used to throw
427
+ * straight out of here, so a failed test left no trace in
428
+ * Notification Logs and support had no way to see what Microsoft
429
+ * or Slack actually said.
430
+ */
431
+ await this.logTestSendFailures({
432
+ projectId: data.projectId,
433
+ workspaceType: res.workspaceType,
434
+ errors: res.errors,
435
+ message: messageSummary,
436
+ userId: data.testByUserId,
437
+ });
438
+
411
439
  const errorMessages: Array<string> = res.errors.map(
412
440
  (error: { channel: WorkspaceChannel; error: string }) => {
413
441
  return `Channel ${error.channel.name}: ${error.error}`;
@@ -540,6 +568,47 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
540
568
  }
541
569
  }
542
570
 
571
+ /*
572
+ * Persist per-destination failures from a test send so they show up in
573
+ * Settings > Notification Logs alongside real notification failures. Real
574
+ * sends already log; test sends threw before reaching any logging, which
575
+ * meant the one action an admin takes to diagnose a broken integration was
576
+ * also the only one that recorded nothing.
577
+ *
578
+ * Best-effort: a logging failure must never mask the underlying send error.
579
+ */
580
+ @CaptureSpan()
581
+ private async logTestSendFailures(data: {
582
+ projectId: ObjectID;
583
+ workspaceType: WorkspaceType;
584
+ errors: Array<{ channel: WorkspaceChannel; error: string }>;
585
+ message: string;
586
+ userId: ObjectID;
587
+ }): Promise<void> {
588
+ for (const error of data.errors) {
589
+ try {
590
+ const log: WorkspaceNotificationLog = new WorkspaceNotificationLog();
591
+ log.projectId = data.projectId;
592
+ log.workspaceType = data.workspaceType;
593
+ log.channelId = error.channel.id;
594
+ log.channelName = error.channel.name;
595
+ log.message = data.message;
596
+ log.status = WorkspaceNotificationStatus.Error;
597
+ log.statusMessage = error.error;
598
+ log.userId = data.userId;
599
+ log.actionType = WorkspaceNotificationActionType.SendMessage;
600
+
601
+ await WorkspaceNotificationLogService.create({
602
+ data: log,
603
+ props: { isRoot: true },
604
+ });
605
+ } catch (err) {
606
+ logger.error("Could not write test-send failure to notification log:");
607
+ logger.error(err);
608
+ }
609
+ }
610
+ }
611
+
543
612
  @CaptureSpan()
544
613
  public async archiveWorkspaceChannels(data: {
545
614
  projectId: ObjectID;
@@ -1,5 +1,7 @@
1
1
  import ObjectID from "../../Types/ObjectID";
2
- import WorkspaceType from "../../Types/Workspace/WorkspaceType";
2
+ import WorkspaceType, {
3
+ getWorkspaceTypeDisplayName,
4
+ } from "../../Types/Workspace/WorkspaceType";
3
5
  import DatabaseService from "./DatabaseService";
4
6
  import Model, {
5
7
  WorkspaceMiscData,
@@ -7,6 +9,7 @@ import Model, {
7
9
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
8
10
  import BadDataException from "../../Types/Exception/BadDataException";
9
11
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
12
+ import logger from "../Utils/Logger";
10
13
 
11
14
  export class Service extends DatabaseService<Model> {
12
15
  public constructor() {
@@ -112,12 +115,45 @@ export class Service extends DatabaseService<Model> {
112
115
  },
113
116
  select: {
114
117
  _id: true,
118
+ workspaceProjectId: true,
115
119
  },
116
120
  props: {
117
121
  isRoot: true,
118
122
  },
119
123
  });
120
124
 
125
+ /*
126
+ * A project stores exactly one workspace (one Microsoft tenant / one Slack
127
+ * team). Re-running the connect flow from a DIFFERENT workspace used to
128
+ * silently repoint the project, which breaks every existing notification
129
+ * rule and orphans the bot conversations captured under the old workspace —
130
+ * with no error and nothing in the logs.
131
+ *
132
+ * Refuse instead, and tell the admin to disconnect first if the change is
133
+ * deliberate.
134
+ */
135
+ if (
136
+ projectAuth &&
137
+ projectAuth.workspaceProjectId &&
138
+ projectAuth.workspaceProjectId !== data.workspaceProjectId
139
+ ) {
140
+ logger.error(
141
+ `Refusing to repoint ${data.workspaceType} for project ${data.projectId.toString()} from workspace ${projectAuth.workspaceProjectId} to ${data.workspaceProjectId}.`,
142
+ {
143
+ projectId: data.projectId.toString(),
144
+ workspaceType: data.workspaceType,
145
+ },
146
+ );
147
+
148
+ const workspaceName: string = getWorkspaceTypeDisplayName(
149
+ data.workspaceType,
150
+ );
151
+
152
+ throw new BadDataException(
153
+ `This OneUptime project is already connected to a different ${workspaceName} workspace. Disconnect the existing ${workspaceName} connection in Project Settings before connecting a new one.`,
154
+ );
155
+ }
156
+
121
157
  if (!projectAuth) {
122
158
  projectAuth = new Model();
123
159
 
@@ -5,5 +5,7 @@ import User from "../../../Models/DatabaseModels/User";
5
5
  export default interface DeleteById {
6
6
  id: ObjectID;
7
7
  deletedByUser?: User;
8
+ // See DeleteOneBy.deletionReason - passed straight through to the hooks.
9
+ deletionReason?: string | undefined;
8
10
  props: DatabaseCommonInteractionProps;
9
11
  }
@@ -6,5 +6,11 @@ import User from "../../../Models/DatabaseModels/User";
6
6
  export default interface DeleteOneBy<TBaseModel extends BaseModel> {
7
7
  query: Query<TBaseModel>;
8
8
  deletedByUser?: User | undefined;
9
+ /*
10
+ * Free-text reason the caller gave for the delete. Never persisted on the
11
+ * row being deleted (it is about to be gone) - it rides through to the
12
+ * delete hooks, which are the last place that can record it anywhere.
13
+ */
14
+ deletionReason?: string | undefined;
9
15
  props: DatabaseCommonInteractionProps;
10
16
  }