@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,315 @@
1
+ import API, { RequestOptions } from "../../../../Utils/API";
2
+ import HTTPResponse from "../../../../Types/API/HTTPResponse";
3
+ import { JSONObject } from "../../../../Types/JSON";
4
+ import LlmType from "../../../../Types/LLM/LlmType";
5
+ import LLMService from "../../../../Server/Utils/LLM/LLMService";
6
+ import DataSourceEgressGuard, {
7
+ EgressResolveFunction,
8
+ ResolvedAddress,
9
+ } from "../../../../Server/Utils/DataSource/EgressGuard";
10
+ import {
11
+ afterEach,
12
+ beforeEach,
13
+ describe,
14
+ expect,
15
+ jest,
16
+ test,
17
+ } from "@jest/globals";
18
+ import dns from "dns";
19
+
20
+ /*
21
+ * LlmProvider.baseUrl is writable by any ProjectMember, while reading the
22
+ * provider's apiKey is restricted to Owner/Admin. So an unguarded outbound
23
+ * request is a privilege escalation with two payouts: the decrypted key goes
24
+ * out in the auth header of a request to a host the member chose, and the
25
+ * provider's error body is reflected back through the LLM provider API, which
26
+ * turns the same lever into a read primitive against the internal network.
27
+ *
28
+ * These tests assert that no request leaves for an internal address, that the
29
+ * checked address is pinned into the socket, and that redirects are refused.
30
+ * DNS is mocked so they are deterministic and offline.
31
+ */
32
+
33
+ type LookupSpy = jest.SpiedFunction<
34
+ (
35
+ hostname: string,
36
+ options: { all: true },
37
+ ) => Promise<Array<{ address: string; family: number }>>
38
+ >;
39
+
40
+ let lookupSpy: LookupSpy;
41
+
42
+ // Every API.post the service made, so tests can assert on "never sent".
43
+ const postCalls: Array<{ options?: RequestOptions | undefined }> = [];
44
+
45
+ function mockPostSuccess(): void {
46
+ jest.spyOn(API, "post").mockImplementation(((options: {
47
+ options?: RequestOptions | undefined;
48
+ }) => {
49
+ postCalls.push(options);
50
+
51
+ return Promise.resolve({
52
+ jsonData: {
53
+ choices: [{ message: { content: "ok" } }],
54
+ content: [{ type: "text", text: "ok" }],
55
+ message: { content: "ok" },
56
+ usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
57
+ },
58
+ } as unknown as HTTPResponse<JSONObject>);
59
+ }) as never);
60
+ }
61
+
62
+ function lastPostOptions(): RequestOptions {
63
+ return postCalls[postCalls.length - 1]!.options as RequestOptions;
64
+ }
65
+
66
+ beforeEach(() => {
67
+ postCalls.length = 0;
68
+ lookupSpy = jest.spyOn(dns.promises, "lookup") as unknown as LookupSpy;
69
+
70
+ /*
71
+ * Stand in for getaddrinfo: "localhost" is loopback, everything else is a
72
+ * public address. Individual tests override this to model rebinding.
73
+ */
74
+ lookupSpy.mockImplementation((hostname: string) => {
75
+ if (hostname === "localhost") {
76
+ return Promise.resolve([{ address: "127.0.0.1", family: 4 }]);
77
+ }
78
+
79
+ return Promise.resolve([{ address: "93.184.216.34", family: 4 }]);
80
+ });
81
+
82
+ mockPostSuccess();
83
+ });
84
+
85
+ afterEach(() => {
86
+ jest.restoreAllMocks();
87
+ });
88
+
89
+ describe("LLMService refuses internal LLM provider base URLs", () => {
90
+ const blockedBaseUrls: Array<string> = [
91
+ "http://127.0.0.1:11434",
92
+ "http://localhost:11434",
93
+ "http://169.254.169.254",
94
+ "http://169.254.169.254:80",
95
+ "http://[::1]:11434",
96
+ "http://0.0.0.0:11434",
97
+ ];
98
+
99
+ test.each(blockedBaseUrls)(
100
+ "OpenAI-compatible provider at %s never posts",
101
+ async (baseUrl: string) => {
102
+ await expect(
103
+ LLMService.getCompletion({
104
+ llmProviderConfig: {
105
+ llmType: LlmType.OpenAICompatible,
106
+ apiKey: "sk-secret",
107
+ modelName: "some-model",
108
+ baseUrl,
109
+ },
110
+ messages: [{ role: "user", content: "hi" }],
111
+ }),
112
+ ).rejects.toThrow();
113
+
114
+ expect(postCalls).toHaveLength(0);
115
+ },
116
+ );
117
+
118
+ test("Ollama provider at a loopback base URL never posts", async () => {
119
+ await expect(
120
+ LLMService.getCompletion({
121
+ llmProviderConfig: {
122
+ llmType: LlmType.Ollama,
123
+ modelName: "llama2",
124
+ baseUrl: "http://127.0.0.1:11434",
125
+ },
126
+ messages: [{ role: "user", content: "hi" }],
127
+ }),
128
+ ).rejects.toThrow();
129
+
130
+ expect(postCalls).toHaveLength(0);
131
+ });
132
+
133
+ test("Anthropic provider repointed at the metadata endpoint never posts", async () => {
134
+ await expect(
135
+ LLMService.getCompletion({
136
+ llmProviderConfig: {
137
+ llmType: LlmType.Anthropic,
138
+ apiKey: "sk-ant-secret",
139
+ modelName: "claude-sonnet-4-20250514",
140
+ baseUrl: "http://169.254.169.254/latest",
141
+ },
142
+ messages: [{ role: "user", content: "hi" }],
143
+ }),
144
+ ).rejects.toThrow();
145
+
146
+ expect(postCalls).toHaveLength(0);
147
+ });
148
+
149
+ test("Azure provider repointed at loopback never posts", async () => {
150
+ await expect(
151
+ LLMService.getCompletion({
152
+ llmProviderConfig: {
153
+ llmType: LlmType.AzureOpenAI,
154
+ apiKey: "azure-secret",
155
+ modelName: "gpt-4o",
156
+ baseUrl: "http://127.0.0.1/openai/deployments/gpt-4o",
157
+ },
158
+ messages: [{ role: "user", content: "hi" }],
159
+ }),
160
+ ).rejects.toThrow();
161
+
162
+ expect(postCalls).toHaveLength(0);
163
+ });
164
+
165
+ test("a public hostname that resolves to a private address never posts", async () => {
166
+ // The DNS-rebinding shape: the name looks fine, the answer does not.
167
+ lookupSpy.mockResolvedValue([{ address: "10.0.0.5", family: 4 }]);
168
+
169
+ /*
170
+ * Private ranges are only blocked under SaaS policy; this is the
171
+ * documented switch that turns it on for a self-hosted install.
172
+ */
173
+ process.env["DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES"] = "true";
174
+
175
+ try {
176
+ await expect(
177
+ LLMService.getCompletion({
178
+ llmProviderConfig: {
179
+ llmType: LlmType.OpenAICompatible,
180
+ apiKey: "sk-secret",
181
+ modelName: "some-model",
182
+ baseUrl: "https://ollama.attacker.example/v1",
183
+ },
184
+ messages: [{ role: "user", content: "hi" }],
185
+ }),
186
+ ).rejects.toThrow();
187
+ } finally {
188
+ delete process.env["DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES"];
189
+ }
190
+
191
+ expect(postCalls).toHaveLength(0);
192
+ });
193
+ });
194
+
195
+ describe("LLMService pins and refuses redirects on allowed providers", () => {
196
+ test("a public provider posts with redirects disabled and pinned agents", async () => {
197
+ await LLMService.getCompletion({
198
+ llmProviderConfig: {
199
+ llmType: LlmType.OpenAICompatible,
200
+ apiKey: "sk-secret",
201
+ modelName: "some-model",
202
+ baseUrl: "https://gateway.example.com/v1",
203
+ },
204
+ messages: [{ role: "user", content: "hi" }],
205
+ });
206
+
207
+ const options: RequestOptions = lastPostOptions();
208
+
209
+ expect(options.doNotFollowRedirects).toBe(true);
210
+ expect(options.httpAgent).toBeDefined();
211
+ expect(options.httpsAgent).toBeDefined();
212
+ });
213
+
214
+ test("the pinned agent dials the address that was validated", async () => {
215
+ lookupSpy.mockResolvedValue([{ address: "93.184.216.34", family: 4 }]);
216
+
217
+ await LLMService.getCompletion({
218
+ llmProviderConfig: {
219
+ llmType: LlmType.Anthropic,
220
+ apiKey: "sk-ant-secret",
221
+ modelName: "claude-sonnet-4-20250514",
222
+ baseUrl: "https://api.anthropic.com/v1",
223
+ },
224
+ messages: [{ role: "user", content: "hi" }],
225
+ });
226
+
227
+ const options: RequestOptions = lastPostOptions();
228
+
229
+ const lookup: (
230
+ hostname: string,
231
+ lookupOptions: { all?: boolean },
232
+ callback: (error: Error | null, address: string, family?: number) => void,
233
+ ) => void = (
234
+ options.httpsAgent as unknown as {
235
+ options: {
236
+ lookup: (
237
+ hostname: string,
238
+ lookupOptions: { all?: boolean },
239
+ callback: (
240
+ error: Error | null,
241
+ address: string,
242
+ family?: number,
243
+ ) => void,
244
+ ) => void;
245
+ };
246
+ }
247
+ ).options.lookup;
248
+
249
+ const dialed: string = await new Promise((resolve: (v: string) => void) => {
250
+ lookup(
251
+ "api.anthropic.com",
252
+ { all: false },
253
+ (_error: Error | null, address: string) => {
254
+ resolve(address);
255
+ },
256
+ );
257
+ });
258
+
259
+ expect(dialed).toBe("93.184.216.34");
260
+ });
261
+
262
+ test("the guard resolves once per completion, not once per retry", async () => {
263
+ /*
264
+ * postOpenAIChatCompletion re-posts when the provider rejects a generation
265
+ * parameter. Re-resolving per attempt would add a fresh rebind window
266
+ * between attempts.
267
+ */
268
+ await LLMService.getCompletion({
269
+ llmProviderConfig: {
270
+ llmType: LlmType.OpenAICompatible,
271
+ apiKey: "sk-secret",
272
+ modelName: "some-model",
273
+ baseUrl: "https://gateway.example.com/v1",
274
+ },
275
+ messages: [{ role: "user", content: "hi" }],
276
+ });
277
+
278
+ expect(lookupSpy).toHaveBeenCalledTimes(1);
279
+ });
280
+ });
281
+
282
+ describe("DataSourceEgressGuard self-hosted policy for LLM providers", () => {
283
+ const resolveToPrivate: EgressResolveFunction = (): Promise<
284
+ Array<ResolvedAddress>
285
+ > => {
286
+ return Promise.resolve([{ address: "10.0.0.5", family: 4 }]);
287
+ };
288
+
289
+ test("a self-hosted Ollama on a private address is still reachable", async () => {
290
+ // The documented deployment: blockPrivateAddresses off (no billing).
291
+ await expect(
292
+ DataSourceEgressGuard.assertUrlAllowed("http://ollama.internal:11434", {
293
+ blockPrivateAddresses: false,
294
+ resolveFunction: resolveToPrivate,
295
+ targetLabel: "LLM provider",
296
+ }),
297
+ ).resolves.toBeDefined();
298
+ });
299
+
300
+ test("but loopback and link-local are blocked even self-hosted", async () => {
301
+ await expect(
302
+ DataSourceEgressGuard.assertUrlAllowed("http://127.0.0.1:11434", {
303
+ blockPrivateAddresses: false,
304
+ targetLabel: "LLM provider",
305
+ }),
306
+ ).rejects.toThrow();
307
+
308
+ await expect(
309
+ DataSourceEgressGuard.assertUrlAllowed("http://169.254.169.254/", {
310
+ blockPrivateAddresses: false,
311
+ targetLabel: "LLM provider",
312
+ }),
313
+ ).rejects.toThrow();
314
+ });
315
+ });
@@ -1,4 +1,5 @@
1
1
  import API from "../../../../Utils/API";
