@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.
- package/Models/DatabaseModels/AIInsight.ts +1 -1
- package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
- package/Models/DatabaseModels/DeletedProject.ts +595 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Monitor.ts +111 -0
- package/Models/DatabaseModels/Project.ts +3 -3
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
- package/Server/API/FileAPI.ts +15 -2
- package/Server/API/MicrosoftTeamsAPI.ts +6 -1
- package/Server/API/ProjectAPI.ts +214 -0
- package/Server/API/StatusPageAPI.ts +39 -6
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AIAgentService.ts +20 -0
- package/Server/Services/DashboardDomainService.ts +73 -28
- package/Server/Services/DatabaseService.ts +1 -0
- package/Server/Services/DeletedProjectService.ts +138 -0
- package/Server/Services/FileService.ts +61 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
- package/Server/Services/IncidentPublicNoteService.ts +18 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +311 -0
- package/Server/Services/ProbeService.ts +20 -0
- package/Server/Services/ProjectService.ts +297 -1
- package/Server/Services/PushNotificationService.ts +71 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
- package/Server/Services/StatusPageAnnouncementService.ts +53 -0
- package/Server/Services/StatusPageDomainService.ts +73 -28
- package/Server/Services/TeamMemberService.ts +117 -12
- package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
- package/Server/Services/UserWebhookService.ts +34 -109
- package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
- package/Server/Types/Database/DeleteById.ts +2 -0
- package/Server/Types/Database/DeleteOneBy.ts +6 -0
- package/Server/Types/Domain.ts +41 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
- package/Server/Types/Workflow/Components/API/Get.ts +2 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
- package/Server/Types/Workflow/Components/API/Post.ts +2 -1
- package/Server/Types/Workflow/Components/API/Put.ts +2 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
- package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
- package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
- package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
- package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
- package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
- package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
- package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
- package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
- package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
- package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
- package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
- package/Server/Utils/DataSource/EgressGuard.ts +176 -16
- package/Server/Utils/DataSource/HttpFetch.ts +6 -45
- package/Server/Utils/Execute.ts +8 -0
- package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
- package/Server/Utils/LLM/LLMService.ts +57 -12
- package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
- package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
- package/Server/Utils/Monitor/MonitorResource.ts +36 -0
- package/Server/Utils/Response.ts +142 -6
- package/Server/Utils/SSRFProtection.ts +273 -60
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/VM/VMRunner.ts +88 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
- package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
- package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
- package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
- package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
- package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
- package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
- package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
- package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
- package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
- package/Tests/Server/API/ProjectAPI.test.ts +299 -1
- package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
- package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
- package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
- package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
- package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
- package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
- package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
- package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
- package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
- package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
- package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
- package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
- package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
- package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
- package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
- package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
- package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
- package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
- package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
- package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
- package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
- package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
- package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
- package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
- package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
- package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
- package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
- package/Tests/Types/JSONFunctions.test.ts +252 -1
- package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
- package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
- package/Tests/UI/Components/404.test.tsx +1 -1
- package/Tests/UI/Components/Alert.test.tsx +1 -1
- package/Tests/UI/Components/Badge.test.tsx +1 -1
- package/Tests/UI/Components/BasicForm.test.tsx +39 -10
- package/Tests/UI/Components/Button.test.tsx +1 -1
- package/Tests/UI/Components/Card.test.tsx +1 -1
- package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
- package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
- package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
- package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
- package/Tests/UI/Components/Dropdown.test.tsx +1 -1
- package/Tests/UI/Components/FilePicker.test.tsx +1 -1
- package/Tests/UI/Components/HiddenText.test.tsx +1 -1
- package/Tests/UI/Components/Input.test.tsx +1 -1
- package/Tests/UI/Components/Loader.test.tsx +1 -1
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/MasterPage.test.tsx +1 -1
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
- package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
- package/Tests/UI/Components/NavBar.test.tsx +1 -1
- package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
- package/Tests/UI/Components/Pagination.test.tsx +1 -1
- package/Tests/UI/Components/Pill.test.tsx +1 -1
- package/Tests/UI/Components/Probe.test.tsx +1 -1
- package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
- package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
- package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
- package/Tests/UI/Components/SideOver.test.tsx +500 -6
- package/Tests/UI/Components/Tabs.test.tsx +1 -1
- package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
- package/Tests/UI/Components/TextArea.test.tsx +1 -1
- package/Tests/UI/Components/Toast.test.tsx +1 -1
- package/Tests/UI/Components/Toggle.test.tsx +1 -1
- package/Tests/UI/Components/TopSection.test.tsx +1 -1
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
- package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
- package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
- package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
- package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
- package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
- package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
- package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
- package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
- package/Tests/Utils/Realtime.test.ts +78 -0
- package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
- package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
- package/Types/AI/FixPullRequestCiStatus.ts +1 -1
- package/Types/AI/FixVerificationStatus.ts +2 -2
- package/Types/API/Hostname.ts +92 -4
- package/Types/API/URL.ts +43 -25
- package/Types/Billing/BalanceAdjustmentType.ts +14 -0
- package/Types/Billing/ProjectBalanceType.ts +13 -0
- package/Types/Domain.ts +26 -0
- package/Types/File/MimeType.ts +11 -0
- package/Types/JSONFunctions.ts +15 -0
- package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
- package/Types/Workflow/Components/BaseModel.ts +92 -28
- package/Typings/Index.d.ts +12 -0
- package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
- package/UI/Components/Modal/ConfirmModal.tsx +10 -5
- package/UI/Components/Modal/Modal.tsx +27 -47
- package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
- package/UI/Components/SideOver/SideOver.tsx +195 -68
- package/UI/Utils/PageScrollLock.ts +66 -0
- package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
- package/Utils/Dashboard/VariableInterpolation.ts +19 -1
- package/Utils/LegacySerializedArray.ts +60 -0
- package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
- package/Utils/Telemetry/SavedViewFilters.ts +88 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
- package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +3 -3
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/FileAPI.js +14 -2
- package/build/dist/Server/API/FileAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +157 -4
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +27 -3
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +17 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +55 -18
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +1 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DeletedProjectService.js +124 -0
- package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
- package/build/dist/Server/Services/FileService.js +52 -0
- package/build/dist/Server/Services/FileService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +232 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +17 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +228 -6
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +56 -0
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
- package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +97 -6
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +31 -87
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +37 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
- package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
- package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
- package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
- package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
- package/build/dist/Server/Utils/Execute.js +2 -2
- package/build/dist/Server/Utils/Execute.js.map +1 -1
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
- package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
- package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +112 -5
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +219 -53
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
- package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
- package/build/dist/Types/API/Hostname.js +73 -4
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/API/URL.js +42 -24
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
- package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
- package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
- package/build/dist/Types/Domain.js +22 -0
- package/build/dist/Types/Domain.js.map +1 -1
- package/build/dist/Types/File/MimeType.js +11 -0
- package/build/dist/Types/File/MimeType.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +14 -0
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
- package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +24 -34
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Utils/PageScrollLock.js +52 -0
- package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
- package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
- package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
- package/build/dist/Utils/LegacySerializedArray.js +51 -0
- package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
- package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
- package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
- package/jest.config.json +3 -0
- package/package.json +1 -1
- 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
|
|
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
|
|
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
|
-
|
|
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"]).
|
|
726
|
+
expect(forgedScalar.query["namespaceKey"]).toBeUndefined();
|
|
721
727
|
|
|
722
|
-
const
|
|
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:
|
|
730
|
-
selectedValues: [],
|
|
735
|
+
selectedValue: "kube-system",
|
|
736
|
+
selectedValues: ["prod"],
|
|
731
737
|
},
|
|
732
738
|
],
|
|
733
739
|
});
|
|
734
|
-
expect(
|
|
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
|
+
});
|