@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
@@ -0,0 +1,256 @@
1
+ import DiscordSendMessage from "../../../../../Server/Types/Workflow/Components/Discord/SendMessageToChannel";
2
+ import MicrosoftTeamsSendMessage from "../../../../../Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel";
3
+ import SlackSendMessage from "../../../../../Server/Types/Workflow/Components/Slack/SendMessageToChannel";
4
+ import ComponentCode, {
5
+ RunOptions,
6
+ } from "../../../../../Server/Types/Workflow/ComponentCode";
7
+ import HTTPResponse from "../../../../../Types/API/HTTPResponse";
8
+ import Exception from "../../../../../Types/Exception/Exception";
9
+ import { JSONObject } from "../../../../../Types/JSON";
10
+ import ObjectID from "../../../../../Types/ObjectID";
11
+ import API from "../../../../../Utils/API";
12
+ import { beforeEach, describe, expect, test } from "@jest/globals";
13
+
14
+ /*
15
+ * The Slack / Discord / Microsoft Teams workflow components take a free-text
16
+ * "webhook-url" argument and POST to it from the API server, with no check —
17
+ * the same SSRF GHSA-v5xh-rw9h-77fv reported against the API components, one
18
+ * component over. Because these are chat webhooks there is a real vendor host
19
+ * to pin to, which is stronger than a blocklist: a pin cannot be walked around
20
+ * with DNS rebinding, an alternate IP notation, or a redirect.
21
+ *
22
+ * Slack's and Teams' pins already existed but were only ever called from the
23
+ * status page subscriber path; Discord had none at all.
24
+ */
25
+
26
+ jest.mock("../../../../../Utils/API", () => {
27
+ return {
28
+ __esModule: true,
29
+ default: { post: jest.fn() },
30
+ };
31
+ });
32
+
33
+ /*
34
+ * Slack posts through SlackUtil rather than calling API.post itself. That
35
+ * module pulls in the workspace stack, so stub it down to the one method the
36
+ * component uses — the pin under test lives in the component and in the
37
+ * (separately tested) util, not in the transport.
38
+ */
39
+ jest.mock("../../../../../Server/Utils/Workspace/Slack/Slack", () => {
40
+ const actual: { default: { isValidSlackIncomingWebhookUrl: unknown } } =
41
+ jest.requireActual("../../../../../Server/Utils/Workspace/Slack/Slack");
42
+ return {
43
+ __esModule: true,
44
+ default: {
45
+ isValidSlackIncomingWebhookUrl:
46
+ actual.default.isValidSlackIncomingWebhookUrl,
47
+ sendMessageToChannelViaIncomingWebhook: jest.fn(),
48
+ },
49
+ };
50
+ });
51
+
52
+ import SlackUtil from "../../../../../Server/Utils/Workspace/Slack/Slack";
53
+
54
+ function makeOptions(): RunOptions {
55
+ return {
56
+ log: jest.fn() as RunOptions["log"],
57
+ workflowLogId: ObjectID.generate(),
58
+ workflowId: ObjectID.generate(),
59
+ projectId: ObjectID.generate(),
60
+ onError: ((exception: Exception): Exception => {
61
+ return exception;
62
+ }) as RunOptions["onError"],
63
+ executeWorkflow: async (): Promise<void> => {},
64
+ };
65
+ }
66
+
67
+ const apiPostMock: jest.Mock = API.post as unknown as jest.Mock;
68
+ const slackSendMock: jest.Mock =
69
+ SlackUtil.sendMessageToChannelViaIncomingWebhook as unknown as jest.Mock;
70
+
71
+ /*
72
+ * Every one of these is a place the request must not go. They cover the shapes
73
+ * the guard has to survive: raw internal literals, a URL whose PATH or QUERY
74
+ * mentions the vendor domain, a host that merely ends in something similar, and
75
+ * the "#"/"?" parser trick that made "https://169.254.169.254#.office.com" look
76
+ * like a Microsoft host to OneUptime's own URL parser while axios dialled the
77
+ * metadata endpoint.
78
+ */
79
+ function unsafeUrlsFor(vendorDomain: string): Array<[string, string]> {
80
+ return [
81
+ ["the cloud metadata endpoint", "http://169.254.169.254/latest/meta-data/"],
82
+ ["loopback", "http://127.0.0.1:8080/"],
83
+ ["localhost", "http://localhost/hook"],
84
+ ["an RFC-1918 address", "http://10.0.0.5/hook"],
85
+ [
86
+ "an internal host with the vendor domain in the query",
87
+ `http://169.254.169.254/?x=${vendorDomain}`,
88
+ ],
89
+ [
90
+ "an internal host with the vendor domain in a fragment",
91
+ `https://169.254.169.254#.${vendorDomain}`,
92
+ ],
93
+ [
94
+ "an internal host with the vendor domain in the path",
95
+ `http://10.0.0.5/${vendorDomain}/webhook`,
96
+ ],
97
+ ["a lookalike suffix domain", `https://evil-${vendorDomain}/webhook`],
98
+ ["a subdomain-of-attacker domain", `https://${vendorDomain}.evil.tld/hook`],
99
+ ["plain http on the real vendor host", `http://${vendorDomain}/webhook`],
100
+ ];
101
+ }
102
+
103
+ beforeEach(() => {
104
+ jest.clearAllMocks();
105
+ });
106
+
107
+ describe("Discord SendMessageToChannel — webhook URL pin", () => {
108
+ test.each(unsafeUrlsFor("discord.com"))(
109
+ "refuses %s and never sends",
110
+ async (_label: string, url: string) => {
111
+ await expect(
112
+ new DiscordSendMessage().run(
113
+ { text: "hi", "webhook-url": url },
114
+ makeOptions(),
115
+ ),
116
+ ).rejects.toThrow();
117
+ expect(apiPostMock).not.toHaveBeenCalled();
118
+ },
119
+ );
120
+
121
+ test("sends to a genuine Discord webhook, with redirects off", async () => {
122
+ apiPostMock.mockResolvedValue(new HTTPResponse<JSONObject>(200, {}, {}));
123
+
124
+ await new DiscordSendMessage().run(
125
+ {
126
+ text: "hi",
127
+ "webhook-url": "https://discord.com/api/webhooks/123/abcdef",
128
+ },
129
+ makeOptions(),
130
+ );
131
+
132
+ expect(apiPostMock).toHaveBeenCalledTimes(1);
133
+ const request: { options?: { doNotFollowRedirects?: boolean } } =
134
+ apiPostMock.mock.calls[0]![0] as {
135
+ options?: { doNotFollowRedirects?: boolean };
136
+ };
137
+ expect(request.options?.doNotFollowRedirects).toBe(true);
138
+ });
139
+
140
+ test("accepts the discordapp.com alias", async () => {
141
+ apiPostMock.mockResolvedValue(new HTTPResponse<JSONObject>(200, {}, {}));
142
+
143
+ await new DiscordSendMessage().run(
144
+ {
145
+ text: "hi",
146
+ "webhook-url": "https://discordapp.com/api/webhooks/123/abcdef",
147
+ },
148
+ makeOptions(),
149
+ );
150
+
151
+ expect(apiPostMock).toHaveBeenCalledTimes(1);
152
+ });
153
+ });
154
+
155
+ describe("Microsoft Teams SendMessageToChannel — webhook URL pin", () => {
156
+ test.each(unsafeUrlsFor("office.com"))(
157
+ "refuses %s and never sends",
158
+ async (_label: string, url: string) => {
159
+ await expect(
160
+ new MicrosoftTeamsSendMessage().run(
161
+ { text: "hi", "webhook-url": url },
162
+ makeOptions(),
163
+ ),
164
+ ).rejects.toThrow();
165
+ expect(apiPostMock).not.toHaveBeenCalled();
166
+ },
167
+ );
168
+
169
+ test("sends to a genuine Teams webhook, with redirects off", async () => {
170
+ apiPostMock.mockResolvedValue(new HTTPResponse<JSONObject>(200, {}, {}));
171
+
172
+ await new MicrosoftTeamsSendMessage().run(
173
+ {
174
+ text: "hi",
175
+ "webhook-url": "https://outlook.office.com/webhook/abc/IncomingWebhook",
176
+ },
177
+ makeOptions(),
178
+ );
179
+
180
+ expect(apiPostMock).toHaveBeenCalledTimes(1);
181
+ const request: { options?: { doNotFollowRedirects?: boolean } } =
182
+ apiPostMock.mock.calls[0]![0] as {
183
+ options?: { doNotFollowRedirects?: boolean };
184
+ };
185
+ expect(request.options?.doNotFollowRedirects).toBe(true);
186
+ });
187
+ });
188
+
189
+ describe("Slack SendMessageToChannel — webhook URL pin", () => {
190
+ test.each(unsafeUrlsFor("slack.com"))(
191
+ "refuses %s and never sends",
192
+ async (_label: string, url: string) => {
193
+ await expect(
194
+ new SlackSendMessage().run(
195
+ { text: "hi", "webhook-url": url },
196
+ makeOptions(),
197
+ ),
198
+ ).rejects.toThrow();
199
+ expect(slackSendMock).not.toHaveBeenCalled();
200
+ },
201
+ );
202
+
203
+ test("refuses a Slack host without the /services/ path prefix", async () => {
204
+ await expect(
205
+ new SlackSendMessage().run(
206
+ { text: "hi", "webhook-url": "https://hooks.slack.com/evil" },
207
+ makeOptions(),
208
+ ),
209
+ ).rejects.toThrow();
210
+ expect(slackSendMock).not.toHaveBeenCalled();
211
+ });
212
+
213
+ test("sends to a genuine Slack incoming webhook", async () => {
214
+ slackSendMock.mockResolvedValue(new HTTPResponse<JSONObject>(200, {}, {}));
215
+
216
+ await new SlackSendMessage().run(
217
+ {
218
+ text: "hi",
219
+ "webhook-url": "https://hooks.slack.com/services/T000/B000/XXXX",
220
+ },
221
+ makeOptions(),
222
+ );
223
+
224
+ expect(slackSendMock).toHaveBeenCalledTimes(1);
225
+ });
226
+ });
227
+
228
+ describe("every chat component rejects a missing URL", () => {
229
+ const components: Array<[string, () => ComponentCode]> = [
230
+ [
231
+ "Discord",
232
+ (): ComponentCode => {
233
+ return new DiscordSendMessage();
234
+ },
235
+ ],
236
+ [
237
+ "MicrosoftTeams",
238
+ (): ComponentCode => {
239
+ return new MicrosoftTeamsSendMessage();
240
+ },
241
+ ],
242
+ [
243
+ "Slack",
244
+ (): ComponentCode => {
245
+ return new SlackSendMessage();
246
+ },
247
+ ],
248
+ ];
249
+
250
+ test.each(components)(
251
+ "%s",
252
+ async (_label: string, make: () => ComponentCode) => {
253
+ await expect(make().run({ text: "hi" }, makeOptions())).rejects.toThrow();
254
+ },
255
+ );
256
+ });
@@ -0,0 +1,371 @@
1
+ import { buildColumnHint } from "../../../../../Server/Types/Workflow/Components/BaseModel/LogComponentError";
2
+ import {
3
+ ID_ARGUMENT_KEY,
4
+ PRIMARY_KEY_COLUMN,
5
+ applyTenantColumn,
6
+ describeModelColumns,
7
+ normalizeModelKeys,
8
+ } from "../../../../../Server/Types/Workflow/Components/BaseModel/ModelArguments";
9
+ import Monitor from "../../../../../Models/DatabaseModels/Monitor";
10
+ import Project from "../../../../../Models/DatabaseModels/Project";
11
+ import { JSONObject } from "../../../../../Types/JSON";
12
+ import ObjectID from "../../../../../Types/ObjectID";
13
+ import { afterEach, describe, expect, test } from "@jest/globals";
14
+
15
+ /*
16
+ * https://github.com/OneUptime/oneuptime/issues/3132 - a workflow that
17
+ * queried `{"id": "<uuid>"}` failed with
18
+ * `Property "id" was not found in "Monitor". Make sure your query is correct.`
19
+ *
20
+ * "id" is only a TypeScript accessor on the model; the persisted primary key
21
+ * column is "_id". The create path had always rewritten "id" to "_id"
22
+ * (DatabaseBaseModel._fromJSON), so these tests pin the rewrite that makes
23
+ * every other workflow argument agree with create.
24
+ */
25
+
26
+ const MONITOR_ID: string = "09dc4d63-2fc3-4757-a509-5f688ae38ffd";
27
+
28
+ describe("normalizeModelKeys", () => {
29
+ afterEach(() => {
30
+ jest.restoreAllMocks();
31
+ });
32
+
33
+ test("rewrites a top-level id to the _id column", () => {
34
+ const result: JSONObject = normalizeModelKeys(
35
+ { id: MONITOR_ID },
36
+ new Monitor(),
37
+ );
38
+
39
+ expect(result).toEqual({ _id: MONITOR_ID });
40
+ expect(result[ID_ARGUMENT_KEY]).toBeUndefined();
41
+ });
42
+
43
+ test("leaves an explicit _id alone", () => {
44
+ const result: JSONObject = normalizeModelKeys(
45
+ { _id: MONITOR_ID },
46
+ new Monitor(),
47
+ );
48
+
49
+ expect(result).toEqual({ _id: MONITOR_ID });
50
+ });
51
+
52
+ test("keeps _id and drops id when both are given, whatever the key order", () => {
53
+ const idFirst: JSONObject = normalizeModelKeys(
54
+ { id: "alias", _id: "explicit" },
55
+ new Monitor(),
56
+ );
57
+
58
+ const primaryKeyFirst: JSONObject = normalizeModelKeys(
59
+ { _id: "explicit", id: "alias" },
60
+ new Monitor(),
61
+ );
62
+
63
+ expect(idFirst).toEqual({ _id: "explicit" });
64
+ expect(primaryKeyFirst).toEqual({ _id: "explicit" });
65
+ });
66
+
67
+ test("leaves every other key untouched", () => {
68
+ const result: JSONObject = normalizeModelKeys(
69
+ {
70
+ id: MONITOR_ID,
71
+ monitorType: "Website",
72
+ isEnabled: true,
73
+ disableActiveMonitoringBecauseOfManualIncident: false,
74
+ description: null,
75
+ },
76
+ new Monitor(),
77
+ );
78
+
79
+ expect(result).toEqual({
80
+ _id: MONITOR_ID,
81
+ monitorType: "Website",
82
+ isEnabled: true,
83
+ disableActiveMonitoringBecauseOfManualIncident: false,
84
+ description: null,
85
+ });
86
+ });
87
+
88
+ test("passes unknown keys through rather than validating them", () => {
89
+ /*
90
+ * Rewriting must not become a whitelist: an argument that reaches the
91
+ * database today has to keep reaching it, right or wrong.
92
+ */
93
+ const result: JSONObject = normalizeModelKeys(
94
+ { notAColumnAtAll: "value" },
95
+ new Monitor(),
96
+ );
97
+
98
+ expect(result).toEqual({ notAColumnAtAll: "value" });
99
+ });
100
+
101
+ test("returns a new object and does not mutate the input", () => {
102
+ const input: JSONObject = { id: MONITOR_ID };
103
+ const result: JSONObject = normalizeModelKeys(input, new Monitor());
104
+
105
+ expect(result).not.toBe(input);
106
+ expect(input).toEqual({ id: MONITOR_ID });
107
+ });
108
+
109
+ test("handles an empty object", () => {
110
+ expect(normalizeModelKeys({}, new Monitor())).toEqual({});
111
+ });
112
+
113
+ test("rewrites id inside a related record on an Entity column", () => {
114
+ const result: JSONObject = normalizeModelKeys(
115
+ { project: { id: true, name: true } },
116
+ new Monitor(),
117
+ );
118
+
119
+ expect(result).toEqual({ project: { _id: true, name: true } });
120
+ });
121
+
122
+ test("rewrites id inside a related record on an EntityArray column", () => {
123
+ const result: JSONObject = normalizeModelKeys(
124
+ { labels: { id: true } },
125
+ new Monitor(),
126
+ );
127
+
128
+ expect(result).toEqual({ labels: { _id: true } });
129
+ });
130
+
131
+ test("rewrites the alias at both levels at once", () => {
132
+ const result: JSONObject = normalizeModelKeys(
133
+ { id: MONITOR_ID, currentMonitorStatus: { id: true } },
134
+ new Monitor(),
135
+ );
136
+
137
+ expect(result).toEqual({
138
+ _id: MONITOR_ID,
139
+ currentMonitorStatus: { _id: true },
140
+ });
141
+ });
142
+
143
+ test("does not descend into a JSON column, whose keys are not column names", () => {
144
+ /*
145
+ * customFields is a JSON blob. An "id" in there belongs to the customer's
146
+ * own data and renaming it would corrupt the value.
147
+ */
148
+ const result: JSONObject = normalizeModelKeys(
149
+ { customFields: { id: "belongs-to-the-customer" } },
150
+ new Monitor(),
151
+ );
152
+
153
+ expect(result).toEqual({
154
+ customFields: { id: "belongs-to-the-customer" },
155
+ });
156
+ });
157
+
158
+ test("does not descend into a serialized value", () => {
159
+ /*
160
+ * ObjectID, Date and every QueryHelper operator travel as
161
+ * { "_type": ..., "value": ... }. Those are values, not column maps.
162
+ */
163
+ const result: JSONObject = normalizeModelKeys(
164
+ {
165
+ project: { _type: "ObjectID", value: MONITOR_ID, id: "not-a-column" },
166
+ },
167
+ new Monitor(),
168
+ );
169
+
170
+ expect(result).toEqual({
171
+ project: { _type: "ObjectID", value: MONITOR_ID, id: "not-a-column" },
172
+ });
173
+ });
174
+
175
+ test("leaves array values alone", () => {
176
+ const result: JSONObject = normalizeModelKeys(
177
+ { _id: ["first", "second"], labels: [{ id: "untouched" }] },
178
+ new Monitor(),
179
+ );
180
+
181
+ expect(result).toEqual({
182
+ _id: ["first", "second"],
183
+ labels: [{ id: "untouched" }],
184
+ });
185
+ });
186
+
187
+ test("leaves a relation queried by a plain id string alone", () => {
188
+ const result: JSONObject = normalizeModelKeys(
189
+ { project: MONITOR_ID },
190
+ new Monitor(),
191
+ );
192
+
193
+ expect(result).toEqual({ project: MONITOR_ID });
194
+ });
195
+
196
+ test("works on any model, not just Monitor", () => {
197
+ const result: JSONObject = normalizeModelKeys(
198
+ { id: MONITOR_ID, name: true },
199
+ new Project(),
200
+ );
201
+
202
+ expect(result).toEqual({ _id: MONITOR_ID, name: true });
203
+ });
204
+
205
+ test("keeps id as-is on a model that owns a column called id", () => {
206
+ /*
207
+ * No model ships an "id" column today. One that grew one would mean
208
+ * something different by "id", so the alias has to stand down.
209
+ */
210
+ const model: Monitor = new Monitor();
211
+
212
+ jest
213
+ .spyOn(model, "hasColumn")
214
+ .mockImplementation((columnName: string): boolean => {
215
+ return columnName === ID_ARGUMENT_KEY || columnName === "_id";
216
+ });
217
+
218
+ expect(normalizeModelKeys({ id: MONITOR_ID }, model)).toEqual({
219
+ id: MONITOR_ID,
220
+ });
221
+ });
222
+
223
+ test("exports the key names it rewrites between", () => {
224
+ expect(ID_ARGUMENT_KEY).toBe("id");
225
+ expect(PRIMARY_KEY_COLUMN).toBe("_id");
226
+ });
227
+ });
228
+
229
+ describe("applyTenantColumn", () => {
230
+ const PROJECT_ID: ObjectID = ObjectID.generate();
231
+
232
+ test("stamps the scalar tenant column", () => {
233
+ const result: JSONObject = applyTenantColumn(
234
+ { isEnabled: true },
235
+ new Monitor(),
236
+ PROJECT_ID,
237
+ );
238
+
239
+ expect(result).toEqual({ isEnabled: true, projectId: PROJECT_ID });
240
+ });
241
+
242
+ test("overwrites a projectId the caller supplied", () => {
243
+ const result: JSONObject = applyTenantColumn(
244
+ { projectId: "some-other-project" },
245
+ new Monitor(),
246
+ PROJECT_ID,
247
+ );
248
+
249
+ expect(result["projectId"]).toBe(PROJECT_ID);
250
+ });
251
+
252
+ test("drops the relation that writes the same column as the tenant column", () => {
253
+ /*
254
+ * Monitor.project is @JoinColumn({name: "projectId"}), so TypeORM writes
255
+ * the tenant column from the relation property and ignores the scalar.
256
+ * Left in place, a payload naming another project moved the record there.
257
+ */
258
+ const result: JSONObject = applyTenantColumn(
259
+ { name: "keep me", project: "some-other-project" },
260
+ new Monitor(),
261
+ PROJECT_ID,
262
+ );
263
+
264
+ expect(result).toEqual({ name: "keep me", projectId: PROJECT_ID });
265
+ expect(result["project"]).toBeUndefined();
266
+ });
267
+
268
+ test("drops the relation whichever spelling it takes", () => {
269
+ const asObject: JSONObject = applyTenantColumn(
270
+ { project: { _id: "some-other-project" } },
271
+ new Monitor(),
272
+ PROJECT_ID,
273
+ );
274
+
275
+ const asAlias: JSONObject = applyTenantColumn(
276
+ normalizeModelKeys(
277
+ { project: { id: "some-other-project" } },
278
+ new Monitor(),
279
+ ),
280
+ new Monitor(),
281
+ PROJECT_ID,
282
+ );
283
+
284
+ expect(asObject).toEqual({ projectId: PROJECT_ID });
285
+ expect(asAlias).toEqual({ projectId: PROJECT_ID });
286
+ });
287
+
288
+ test("keeps relations that point at some other column", () => {
289
+ const result: JSONObject = applyTenantColumn(
290
+ { currentMonitorStatus: { _id: MONITOR_ID } },
291
+ new Monitor(),
292
+ PROJECT_ID,
293
+ );
294
+
295
+ expect(result["currentMonitorStatus"]).toEqual({ _id: MONITOR_ID });
296
+ expect(result["projectId"]).toBe(PROJECT_ID);
297
+ });
298
+
299
+ test("leaves a model without a tenant column untouched", () => {
300
+ const model: Monitor = new Monitor();
301
+ jest.spyOn(model, "getTenantColumn").mockReturnValue(null);
302
+
303
+ const input: JSONObject = { name: "unchanged" };
304
+ const result: JSONObject = applyTenantColumn(input, model, PROJECT_ID);
305
+
306
+ expect(result).toBe(input);
307
+ expect(result["projectId"]).toBeUndefined();
308
+ });
309
+
310
+ test("does not mutate the input", () => {
311
+ const input: JSONObject = { project: "some-other-project" };
312
+ applyTenantColumn(input, new Monitor(), PROJECT_ID);
313
+
314
+ expect(input).toEqual({ project: "some-other-project" });
315
+ });
316
+ });
317
+
318
+ describe("describeModelColumns", () => {
319
+ test("lists the model's real columns, sorted", () => {
320
+ const columns: string = describeModelColumns(new Monitor());
321
+
322
+ expect(columns).toContain("_id");
323
+ expect(columns).toContain("monitorType");
324
+ expect(columns).toContain("projectId");
325
+ expect(columns).not.toContain(" id,");
326
+ });
327
+
328
+ test("is sorted so the list is scannable", () => {
329
+ const columns: Array<string> = describeModelColumns(new Monitor()).split(
330
+ ", ",
331
+ );
332
+
333
+ const sorted: Array<string> = [...columns].sort(
334
+ (a: string, b: string): number => {
335
+ return a.localeCompare(b);
336
+ },
337
+ );
338
+
339
+ expect(columns).toEqual(sorted);
340
+ });
341
+ });
342
+
343
+ describe("buildColumnHint", () => {
344
+ test("points id at _id, which is the mistake in issue 3132", () => {
345
+ const hint: string | null = buildColumnHint(
346
+ 'Property "id" was not found in "Monitor". Make sure your query is correct.',
347
+ new Monitor(),
348
+ );
349
+
350
+ expect(hint).toContain('"id" is not a column on Monitor');
351
+ expect(hint).toContain('Use "_id" instead');
352
+ expect(hint).toContain("monitorType");
353
+ });
354
+
355
+ test("names the offending column and lists the real ones", () => {
356
+ const hint: string | null = buildColumnHint(
357
+ 'Property "currentStatus" was not found in "Monitor". Make sure your query is correct.',
358
+ new Monitor(),
359
+ );
360
+
361
+ expect(hint).toContain('"currentStatus" is not a column on Monitor');
362
+ expect(hint).toContain("Query, Select and Data");
363
+ expect(hint).toContain("currentMonitorStatusId");
364
+ });
365
+
366
+ test("returns null for an error that is not about an unknown column", () => {
367
+ expect(
368
+ buildColumnHint("connect ECONNREFUSED 127.0.0.1:5432", new Monitor()),
369
+ ).toBeNull();
370
+ });
371
+ });