2
+ import stubLLMEgressGuard from "./StubLLMEgressGuard";
2
3
  import logger from "../../../../Server/Utils/Logger";
3
4
  import HTTPErrorResponse from "../../../../Types/API/HTTPErrorResponse";
4
5
  import HTTPResponse from "../../../../Types/API/HTTPResponse";
@@ -90,6 +91,8 @@ function bodyOf(spy: PostSpy, callIndex: number): JSONObject {
90
91
  }
91
92
 
92
93
  beforeEach(() => {
94
+ // These hosts are placeholders; the SSRF guard is covered elsewhere.
95
+ stubLLMEgressGuard();
93
96
  LLMService.clearRequestAdaptationCache();
94
97
  jest.spyOn(logger, "debug").mockImplementation((): void => {});
95
98
  jest.spyOn(logger, "error").mockImplementation((): void => {});
@@ -7,7 +7,8 @@ import LlmType from "../../../../Types/LLM/LlmType";
7
7
  import LLMService, {
8
8
  LLMProviderConfig,
9
9
  } from "../../../../Server/Utils/LLM/LLMService";
10
- import { afterEach, describe, expect, test } from "@jest/globals";
10
+ import stubLLMEgressGuard from "./StubLLMEgressGuard";
11
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
11
12
 
12
13
  type PostSpy = ReturnType<typeof jest.spyOn>;
13
14
 
@@ -83,6 +84,11 @@ function mockPost(response: JSONObject): PostSpy {
83
84
  } as unknown as HTTPResponse<JSONObject>) as PostSpy;
84
85
  }
