@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,79 @@
1
+ import AIChatMessageStatus, {
2
+ AIChatMessageStatusHelper,
3
+ } from "../../../Types/AI/AIChatMessageStatus";
4
+
5
+ describe("AIChatMessageStatusHelper", () => {
6
+ const allStatuses: Array<AIChatMessageStatus> =
7
+ Object.values(AIChatMessageStatus);
8
+
9
+ describe("isTerminalStatus", () => {
10
+ test("Completed and Error are terminal", () => {
11
+ expect(
12
+ AIChatMessageStatusHelper.isTerminalStatus(
13
+ AIChatMessageStatus.Completed,
14
+ ),
15
+ ).toBe(true);
16
+ expect(
17
+ AIChatMessageStatusHelper.isTerminalStatus(AIChatMessageStatus.Error),
18
+ ).toBe(true);
19
+ });
20
+
21
+ test("in-flight statuses are not terminal", () => {
22
+ expect(
23
+ AIChatMessageStatusHelper.isTerminalStatus(AIChatMessageStatus.Pending),
24
+ ).toBe(false);
25
+ expect(
26
+ AIChatMessageStatusHelper.isTerminalStatus(
27
+ AIChatMessageStatus.InProgress,
28
+ ),
29
+ ).toBe(false);
30
+ expect(
31
+ AIChatMessageStatusHelper.isTerminalStatus(
32
+ AIChatMessageStatus.WaitingForApproval,
33
+ ),
34
+ ).toBe(false);
35
+ });
36
+ });
37
+
38
+ describe("isActiveStatus", () => {
39
+ test("Pending, InProgress and WaitingForApproval are active", () => {
40
+ expect(
41
+ AIChatMessageStatusHelper.isActiveStatus(AIChatMessageStatus.Pending),
42
+ ).toBe(true);
43
+ expect(
44
+ AIChatMessageStatusHelper.isActiveStatus(
45
+ AIChatMessageStatus.InProgress,
46
+ ),
47
+ ).toBe(true);
48
+ expect(
49
+ AIChatMessageStatusHelper.isActiveStatus(
50
+ AIChatMessageStatus.WaitingForApproval,
51
+ ),
52
+ ).toBe(true);
53
+ });
54
+
55
+ test("terminal statuses are not active", () => {
56
+ expect(
57
+ AIChatMessageStatusHelper.isActiveStatus(AIChatMessageStatus.Completed),
58
+ ).toBe(false);
59
+ expect(
60
+ AIChatMessageStatusHelper.isActiveStatus(AIChatMessageStatus.Error),
61
+ ).toBe(false);
62
+ });
63
+ });
64
+
65
+ test("active and terminal are mutually exclusive and jointly exhaustive", () => {
66
+ /*
67
+ * Every status must be exactly one of active or terminal — a status that
68
+ * is neither would hang a spinner forever; one that is both is a
69
+ * contradiction. This guards against a new enum member being added
70
+ * without wiring it into both predicates.
71
+ */
72
+ for (const status of allStatuses) {
73
+ const active: boolean = AIChatMessageStatusHelper.isActiveStatus(status);
74
+ const terminal: boolean =
75
+ AIChatMessageStatusHelper.isTerminalStatus(status);
76
+ expect(active).not.toBe(terminal);
77
+ }
78
+ });
79
+ });
@@ -0,0 +1,40 @@
1
+ import AIInsightStatus, {
2
+ AIInsightStatusHelper,
3
+ } from "../../../Types/AI/AIInsightStatus";
4
+
5
+ describe("AIInsightStatusHelper.isTerminalStatus", () => {
6
+ test("the two human end states are terminal", () => {
7
+ expect(
8
+ AIInsightStatusHelper.isTerminalStatus(AIInsightStatus.Resolved),
9
+ ).toBe(true);
10
+ expect(
11
+ AIInsightStatusHelper.isTerminalStatus(AIInsightStatus.Dismissed),
12
+ ).toBe(true);
13
+ });
14
+
15
+ test("the scanner-owned lifecycle states are not terminal", () => {
16
+ /*
17
+ * Detected/ActionRequired/FixOpened are the states a detector may still
18
+ * refresh (dedupe into). If any were treated as terminal the scanner
19
+ * would stop updating a live insight.
20
+ */
21
+ expect(
22
+ AIInsightStatusHelper.isTerminalStatus(AIInsightStatus.Detected),
23
+ ).toBe(false);
24
+ expect(
25
+ AIInsightStatusHelper.isTerminalStatus(AIInsightStatus.ActionRequired),
26
+ ).toBe(false);
27
+ expect(
28
+ AIInsightStatusHelper.isTerminalStatus(AIInsightStatus.FixOpened),
29
+ ).toBe(false);
30
+ });
31
+
32
+ test("exactly two of the five statuses are terminal", () => {
33
+ const terminalCount: number = Object.values(AIInsightStatus).filter(
34
+ (status: AIInsightStatus) => {
35
+ return AIInsightStatusHelper.isTerminalStatus(status);
36
+ },
37
+ ).length;
38
+ expect(terminalCount).toBe(2);
39
+ });
40
+ });
@@ -0,0 +1,157 @@
1
+ import Hostname from "../../../Types/API/Hostname";
2
+ import URL from "../../../Types/API/URL";
3
+ import BadDataException from "../../../Types/Exception/BadDataException";
4
+ import { describe, expect, test } from "@jest/globals";
5
+
6
+ /*
7
+ * Hostname used to validate with a character allowlist that happened to
8
+ * include "/", "?", "#" and "@". Anything that stores a Hostname and later
9
+ * interpolates it into a URL therefore inherited control of the host, port
10
+ * AND path of the resulting request — which is how a status page subdomain of
11
+ * "169.254.169.254/latest/meta-data/#" became a request to the cloud metadata
12
+ * endpoint. These tests pin the structural rules that replaced it, and the
13
+ * matching fix in URL.fromString, which used to leave a query string glued to
14
+ * the host.
15
+ */
16
+
17
+ describe("Hostname.isValid accepts real hosts", () => {
18
+ const validHostnames: Array<string> = [
19
+ "localhost",
20
+ "localhost:5000",
21
+ "example.com",
22
+ "status.example.com",
23
+ "deep.sub.domain.example.com",
24
+ "example.com.",
25
+ "EXAMPLE.com",
26
+ "my-host.internal",
27
+ "host_with_underscore.internal",
28
+ "_dmarc.example.com",
29
+ "1.2.3.4",
30
+ "1.2.3.4:8080",
31
+ "smtp.example.com:587",
32
+ "[::1]",
33
+ "[::1]:8080",
34
+ "[2606:2800:220:1:248:1893:25c8:1946]:443",
35
+ "::1",
36
+ "2606:2800:220:1:248:1893:25c8:1946",
37
+ // Userinfo is still accepted: it is already in customers' stored URLs.
38
+ "user:token@hooks.example.com",
39
+ "user@hooks.example.com",
40
+ "user:token@hooks.example.com:8443",
41
+ ];
42
+
43
+ test.each(validHostnames)("accepts %s", (hostname: string) => {
44
+ expect(Hostname.isValid(hostname)).toBe(true);
45
+ });
46
+ });
47
+
48
+ describe("Hostname.isValid rejects smuggled paths, queries and fragments", () => {
49
+ const invalidHostnames: Array<string> = [
50
+ // The status page / dashboard subdomain injection.
51
+ "169.254.169.254/latest/meta-data/#",
52
+ "169.254.169.254/latest/meta-data/#.example.com",
53
+ "example.com/../../etc/passwd",
54
+ "example.com/path",
55
+ "example.com?query=1",
56
+ "example.com#fragment",
57
+ "example.com:8080/path",
58
+ // Junk the old character allowlist waved through.
59
+ "example.com;evil",
60
+ "example.com,evil",
61
+ "exa mple.com",
62
+ "undefined undefined",
63
+ "localhost 5000",
64
+ "",
65
+ " ",
66
+ // A port that is not a port.
67
+ "example.com:notaport",
68
+ "example.com:80:90",
69
+ // Hyphen placement.
70
+ "-example.com",
71
+ "example-.com",
72
+ // Userinfo may not itself carry a path or a second authority.
73
+ "user@evil.com/@example.com",
74
+ "user@example.com?x=1",
75
+ ];
76
+
77
+ test.each(invalidHostnames)("rejects %s", (hostname: string) => {
78
+ expect(Hostname.isValid(hostname)).toBe(false);
79
+ });
80
+
81
+ test("the constructor throws rather than storing a smuggled value", () => {
82
+ expect(() => {
83
+ return new Hostname("169.254.169.254/latest/meta-data/#");
84
+ }).toThrow(BadDataException);
85
+ });
86
+ });
87
+
88
+ describe("URL.fromString keeps the authority separate from the rest", () => {
89
+ test("a query string on a path-less URL does not end up in the host", () => {
90
+ const url: URL = URL.fromString("https://hooks.example.com?token=abc");
91
+
92
+ expect(url.hostname.hostname).toBe("hooks.example.com");
93
+ expect(url.getQueryParam("token")).toBe("abc");
94
+ });
95
+
96
+ test("a fragment on a path-less URL does not end up in the host", () => {
97
+ const url: URL = URL.fromString("https://hooks.example.com#section");
98
+
99
+ expect(url.hostname.hostname).toBe("hooks.example.com");
100
+ });
101
+
102
+ test("a path still parses as a route", () => {
103
+ const url: URL = URL.fromString("https://hooks.example.com/a/b?x=1");
104
+
105
+ expect(url.hostname.hostname).toBe("hooks.example.com");
106
+ expect(url.route.toString()).toBe("a/b");
107
+ expect(url.getQueryParam("x")).toBe("1");
108
+ expect(url.toString()).toBe("https://hooks.example.com/a/b?x=1");
109
+ });
110
+
111
+ test("an upper-case scheme is stripped, not left in the authority", () => {
112
+ /*
113
+ * Schemes are case-insensitive. Matching the literal lower-case prefix
114
+ * left "HTTPS://" in place, so the authority read as "HTTPS:" — which
115
+ * the old permissive host check waved through and the structural one
116
+ * (correctly) refuses, turning a valid webhook URL into an error.
117
+ */
118
+ const url: URL = URL.fromString("HTTPS://hooks.example.com/webhook");
119
+
120
+ expect(url.hostname.hostname).toBe("hooks.example.com");
121
+ expect(url.protocol.toString()).toBe("https://");
122
+ });
123
+
124
+ test("a host with a port survives", () => {
125
+ const url: URL = URL.fromString("http://localhost:5000/api/test");
126
+
127
+ expect(url.hostname.toString()).toBe("localhost:5000");
128
+ expect(url.toString()).toBe("http://localhost:5000/api/test");
129
+ });
130
+
131
+ test("a mailto address is parsed as an email, not as a host", () => {
132
+ const url: URL = URL.fromString("mailto:support@oneuptime.com");
133
+
134
+ expect(url.email.toString()).toBe("support@oneuptime.com");
135
+ expect(url.toString()).toBe("mailto:support@oneuptime.com");
136
+ });
137
+
138
+ test("a mailto address with a subject does not go through the host validator", () => {
139
+ const url: URL = URL.fromString(
140
+ "mailto:support@oneuptime.com?subject=Hello",
141
+ );
142
+
143
+ expect(url.email.toString()).toBe("support@oneuptime.com");
144
+ expect(url.getQueryParam("subject")).toBe("Hello");
145
+ });
146
+
147
+ test("a webhook URL with basic-auth credentials still round-trips", () => {
148
+ /*
149
+ * These are already in customers' databases. Rejecting them here would
150
+ * fail on read, not just on write.
151
+ */
152
+ const url: URL = URL.fromString("https://user:token@hooks.example.com/x");
153
+
154
+ expect(url.hostname.hostname).toBe("user:token@hooks.example.com");
155
+ expect(url.toString()).toBe("https://user:token@hooks.example.com/x");
156
+ });
157
+ });
@@ -767,6 +767,33 @@ describe("DashboardTemplates invariants (all templates)", () => {
767
767
  }
768
768
  });