85
86
 
87
+ // These hosts are placeholders; the SSRF guard is covered elsewhere.
88
+ beforeEach(() => {
89
+ stubLLMEgressGuard();
90
+ });
91
+
86
92
  afterEach(() => {
87
93
  jest.restoreAllMocks();
88
94
  });
@@ -103,11 +109,16 @@ describe("LLMService request timeout and retry policy", () => {
103
109
  expect(postSpy).toHaveBeenCalledTimes(1);
104
110
  expect(postSpy.mock.calls[0]![0]).toEqual(
105
111
  expect.objectContaining({
106
- options: {
112
+ options: expect.objectContaining({
107
113
  retries: 0,
108
114
  exponentialBackoff: true,
109
115
  timeout: 12_345,
110
- },
116
+ /*
117
+ * Every provider request also carries the SSRF guard's
118
+ * settings — the policy above must not displace them.
119
+ */
120
+ doNotFollowRedirects: true,
121
+ }),
111
122
  }),
112
123
  );
113
124
  },
@@ -125,11 +136,12 @@ describe("LLMService request timeout and retry policy", () => {
125
136
 
126
137
  expect(postSpy.mock.calls[0]![0]).toEqual(
127
138
  expect.objectContaining({
128
- options: {
139
+ options: expect.objectContaining({
129
140
  retries: 2,
130
141
  exponentialBackoff: true,
131
142
  timeout: defaultTimeoutInMs,
132
- },
143
+ doNotFollowRedirects: true,
144
+ }),
133
145
  }),
134
146
  );
135
147
  },
@@ -5,7 +5,15 @@ import LlmType from "../../../../Types/LLM/LlmType";
5
5
  import LLMService, {
6
6
  LLMCompletionResponse,
7
7
  } from "../../../../Server/Utils/LLM/LLMService";
8
- import { afterEach, describe, expect, jest, test } from "@jest/globals";
8
+ import stubLLMEgressGuard from "./StubLLMEgressGuard";
9
+ import {
10
+ afterEach,
11
+ beforeEach,
12
+ describe,
13
+ expect,
14
+ jest,
15
+ test,
16
+ } from "@jest/globals";
9
17
 
10
18
  type PostSpy = ReturnType<typeof jest.spyOn>;
11
19
 
@@ -15,6 +23,11 @@ function mockPostResponse(jsonData: JSONObject): PostSpy {
15
23
  } as unknown as HTTPResponse<JSONObject>) as PostSpy;
16
24
  }
17
25
 
26
+ // These hosts are placeholders; the SSRF guard is covered elsewhere.
27
+ beforeEach(() => {
28
+ stubLLMEgressGuard();
29
+ });
30
+
18
31
  afterEach(() => {
19
32
  jest.restoreAllMocks();
20
33
  });
@@ -140,3 +140,38 @@ describe("buildObservabilityChatSystemPrompt with page context", () => {
140
140
  expect(prompt).toContain("Cite your sources.");
141
141
  });
142
142
  });
143
+
144
+ /*
145
+ * open_code_pull_request now opens the PR ready for review rather than as a
146
+ * draft. The system prompt describes that tool to the model in its own words,
147
+ * so a stale sentence here makes the model tell the user it filed a draft and
148
+ * that they should go mark it ready — for a PR that already is.
149
+ *
150
+ * What must stay is the part that was never about the draft flag: this is a
151
+ * proposal off the default branch that a human reviews and merges.
152
+ */
153
+ describe("buildObservabilityChatSystemPrompt — how it describes code changes", () => {
154
+ const prompt: string = buildObservabilityChatSystemPrompt({
155
+ currentTime: new Date("2026-07-16T00:00:00Z"),
156
+ permissionMode: AIChatPermissionMode.AskForApproval,
157
+ });
158
+
159
+ test("does not tell the model the pull request is a draft", () => {
160
+ expect(prompt.toLowerCase()).not.toContain("draft pull request");
161
+ });
162
+
163
+ test("says the pull request opens ready for review", () => {
164
+ expect(prompt).toContain("ready for review");
165
+ });
166
+
167
+ test("still says the change goes off the default branch for a human", () => {
168
+ expect(prompt).toContain("off the default branch");
169
+ expect(prompt).toContain("for a human to review");
170
+ });
171
+
172
+ test("still prefers open_code_pull_request over committing to a branch", () => {
173
+ expect(prompt).toContain(
174
+ "open_code_pull_request is the right tool almost always",
175
+ );
176
+ });
177
+ });
@@ -0,0 +1,25 @@
1
+ import DataSourceEgressGuard from "../../../../Server/Utils/DataSource/EgressGuard";
2
+ import { jest } from "@jest/globals";
3
+
4
+ /*
5
+ * LLMService validates and pins every provider URL through the SSRF egress
6
+ * guard before posting, which means a real DNS lookup for whatever host the
7
+ * test configured. The suites that are about request SHAPE rather than egress
8
+ * policy use unresolvable placeholder hosts ("http://host:8000"), so they stub
9
+ * the guard and stay offline.
10
+ *
11
+ * Call this from a beforeEach, before the API.post stub. Egress policy itself
12
+ * is covered by LLMServiceEgressGuard.test.ts, which does NOT stub it.
13
+ */
14
+ export default function stubLLMEgressGuard(): void {
15
+ jest
16
+ .spyOn(DataSourceEgressGuard, "assertUrlAllowedAndPin")
17
+ .mockImplementation((urlString: string) => {
18
+ return Promise.resolve({
19
+ url: new globalThis.URL(urlString),
20
+ addresses: [{ address: "93.184.216.34", family: 4 }],
21
+ httpAgent: undefined as never,
22
+ httpsAgent: undefined as never,
23
+ });
24
+ });
25
+ }
@@ -694,7 +694,13 @@ describe("PublicDashboardResourceListPolicy", () => {
694
694
  );
695
695
  });