769
769
 
770
+ /*
771
+ * A multi-select variable resolves from its picks alone, and starts
772
+ * with none — so it opens on "All" and `defaultValue` never applies
773
+ * (see DashboardVariableInterpolation.resolveValue). A template that
774
+ * shipped one would look like it scoped its widgets and would not.
775
+ * Templates that want an opening scope must use a single-select.
776
+ */
777
+ test("no template ships a multi-select variable with a default", (): void => {
778
+ const offenders: Array<string> = [];
779
+ let multiSelectVariables: number = 0;
780
+
781
+ for (const loaded of loadAllTemplates()) {
782
+ for (const variable of loaded.config.variables || []) {
783
+ if (!variable.isMultiSelect) {
784
+ continue;
785
+ }
786
+ multiSelectVariables++;
787
+ if ((variable.defaultValue ?? "").length > 0) {
788
+ offenders.push(`${loaded.type} variable "${variable.name}"`);
789
+ }
790
+ }
791
+ }
792
+
793
+ expect(multiSelectVariables).toBeGreaterThan(0);
794
+ expect(offenders).toEqual([]);
795
+ });
796
+
770
797
  test("variable ids are freshly generated on every call", (): void => {
771
798
  const typesWithVariables: Array<DashboardTemplateType> =
772
799
  TEMPLATE_TYPES_WITH_CONFIG.filter(
@@ -0,0 +1,71 @@
1
+ import {
2
+ DATA_SOURCE_MAX_STEP_IN_SECONDS,
3
+ DATA_SOURCE_MIN_STEP_IN_SECONDS,
4
+ DATA_SOURCE_TARGET_BUCKETS,
5
+ DataSourceLimitsUtil,
6
+ } from "../../../Types/DataSource/DataSourceLimits";
7
+
8
+ describe("DataSourceLimitsUtil.getStepInSeconds", () => {
9
+ const stepFor: (seconds: number) => number = (seconds: number): number => {
10
+ const start: Date = new Date("2026-01-01T00:00:00.000Z");
11
+ const end: Date = new Date(start.getTime() + seconds * 1000);
12
+ return DataSourceLimitsUtil.getStepInSeconds(start, end);
13
+ };
14
+
15
+ test("aims for ~TARGET_BUCKETS buckets across the window", () => {
16
+ // A one-hour window / 250 buckets = 14.4s, ceil -> 15s.
17
+ expect(stepFor(60 * 60)).toBe(15);
18
+
19
+ // A 24-hour window / 250 = 345.6s, ceil -> 346s.
20
+ expect(stepFor(24 * 60 * 60)).toBe(346);
21
+ });
22
+
23
+ test("never returns a step below MIN_STEP for short windows", () => {
24
+ // A tiny window would compute a sub-second step; it is clamped up.
25
+ expect(stepFor(60)).toBe(DATA_SOURCE_MIN_STEP_IN_SECONDS);
26
+ expect(stepFor(1)).toBe(DATA_SOURCE_MIN_STEP_IN_SECONDS);
27
+ });
28
+
29
+ test("the MIN_STEP boundary is exact, not off-by-one", () => {
30
+ /*
31
+ * rawStep = ceil(range / TARGET_BUCKETS). The largest range whose rawStep
32
+ * still equals MIN_STEP is MIN_STEP * TARGET_BUCKETS seconds. One second
33
+ * more must tip rawStep to MIN_STEP + 1.
34
+ */
35
+ const atBoundary: number =
36
+ DATA_SOURCE_MIN_STEP_IN_SECONDS * DATA_SOURCE_TARGET_BUCKETS;
37
+ expect(stepFor(atBoundary)).toBe(DATA_SOURCE_MIN_STEP_IN_SECONDS);
38
+ expect(stepFor(atBoundary + 1)).toBe(DATA_SOURCE_MIN_STEP_IN_SECONDS + 1);
39
+ });
40
+
41
+ test("never returns a step above MAX_STEP for huge windows", () => {
42
+ // A one-year window would compute a far larger step; it is clamped down.
43
+ expect(stepFor(365 * 24 * 60 * 60)).toBe(DATA_SOURCE_MAX_STEP_IN_SECONDS);
44
+ });
45
+
46
+ test("a zero-length window is treated as at least one second", () => {
47
+ const instant: Date = new Date("2026-01-01T00:00:00.000Z");
48
+ // range floors to >=1s, so the step is the clamped minimum, never 0.
49
+ expect(DataSourceLimitsUtil.getStepInSeconds(instant, instant)).toBe(
50
+ DATA_SOURCE_MIN_STEP_IN_SECONDS,
51
+ );
52
+ });
53
+
54
+ test("an inverted range (end before start) still yields a sane minimum", () => {
55
+ // Guards against a negative step reaching a downstream range query.
56
+ const start: Date = new Date("2026-01-01T12:00:00.000Z");
57
+ const end: Date = new Date("2026-01-01T00:00:00.000Z");
58
+ expect(DataSourceLimitsUtil.getStepInSeconds(start, end)).toBe(
59
+ DATA_SOURCE_MIN_STEP_IN_SECONDS,
60
+ );
61
+ });
62
+
63
+ test("the returned step is always a whole number within bounds", () => {
64
+ for (const seconds of [1, 90, 3600, 86400, 7 * 86400, 400 * 86400]) {
65
+ const step: number = stepFor(seconds);
66
+ expect(Number.isInteger(step)).toBe(true);
67
+ expect(step).toBeGreaterThanOrEqual(DATA_SOURCE_MIN_STEP_IN_SECONDS);
68
+ expect(step).toBeLessThanOrEqual(DATA_SOURCE_MAX_STEP_IN_SECONDS);
69
+ }
70
+ });
71
+ });
@@ -0,0 +1,181 @@
1
+ import DataSourceType, {
2
+ DataSourceTypeCategories,
3
+ DataSourceTypeCategory,
4
+ DataSourceTypeProps,
5
+ DataSourceTypeUtil,
6
+ } from "../../../Types/DataSource/DataSourceType";
7
+
8
+ describe("DataSourceTypeUtil", () => {
9
+ describe("getAllDataSourceTypes", () => {
10
+ test("returns every declared DataSourceType exactly once", () => {
11
+ const all: Array<DataSourceType> =
12
+ DataSourceTypeUtil.getAllDataSourceTypes();
13
+
14
+ // Must match the enum members one-for-one — no missing, no duplicates.
15
+ const enumMembers: Array<DataSourceType> = Object.values(DataSourceType);
16
+ expect([...all].sort()).toEqual([...enumMembers].sort());
17
+ expect(new Set(all).size).toBe(all.length);
18
+ });
19
+
20
+ test("returns a fresh array each call (callers may mutate it)", () => {
21
+ const first: Array<DataSourceType> =
22
+ DataSourceTypeUtil.getAllDataSourceTypes();
23
+ const second: Array<DataSourceType> =
24
+ DataSourceTypeUtil.getAllDataSourceTypes();
25
+ expect(first).not.toBe(second);
26
+ expect(first).toEqual(second);
27
+ });
28
+ });
29
+
30
+ describe("isHttpBasedType / isDatabaseType partition", () => {
31
+ test("every type is exactly one of HTTP-based or database", () => {
32
+ for (const type of DataSourceTypeUtil.getAllDataSourceTypes()) {
33
+ const http: boolean = DataSourceTypeUtil.isHttpBasedType(type);
34
+ const db: boolean = DataSourceTypeUtil.isDatabaseType(type);
35
+ // Exactly one of the two must be true — they partition the set.
36
+ expect(http).not.toEqual(db);
37
+ }
38
+ });
39
+
40
+ test("HTTP-based types are the metrics/logs/search/api connectors", () => {
41
+ expect(
42
+ DataSourceTypeUtil.isHttpBasedType(DataSourceType.Prometheus),
43
+ ).toBe(true);
44
+ expect(DataSourceTypeUtil.isHttpBasedType(DataSourceType.Loki)).toBe(
45
+ true,
46
+ );
47
+ expect(
48
+ DataSourceTypeUtil.isHttpBasedType(DataSourceType.Elasticsearch),
49
+ ).toBe(true);
50
+ expect(DataSourceTypeUtil.isHttpBasedType(DataSourceType.RestApi)).toBe(
51
+ true,
52
+ );
53
+ expect(
54
+ DataSourceTypeUtil.isHttpBasedType(DataSourceType.PostgreSQL),
55
+ ).toBe(false);
56
+ });
57
+
58
+ test("database types are the SQL-driver connectors", () => {
59
+ expect(DataSourceTypeUtil.isDatabaseType(DataSourceType.PostgreSQL)).toBe(
60
+ true,
61
+ );
62
+ expect(DataSourceTypeUtil.isDatabaseType(DataSourceType.MySQL)).toBe(
63
+ true,
64
+ );
65
+ expect(
66
+ DataSourceTypeUtil.isDatabaseType(DataSourceType.MicrosoftSqlServer),
67
+ ).toBe(true);
68
+ expect(DataSourceTypeUtil.isDatabaseType(DataSourceType.ClickHouse)).toBe(
69
+ true,
70
+ );
71
+ expect(DataSourceTypeUtil.isDatabaseType(DataSourceType.Prometheus)).toBe(
72
+ false,
73
+ );
74
+ });
75
+ });
76
+
77
+ describe("getDefaultPort", () => {
78
+ test("database types seed the well-known driver port", () => {
79
+ expect(DataSourceTypeUtil.getDefaultPort(DataSourceType.PostgreSQL)).toBe(
80
+ 5432,
81
+ );
82
+ expect(DataSourceTypeUtil.getDefaultPort(DataSourceType.MySQL)).toBe(
83
+ 3306,
84
+ );
85
+ expect(
86
+ DataSourceTypeUtil.getDefaultPort(DataSourceType.MicrosoftSqlServer),
87
+ ).toBe(1433);
88
+ // ClickHouse is queried over its HTTP interface, not the native 9000.
89
+ expect(DataSourceTypeUtil.getDefaultPort(DataSourceType.ClickHouse)).toBe(
90
+ 8123,
91
+ );
92
+ });
93
+
94
+ test("HTTP-based types have no default port (they carry a full URL)", () => {
95
+ expect(
96
+ DataSourceTypeUtil.getDefaultPort(DataSourceType.Prometheus),
97
+ ).toBeNull();
98
+ expect(DataSourceTypeUtil.getDefaultPort(DataSourceType.Loki)).toBeNull();
99
+ expect(
100
+ DataSourceTypeUtil.getDefaultPort(DataSourceType.Elasticsearch),
101
+ ).toBeNull();
102
+ expect(
103
+ DataSourceTypeUtil.getDefaultPort(DataSourceType.RestApi),
104
+ ).toBeNull();
105
+ });
106
+
107
+ test("a default port is defined for exactly the database types", () => {
108
+ for (const type of DataSourceTypeUtil.getAllDataSourceTypes()) {
109
+ const hasPort: boolean =
110
+ DataSourceTypeUtil.getDefaultPort(type) !== null;
111
+ expect(hasPort).toBe(DataSourceTypeUtil.isDatabaseType(type));
112
+ }
113
+ });
114
+ });
115
+
116
+ describe("getProps", () => {
117
+ test("every type gets self-consistent, non-empty display props", () => {
118
+ for (const type of DataSourceTypeUtil.getAllDataSourceTypes()) {
119
+ const props: DataSourceTypeProps = DataSourceTypeUtil.getProps(type);
120
+
121
+ // The props describe the very type they were requested for.
122
+ expect(props.dataSourceType).toBe(type);
123
+ expect(props.title.length).toBeGreaterThan(0);
124
+ expect(props.description.length).toBeGreaterThan(0);
125
+ expect(props.queryLanguageTitle.length).toBeGreaterThan(0);
126
+ expect(props.queryPlaceholder.length).toBeGreaterThan(0);
127
+ // The category must be one the picker knows how to render.
128
+ expect(DataSourceTypeCategories).toContain(props.category);
129
+ }
130
+ });
131
+
132
+ test("getCategory agrees with getProps().category", () => {
133
+ for (const type of DataSourceTypeUtil.getAllDataSourceTypes()) {
134
+ expect(DataSourceTypeUtil.getCategory(type)).toBe(
135
+ DataSourceTypeUtil.getProps(type).category,
136
+ );
137
+ }
138
+ });
139
+
140
+ test("all four database types are categorised as Databases", () => {
141
+ for (const type of DataSourceTypeUtil.getAllDataSourceTypes()) {
142
+ if (DataSourceTypeUtil.isDatabaseType(type)) {
143
+ expect(DataSourceTypeUtil.getCategory(type)).toBe(
144
+ DataSourceTypeCategory.Databases,
145
+ );
146
+ }
147
+ }
148
+ });
149
+
150
+ test("the connector categories land in their expected sections", () => {
151
+ expect(DataSourceTypeUtil.getCategory(DataSourceType.Prometheus)).toBe(
152
+ DataSourceTypeCategory.Metrics,
153
+ );
154
+ expect(DataSourceTypeUtil.getCategory(DataSourceType.Loki)).toBe(
155
+ DataSourceTypeCategory.Logs,
156
+ );
157
+ expect(DataSourceTypeUtil.getCategory(DataSourceType.Elasticsearch)).toBe(
158
+ DataSourceTypeCategory.Search,
159
+ );
160
+ expect(DataSourceTypeUtil.getCategory(DataSourceType.RestApi)).toBe(
161
+ DataSourceTypeCategory.Api,
162
+ );
163
+ });
164
+ });
165
+
166
+ describe("DataSourceTypeCategories ordering", () => {
167
+ test("lists every category once, in the picker's display order", () => {
168
+ expect(DataSourceTypeCategories).toEqual([
169
+ DataSourceTypeCategory.Metrics,
170
+ DataSourceTypeCategory.Logs,
171
+ DataSourceTypeCategory.Databases,
172
+ DataSourceTypeCategory.Search,
173
+ DataSourceTypeCategory.Api,
174
+ ]);
175
+ // No category is missing and none is listed twice.
176
+ expect(new Set(DataSourceTypeCategories).size).toBe(
177
+ Object.values(DataSourceTypeCategory).length,
178
+ );
179
+ });
180
+ });
181
+ });
@@ -0,0 +1,90 @@
1
+ import Domain from "../../Types/Domain";
2
+ import URL from "../../Types/API/URL";
3
+ import { describe, expect, test } from "@jest/globals";
4
+
5
+ /*
6
+ * StatusPageDomain.subdomain and DashboardDomain.subdomain are ShortText that
7
+ * used to be trimmed and lower-cased and nothing else. Both services build
8
+ * fullDomain as `${subdomain}.${baseDomain}` and then interpolate it into
9
+ * "https://" + fullDomain + "/status-page-api/...", which the certificate
10
+ * cron fetches unattended every 15 minutes. Because URL parsing takes
11
+ * everything before the first "/" as the host, a subdomain carrying a "/"
12
+ * gave an attacker the host, the port and the path of that request.
13
+ */
14
+
15
+ describe("Domain.isValidSubdomain accepts ordinary subdomains", () => {
16
+ const valid: Array<string> = [
17
+ "status",
18
+ "dashboard",
19
+ "status-page",
20
+ "eu-west-1",
21
+ "a",
22
+ "status.eu",
23
+ "deep.nested.label",
24
+ "s1",
25
+ "1status",
26
+ ];
27
+
28
+ test.each(valid)("accepts %s", (subdomain: string) => {
29
+ expect(Domain.isValidSubdomain(subdomain)).toBe(true);
30
+ });
31
+ });
32
+
33
+ describe("Domain.isValidSubdomain rejects anything that is not a DNS label", () => {
34
+ const invalid: Array<string> = [
35
+ // The metadata-endpoint injection this exists to stop.
36
+ "169.254.169.254/latest/meta-data/#",
37
+ "169.254.169.254/latest/meta-data/iam/security-credentials/#",
38
+ "status/../../admin",
39
+ "status/path",
40
+ // Scheme, port, credentials, query, fragment.
41
+ "http://169.254.169.254",
42
+ "169.254.169.254:80",
43
+ "user@169.254.169.254",
44
+ "status?x=1",
45
+ "status#frag",
46
+ "status\\evil",
47
+ // Structural junk.
48
+ "-status",
49
+ "status-",
50
+ "sta tus",
51
+ "",
52
+ "@",
53
+ ".",
54
+ "status..double",
55
+ ".status",
56
+ "status.",
57
+ ];
58
+
59
+ test.each(invalid)("rejects %s", (subdomain: string) => {
60
+ expect(Domain.isValidSubdomain(subdomain)).toBe(false);
61
+ });
62
+
63
+ test("rejects a label longer than 63 characters", () => {
64
+ expect(Domain.isValidSubdomain("a".repeat(63))).toBe(true);
65
+ expect(Domain.isValidSubdomain("a".repeat(64))).toBe(false);
66
+ });
67
+ });
68
+
69
+ describe("the injection this blocks, end to end", () => {
70
+ test("an unvalidated subdomain would have redirected the cert check to metadata", () => {
71
+ /*
72
+ * Demonstrates why a subdomain check is the fix rather than trusting the
73
+ * URL builder: with the malicious label in place, the host of the
74
+ * resulting URL is the metadata endpoint and the base domain has been
75
+ * demoted into the path.
76
+ */
77
+ const maliciousSubdomain: string = "169.254.169.254/latest/meta-data/#";
78
+ const baseDomain: string = "example.com";
79
+ const fullDomain: string = `${maliciousSubdomain}.${baseDomain}`;
80
+
81
+ const url: URL = URL.fromString(
82
+ "https://" + fullDomain + "/status-page-api/cname-verification/token",
83
+ );
84
+
85
+ expect(url.hostname.hostname).toBe("169.254.169.254");
86
+
87
+ // And the validator refuses to let that subdomain be stored at all.
88
+ expect(Domain.isValidSubdomain(maliciousSubdomain)).toBe(false);
89
+ });
90
+ });