696
696
 
697
- it("ignores a forged scalar All value for a stored multi-select", () => {
697
+ /*
698
+ * A stored multi-select reads only `selectedValues`. A scalar in the
699
+ * request is not a narrower selection the viewer is entitled to — it
700
+ * is a field this variable does not have — so it must not become a
701
+ * predicate, whatever it contains.
702
+ */
703
+ it("ignores a forged scalar value for a stored multi-select", () => {
698
704
  const storedVariables: Array<JSONObject> = [
699
705
  {
700
706
  id: "namespace",
@@ -712,26 +718,75 @@ describe("PublicDashboardResourceListPolicy", () => {
712
718
  requestedVariables: [
713
719
  {
714
720
  id: "namespace",
715
- selectedValue: "",
721
+ selectedValue: "kube-system",
716
722
  selectedValues: [],
717
723
  },
718
724
  ],
719
725
  });
720
- expect(forgedScalar.query["namespaceKey"]).toBe("production");
726
+ expect(forgedScalar.query["namespaceKey"]).toBeUndefined();
721
727
 
722
- const compatiblePlaceholder: PublicDashboardResourceListPolicyResult =
728
+ const forgedScalarAlongsidePicks: PublicDashboardResourceListPolicyResult =
723
729
  build({
724
730
  componentType: DashboardComponentType.KubernetesPodList,
725
731
  storedVariables,
726
732
  requestedVariables: [
727
733
  {
728
734
  id: "namespace",
729
- selectedValue: null,
730
- selectedValues: [],
735
+ selectedValue: "kube-system",
736
+ selectedValues: ["prod"],
731
737
  },
732
738
  ],
733
739
  });
734
- expect(compatiblePlaceholder.query["namespaceKey"]).toBe("production");
740
+ expect(forgedScalarAlongsidePicks.query["namespaceKey"]).toEqual(
741
+ new Includes(["prod"]),
742
+ );
743
+ });
744
+
745
+ /*
746
+ * The stored `defaultValue` is a single-select concept and never
747
+ * reaches a multi-select. A viewer who picks nothing — or who clears
748
+ * the popover, which is the same request on the wire — gets the
749
+ * unfiltered view the selector is showing them, not the author's
750
+ * default silently reapplied.
751
+ */
752
+ it("treats an empty multi-select as All rather than the stored default", () => {
753
+ const storedVariables: Array<JSONObject> = [
754
+ {
755
+ id: "namespace",
756
+ name: "Namespace",
757
+ type: DashboardVariableType.TelemetryAttribute,
758
+ attributeKey: "k8s.namespace.name",
759
+ isMultiSelect: true,
760
+ defaultValue: "production",
761
+ },
762
+ ];
763
+
764
+ const emptyList: PublicDashboardResourceListPolicyResult = build({
765
+ componentType: DashboardComponentType.KubernetesPodList,
766
+ storedVariables,
767
+ requestedVariables: [
768
+ {
769
+ id: "namespace",
770
+ selectedValue: null,
771
+ selectedValues: [],
772
+ },
773
+ ],
774
+ });
775
+ expect(emptyList.query["namespaceKey"]).toBeUndefined();
776
+
777
+ const omittedList: PublicDashboardResourceListPolicyResult = build({
778
+ componentType: DashboardComponentType.KubernetesPodList,
779
+ storedVariables,
780
+ requestedVariables: [{ id: "namespace", selectedValue: null }],
781
+ });
782
+ expect(omittedList.query["namespaceKey"]).toBeUndefined();
783
+
784
+ const noSelectionSent: PublicDashboardResourceListPolicyResult = build({
785
+ componentType: DashboardComponentType.KubernetesPodList,
786
+ storedVariables,
787
+ requestedVariables: [],
788
+ });
789
+ expect(noSelectionSent.query["namespaceKey"]).toBeUndefined();
735
790
  });
736
791
 
737
792
  it("ignores stale multi-value input for a stored single-select", () => {
@@ -643,3 +643,72 @@ describe("DataSourceEgressGuard.shouldBlockPrivateAddresses", () => {
643
643
  expect(verdict.blocked).toBe(false);
644
644
  });
645
645
  });
646
+
647
+ /*
648
+ * The private-address policy reads BILLING_ENABLED from process.env at call
649
+ * time (SaaS blocks private ranges; a self-hosted install with billing off
650
+ * reaches its own 10.x/192.168.x). These tests pin that call-time behaviour so
651
+ * the two policy inputs (this flag and DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES)
652
+ * stay consistent and independently controllable.
653
+ */
654
+ describe("DataSourceEgressGuard.shouldBlockPrivateAddresses - BILLING_ENABLED is read dynamically", () => {
655
+ const BILLING_ENV: string = "BILLING_ENABLED";
656
+ let savedBilling: string | undefined = undefined;
657
+
658
+ beforeEach(() => {
659
+ savedBilling = process.env[BILLING_ENV];
660
+ });
661
+
662
+ afterEach(() => {
663
+ if (savedBilling === undefined) {
664
+ delete process.env[BILLING_ENV];
665
+ } else {
666
+ process.env[BILLING_ENV] = savedBilling;
667
+ }
668
+ });
669
+
670
+ test("SaaS (BILLING_ENABLED=true) blocks private ranges by default", () => {
671
+ process.env[BILLING_ENV] = "true";
672
+ delete process.env[ENV_VAR_NAME];
673
+ expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(true);
674
+ });
675
+
676
+ test("self-hosted (BILLING_ENABLED unset) allows private ranges by default", () => {
677
+ delete process.env[BILLING_ENV];
678
+ delete process.env[ENV_VAR_NAME];
679
+ expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(false);
680
+ });
681
+
682
+ test("BILLING_ENABLED with any value other than 'true' counts as off", () => {
683
+ process.env[BILLING_ENV] = "1";
684
+ delete process.env[ENV_VAR_NAME];
685
+ expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(false);
686
+ });
687
+
688
+ test("DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES=true blocks even when billing is off", () => {
689
+ delete process.env[BILLING_ENV];
690
+ process.env[ENV_VAR_NAME] = "true";
691
+ expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(true);
692
+ });
693
+
694
+ test("checkAddress follows the billing flag when no explicit policy is given", () => {
695
+ delete process.env[ENV_VAR_NAME];
696
+
697
+ process.env[BILLING_ENV] = "true";
698
+ expect(DataSourceEgressGuard.checkAddress("10.0.0.5").blocked).toBe(true);
699
+
700
+ delete process.env[BILLING_ENV];
701
+ expect(DataSourceEgressGuard.checkAddress("10.0.0.5").blocked).toBe(false);
702
+ });
703
+
704
+ test("a self-hosted install still cannot reach loopback or metadata ranges", () => {
705
+ delete process.env[BILLING_ENV];
706
+ delete process.env[ENV_VAR_NAME];
707
+
708
+ // Always-blocked ranges ignore the billing flag entirely.
709
+ expect(DataSourceEgressGuard.checkAddress("127.0.0.1").blocked).toBe(true);
710
+ expect(DataSourceEgressGuard.checkAddress("169.254.169.254").blocked).toBe(
711
+ true,
712
+ );
713
+ });
714
+ });