@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,618 @@
|
|
|
1
|
+
import { describe, expect, test, afterEach, beforeEach } from "@jest/globals";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* WorkspaceProjectAuthTokenService.refreshAuthToken — the workspace REPOINT
|
|
5
|
+
* guard.
|
|
6
|
+
*
|
|
7
|
+
* A OneUptime project stores exactly one workspace connection per workspace
|
|
8
|
+
* type: one Microsoft tenant, one Slack team. refreshAuthToken upserts on
|
|
9
|
+
* (projectId, workspaceType) and USED TO overwrite workspaceProjectId
|
|
10
|
+
* unconditionally, so re-running the connect flow while signed into a
|
|
11
|
+
* DIFFERENT Microsoft tenant (or a different Slack team) silently repointed
|
|
12
|
+
* the project. Every existing notification rule then aimed at channels/chats
|
|
13
|
+
* that no longer resolve, and every bot conversation captured under the old
|
|
14
|
+
* workspace was orphaned — with no error to the admin and nothing in the logs.
|
|
15
|
+
*
|
|
16
|
+
* The fix selects workspaceProjectId alongside _id and refuses the write when
|
|
17
|
+
* the stored workspace differs from the incoming one, telling the admin to
|
|
18
|
+
* disconnect the existing connection first.
|
|
19
|
+
*
|
|
20
|
+
* What is pinned here:
|
|
21
|
+
*
|
|
22
|
+
* 1. The happy paths still work: no row -> create with every field set and
|
|
23
|
+
* nothing logged; matching workspaceProjectId -> updateOneById.
|
|
24
|
+
* 2. A MISMATCH throws BadDataException BEFORE any write — neither create
|
|
25
|
+
* nor updateOneById may be reached — the message names the workspace type
|
|
26
|
+
* and tells the admin to disconnect, and the log carries structured
|
|
27
|
+
* context.
|
|
28
|
+
* 3. A stored row with no workspaceProjectId has nothing to protect and must
|
|
29
|
+
* still update.
|
|
30
|
+
* 4. The guard is workspace-agnostic: Slack behaves exactly like Microsoft
|
|
31
|
+
* Teams, and the lookup is scoped to (projectId, workspaceType) so the
|
|
32
|
+
* two types cannot cross-fire.
|
|
33
|
+
* 5. THE SUBTLE REGRESSION: the findOneBy select must include
|
|
34
|
+
* workspaceProjectId. Drop that one field and findOneBy hands back a row
|
|
35
|
+
* whose workspaceProjectId is undefined, the guard never fires, and the
|
|
36
|
+
* silent repoint is back. The findOneBy mock below HONOURS the select for
|
|
37
|
+
* exactly this reason — it projects the stored row down to the columns
|
|
38
|
+
* the caller asked for, the way the database does — so reverting either
|
|
39
|
+
* half of the fix breaks the guard tests on BEHAVIOUR, not just the one
|
|
40
|
+
* white-box assertion on the select object.
|
|
41
|
+
* 6. The pre-existing argument validation is unchanged, and rejects before
|
|
42
|
+
* any database round trip.
|
|
43
|
+
* 7. One known limitation, documented deliberately rather than asserted as
|
|
44
|
+
* desirable: the guard is TENANT-scoped, not team-scoped.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import WorkspaceProjectAuthTokenService from "../../../Server/Services/WorkspaceProjectAuthTokenService";
|
|
48
|
+
import WorkspaceProjectAuthToken, {
|
|
49
|
+
MicrosoftTeamsMiscData,
|
|
50
|
+
SlackMiscData,
|
|
51
|
+
WorkspaceMiscData,
|
|
52
|
+
} from "../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
53
|
+
import WorkspaceType from "../../../Types/Workspace/WorkspaceType";
|
|
54
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
55
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
56
|
+
import Exception from "../../../Types/Exception/Exception";
|
|
57
|
+
import logger from "../../../Server/Utils/Logger";
|
|
58
|
+
|
|
59
|
+
const TEAMS_TENANT_A: string = "11111111-2222-3333-4444-555555555555";
|
|
60
|
+
const TEAMS_TENANT_B: string = "99999999-8888-7777-6666-555555555555";
|
|
61
|
+
const SLACK_TEAM_A: string = "T0AAAAAAA";
|
|
62
|
+
const SLACK_TEAM_B: string = "T0BBBBBBB";
|
|
63
|
+
|
|
64
|
+
let findOneBy: jest.SpyInstance;
|
|
65
|
+
let create: jest.SpyInstance;
|
|
66
|
+
let updateOneById: jest.SpyInstance;
|
|
67
|
+
let loggerError: jest.SpyInstance;
|
|
68
|
+
|
|
69
|
+
function teamsMiscData(
|
|
70
|
+
overrides?: Partial<MicrosoftTeamsMiscData>,
|
|
71
|
+
): MicrosoftTeamsMiscData {
|
|
72
|
+
return {
|
|
73
|
+
tenantId: TEAMS_TENANT_A,
|
|
74
|
+
teamId: "team-1",
|
|
75
|
+
teamName: "Engineering",
|
|
76
|
+
botId: "bot-1",
|
|
77
|
+
...(overrides || {}),
|
|
78
|
+
} as MicrosoftTeamsMiscData;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function slackMiscData(overrides?: Partial<SlackMiscData>): SlackMiscData {
|
|
82
|
+
return {
|
|
83
|
+
teamId: SLACK_TEAM_A,
|
|
84
|
+
teamName: "Acme",
|
|
85
|
+
botUserId: "U0BOT",
|
|
86
|
+
...(overrides || {}),
|
|
87
|
+
} as SlackMiscData;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* The full row as it sits in the database. What refreshAuthToken actually gets
|
|
92
|
+
* to see is decided by its own select — see mockStoredRow.
|
|
93
|
+
*/
|
|
94
|
+
type StoredRow = {
|
|
95
|
+
id: ObjectID;
|
|
96
|
+
projectId: ObjectID;
|
|
97
|
+
workspaceType: WorkspaceType;
|
|
98
|
+
authToken: string;
|
|
99
|
+
workspaceProjectId: string | undefined | null;
|
|
100
|
+
miscData: WorkspaceMiscData;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const SELECT_KEY_TO_ROW_KEY: Record<string, keyof StoredRow> = {
|
|
104
|
+
_id: "id",
|
|
105
|
+
id: "id",
|
|
106
|
+
projectId: "projectId",
|
|
107
|
+
workspaceType: "workspaceType",
|
|
108
|
+
authToken: "authToken",
|
|
109
|
+
workspaceProjectId: "workspaceProjectId",
|
|
110
|
+
miscData: "miscData",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function buildStoredRow(overrides?: Partial<StoredRow>): StoredRow {
|
|
114
|
+
return {
|
|
115
|
+
id: ObjectID.generate(),
|
|
116
|
+
projectId: ObjectID.generate(),
|
|
117
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
118
|
+
authToken: "stored-token",
|
|
119
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
120
|
+
miscData: teamsMiscData(),
|
|
121
|
+
...(overrides || {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* findOneBy hands back ONLY the columns the caller selected — so this
|
|
127
|
+
* projection is the whole point of the mock, not a nicety. A row that carried
|
|
128
|
+
* workspaceProjectId regardless of the select would keep the guard tests green
|
|
129
|
+
* even with `workspaceProjectId: true` deleted from production's select, which
|
|
130
|
+
* is precisely the regression this suite exists to catch.
|
|
131
|
+
*/
|
|
132
|
+
function projectOntoSelect(
|
|
133
|
+
row: StoredRow,
|
|
134
|
+
select: Record<string, boolean> | undefined,
|
|
135
|
+
): WorkspaceProjectAuthToken {
|
|
136
|
+
const selected: Record<string, boolean> = select || {};
|
|
137
|
+
const projected: Record<string, unknown> = {};
|
|
138
|
+
|
|
139
|
+
for (const selectKey of Object.keys(selected)) {
|
|
140
|
+
if (!selected[selectKey]) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const rowKey: keyof StoredRow | undefined =
|
|
145
|
+
SELECT_KEY_TO_ROW_KEY[selectKey];
|
|
146
|
+
|
|
147
|
+
if (!rowKey) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
projected[rowKey] = row[rowKey];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return projected as unknown as WorkspaceProjectAuthToken;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
type FindOneByArgs = {
|
|
158
|
+
query: Record<string, unknown>;
|
|
159
|
+
select: Record<string, boolean> | undefined;
|
|
160
|
+
props: { isRoot: boolean };
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
function mockStoredRow(overrides?: Partial<StoredRow>): StoredRow {
|
|
164
|
+
const row: StoredRow = buildStoredRow(overrides);
|
|
165
|
+
|
|
166
|
+
findOneBy.mockImplementation(
|
|
167
|
+
async (
|
|
168
|
+
findBy: FindOneByArgs,
|
|
169
|
+
): Promise<WorkspaceProjectAuthToken | null> => {
|
|
170
|
+
return projectOntoSelect(row, findBy.select);
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return row;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
type RefreshArgs = {
|
|
178
|
+
projectId: ObjectID;
|
|
179
|
+
workspaceType: WorkspaceType;
|
|
180
|
+
authToken: string;
|
|
181
|
+
workspaceProjectId: string;
|
|
182
|
+
miscData: WorkspaceMiscData;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function refreshArgs(overrides?: Partial<RefreshArgs>): RefreshArgs {
|
|
186
|
+
return {
|
|
187
|
+
projectId: ObjectID.generate(),
|
|
188
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
189
|
+
authToken: "auth-token-1",
|
|
190
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
191
|
+
miscData: teamsMiscData(),
|
|
192
|
+
...(overrides || {}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function refresh(overrides?: Partial<RefreshArgs>): Promise<void> {
|
|
197
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken(
|
|
198
|
+
refreshArgs(overrides),
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function captureError(fn: () => Promise<void>): Promise<Exception> {
|
|
203
|
+
try {
|
|
204
|
+
await fn();
|
|
205
|
+
} catch (err) {
|
|
206
|
+
return err as Exception;
|
|
207
|
+
}
|
|
208
|
+
throw new Error("Expected refreshAuthToken to throw, but it resolved.");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
findOneBy = jest.spyOn(
|
|
213
|
+
WorkspaceProjectAuthTokenService,
|
|
214
|
+
"findOneBy",
|
|
215
|
+
) as jest.SpyInstance;
|
|
216
|
+
create = jest.spyOn(
|
|
217
|
+
WorkspaceProjectAuthTokenService,
|
|
218
|
+
"create",
|
|
219
|
+
) as jest.SpyInstance;
|
|
220
|
+
updateOneById = jest.spyOn(
|
|
221
|
+
WorkspaceProjectAuthTokenService,
|
|
222
|
+
"updateOneById",
|
|
223
|
+
) as jest.SpyInstance;
|
|
224
|
+
loggerError = jest.spyOn(logger, "error") as jest.SpyInstance;
|
|
225
|
+
|
|
226
|
+
findOneBy.mockResolvedValue(null);
|
|
227
|
+
create.mockImplementation(
|
|
228
|
+
async (createBy: {
|
|
229
|
+
data: WorkspaceProjectAuthToken;
|
|
230
|
+
}): Promise<WorkspaceProjectAuthToken> => {
|
|
231
|
+
return createBy.data;
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
updateOneById.mockResolvedValue(1);
|
|
235
|
+
loggerError.mockImplementation((): void => {
|
|
236
|
+
return undefined;
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
afterEach(() => {
|
|
241
|
+
jest.restoreAllMocks();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken first connection", () => {
|
|
245
|
+
test("creates a row with every field set when the project has no connection", async () => {
|
|
246
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
247
|
+
const miscData: MicrosoftTeamsMiscData = teamsMiscData();
|
|
248
|
+
|
|
249
|
+
await refresh({
|
|
250
|
+
projectId: projectId,
|
|
251
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
252
|
+
authToken: "brand-new-token",
|
|
253
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
254
|
+
miscData: miscData,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
258
|
+
expect(updateOneById).not.toHaveBeenCalled();
|
|
259
|
+
|
|
260
|
+
const created: WorkspaceProjectAuthToken = create.mock.calls[0][0].data;
|
|
261
|
+
expect(created).toBeInstanceOf(WorkspaceProjectAuthToken);
|
|
262
|
+
expect(created.projectId?.toString()).toBe(projectId.toString());
|
|
263
|
+
expect(created.authToken).toBe("brand-new-token");
|
|
264
|
+
expect(created.workspaceType).toBe(WorkspaceType.MicrosoftTeams);
|
|
265
|
+
expect(created.workspaceProjectId).toBe(TEAMS_TENANT_A);
|
|
266
|
+
expect(created.miscData).toEqual(miscData);
|
|
267
|
+
expect(create.mock.calls[0][0].props).toEqual({ isRoot: true });
|
|
268
|
+
|
|
269
|
+
// A first connection repoints nothing, so the guard must stay quiet.
|
|
270
|
+
expect(loggerError).not.toHaveBeenCalled();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken same workspace", () => {
|
|
275
|
+
test("updates the existing row when workspaceProjectId matches", async () => {
|
|
276
|
+
const miscData: MicrosoftTeamsMiscData = teamsMiscData({
|
|
277
|
+
teamName: "Renamed Team",
|
|
278
|
+
});
|
|
279
|
+
const row: StoredRow = mockStoredRow({
|
|
280
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await refresh({
|
|
284
|
+
authToken: "rotated-token",
|
|
285
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
286
|
+
miscData: miscData,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(create).not.toHaveBeenCalled();
|
|
290
|
+
expect(updateOneById).toHaveBeenCalledTimes(1);
|
|
291
|
+
|
|
292
|
+
const updateArgs: {
|
|
293
|
+
id: ObjectID;
|
|
294
|
+
data: {
|
|
295
|
+
authToken: string;
|
|
296
|
+
workspaceProjectId: string;
|
|
297
|
+
miscData: WorkspaceMiscData;
|
|
298
|
+
};
|
|
299
|
+
props: { isRoot: boolean };
|
|
300
|
+
} = updateOneById.mock.calls[0][0];
|
|
301
|
+
expect(updateArgs.id.toString()).toBe(row.id.toString());
|
|
302
|
+
expect(updateArgs.data.authToken).toBe("rotated-token");
|
|
303
|
+
expect(updateArgs.data.workspaceProjectId).toBe(TEAMS_TENANT_A);
|
|
304
|
+
expect(updateArgs.data.miscData).toEqual(miscData);
|
|
305
|
+
expect(updateArgs.props).toEqual({ isRoot: true });
|
|
306
|
+
expect(loggerError).not.toHaveBeenCalled();
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken repoint guard", () => {
|
|
311
|
+
test("throws BadDataException when the stored workspace differs", async () => {
|
|
312
|
+
mockStoredRow({ workspaceProjectId: TEAMS_TENANT_A });
|
|
313
|
+
|
|
314
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
315
|
+
return await refresh({ workspaceProjectId: TEAMS_TENANT_B });
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("fires the guard BEFORE any write — no create, no update", async () => {
|
|
322
|
+
mockStoredRow({ workspaceProjectId: TEAMS_TENANT_A });
|
|
323
|
+
|
|
324
|
+
await captureError(async (): Promise<void> => {
|
|
325
|
+
return await refresh({ workspaceProjectId: TEAMS_TENANT_B });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
expect(create).not.toHaveBeenCalled();
|
|
329
|
+
expect(updateOneById).not.toHaveBeenCalled();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("the message tells the admin to disconnect and names the workspace type", async () => {
|
|
333
|
+
mockStoredRow({ workspaceProjectId: TEAMS_TENANT_A });
|
|
334
|
+
|
|
335
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
336
|
+
return await refresh({
|
|
337
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
338
|
+
workspaceProjectId: TEAMS_TENANT_B,
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(error.message.toLowerCase()).toContain("disconnect");
|
|
343
|
+
/*
|
|
344
|
+
* The admin-facing display name, not the enum value: "Microsoft Teams",
|
|
345
|
+
* never "MicrosoftTeams".
|
|
346
|
+
*/
|
|
347
|
+
expect(error.message).toContain("Microsoft Teams");
|
|
348
|
+
expect(error.message).not.toContain(WorkspaceType.MicrosoftTeams);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("logs the refusal with structured project and workspace context", async () => {
|
|
352
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
353
|
+
mockStoredRow({ workspaceProjectId: TEAMS_TENANT_A });
|
|
354
|
+
|
|
355
|
+
await captureError(async (): Promise<void> => {
|
|
356
|
+
return await refresh({
|
|
357
|
+
projectId: projectId,
|
|
358
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
359
|
+
workspaceProjectId: TEAMS_TENANT_B,
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(loggerError).toHaveBeenCalledTimes(1);
|
|
364
|
+
expect(loggerError.mock.calls[0][1]).toEqual({
|
|
365
|
+
projectId: projectId.toString(),
|
|
366
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("compares workspace ids exactly — a case difference is a different workspace", async () => {
|
|
371
|
+
/*
|
|
372
|
+
* The comparison is strict on purpose: a tenant id that arrives in a
|
|
373
|
+
* different case is not evidence that it is the same workspace, and
|
|
374
|
+
* refusing is the safe side of the trade.
|
|
375
|
+
*/
|
|
376
|
+
mockStoredRow({ workspaceProjectId: SLACK_TEAM_A });
|
|
377
|
+
|
|
378
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
379
|
+
return await refresh({
|
|
380
|
+
workspaceType: WorkspaceType.Slack,
|
|
381
|
+
workspaceProjectId: SLACK_TEAM_A.toLowerCase(),
|
|
382
|
+
miscData: slackMiscData(),
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
387
|
+
expect(updateOneById).not.toHaveBeenCalled();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("a stored row with no workspaceProjectId updates instead of throwing", async () => {
|
|
391
|
+
/*
|
|
392
|
+
* Rows written before workspaceProjectId was populated have nothing to
|
|
393
|
+
* protect, so the guard must not strand them — the next connect adopts
|
|
394
|
+
* whatever workspace arrives.
|
|
395
|
+
*/
|
|
396
|
+
mockStoredRow({ workspaceProjectId: null });
|
|
397
|
+
|
|
398
|
+
await expect(
|
|
399
|
+
refresh({ workspaceProjectId: TEAMS_TENANT_B }),
|
|
400
|
+
).resolves.toBeUndefined();
|
|
401
|
+
|
|
402
|
+
expect(updateOneById).toHaveBeenCalledTimes(1);
|
|
403
|
+
expect(updateOneById.mock.calls[0][0].data.workspaceProjectId).toBe(
|
|
404
|
+
TEAMS_TENANT_B,
|
|
405
|
+
);
|
|
406
|
+
expect(create).not.toHaveBeenCalled();
|
|
407
|
+
expect(loggerError).not.toHaveBeenCalled();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken guard scope", () => {
|
|
412
|
+
test("a different Teams TEAM in the SAME tenant is allowed and overwrites miscData", async () => {
|
|
413
|
+
/*
|
|
414
|
+
* DOCUMENTS CURRENT BEHAVIOUR, NOT DESIRED BEHAVIOUR.
|
|
415
|
+
*
|
|
416
|
+
* The guard is TENANT-scoped, not team-scoped: for Microsoft Teams,
|
|
417
|
+
* workspaceProjectId holds the tenant id, while the team the bot was
|
|
418
|
+
* actually installed into lives in miscData.teamId. Two different teams in
|
|
419
|
+
* one tenant are therefore indistinguishable to the guard, so this
|
|
420
|
+
* reconnect passes and replaces miscData wholesale — the old team id, name
|
|
421
|
+
* and bot id are gone, and anything captured under that team is orphaned
|
|
422
|
+
* exactly the way a cross-tenant repoint used to orphan things.
|
|
423
|
+
*
|
|
424
|
+
* If the guard is ever tightened to compare teams too, this is the test
|
|
425
|
+
* that should change.
|
|
426
|
+
*/
|
|
427
|
+
const row: StoredRow = mockStoredRow({
|
|
428
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
429
|
+
miscData: teamsMiscData({ teamId: "team-1", teamName: "Engineering" }),
|
|
430
|
+
});
|
|
431
|
+
const differentTeam: MicrosoftTeamsMiscData = teamsMiscData({
|
|
432
|
+
teamId: "team-2",
|
|
433
|
+
teamName: "Support",
|
|
434
|
+
botId: "bot-2",
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await expect(
|
|
438
|
+
refresh({
|
|
439
|
+
workspaceProjectId: TEAMS_TENANT_A,
|
|
440
|
+
miscData: differentTeam,
|
|
441
|
+
}),
|
|
442
|
+
).resolves.toBeUndefined();
|
|
443
|
+
|
|
444
|
+
expect(updateOneById).toHaveBeenCalledTimes(1);
|
|
445
|
+
expect(updateOneById.mock.calls[0][0].id.toString()).toBe(
|
|
446
|
+
row.id.toString(),
|
|
447
|
+
);
|
|
448
|
+
expect(updateOneById.mock.calls[0][0].data.miscData).toEqual(differentTeam);
|
|
449
|
+
expect(loggerError).not.toHaveBeenCalled();
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken across workspace types", () => {
|
|
454
|
+
test("Slack repoints are refused exactly like Microsoft Teams ones", async () => {
|
|
455
|
+
mockStoredRow({
|
|
456
|
+
workspaceType: WorkspaceType.Slack,
|
|
457
|
+
workspaceProjectId: SLACK_TEAM_A,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
461
|
+
return await refresh({
|
|
462
|
+
workspaceType: WorkspaceType.Slack,
|
|
463
|
+
workspaceProjectId: SLACK_TEAM_B,
|
|
464
|
+
miscData: slackMiscData({ teamId: SLACK_TEAM_B }),
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
469
|
+
expect(error.message).toContain("Slack");
|
|
470
|
+
expect(error.message).not.toContain("Microsoft Teams");
|
|
471
|
+
expect(create).not.toHaveBeenCalled();
|
|
472
|
+
expect(updateOneById).not.toHaveBeenCalled();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test("a Slack reconnect to the same team still updates", async () => {
|
|
476
|
+
mockStoredRow({
|
|
477
|
+
workspaceType: WorkspaceType.Slack,
|
|
478
|
+
workspaceProjectId: SLACK_TEAM_A,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
await refresh({
|
|
482
|
+
workspaceType: WorkspaceType.Slack,
|
|
483
|
+
workspaceProjectId: SLACK_TEAM_A,
|
|
484
|
+
miscData: slackMiscData(),
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
expect(updateOneById).toHaveBeenCalledTimes(1);
|
|
488
|
+
expect(create).not.toHaveBeenCalled();
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken lookup", () => {
|
|
493
|
+
/*
|
|
494
|
+
* Without workspaceProjectId in the select, findOneBy returns a row whose
|
|
495
|
+
* workspaceProjectId is undefined and every repoint sails through as an
|
|
496
|
+
* update. The guard tests above already fail on behaviour if that happens
|
|
497
|
+
* (the findOneBy mock honours the select); this pins the cause directly so
|
|
498
|
+
* the failure is diagnosable rather than mysterious.
|
|
499
|
+
*/
|
|
500
|
+
test("selects workspaceProjectId alongside _id so the guard can fire", async () => {
|
|
501
|
+
await refresh();
|
|
502
|
+
|
|
503
|
+
expect(findOneBy).toHaveBeenCalledTimes(1);
|
|
504
|
+
const select: { _id?: boolean; workspaceProjectId?: boolean } =
|
|
505
|
+
findOneBy.mock.calls[0][0].select;
|
|
506
|
+
expect(select.workspaceProjectId).toBe(true);
|
|
507
|
+
expect(select._id).toBe(true);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test("scopes the lookup to this project and this workspace type only", async () => {
|
|
511
|
+
/*
|
|
512
|
+
* A project connected to Microsoft Teams must still be able to connect
|
|
513
|
+
* Slack, so the row this guard compares against has to be the row for THIS
|
|
514
|
+
* workspace type — and the upsert has to key on nothing else, or a
|
|
515
|
+
* reconnect from a new workspace would find no row and create a duplicate
|
|
516
|
+
* instead of hitting the guard.
|
|
517
|
+
*/
|
|
518
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
519
|
+
|
|
520
|
+
await refresh({
|
|
521
|
+
projectId: projectId,
|
|
522
|
+
workspaceType: WorkspaceType.Slack,
|
|
523
|
+
workspaceProjectId: SLACK_TEAM_A,
|
|
524
|
+
miscData: slackMiscData(),
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const query: { projectId: ObjectID; workspaceType: WorkspaceType } =
|
|
528
|
+
findOneBy.mock.calls[0][0].query;
|
|
529
|
+
expect(query.projectId.toString()).toBe(projectId.toString());
|
|
530
|
+
expect(query.workspaceType).toBe(WorkspaceType.Slack);
|
|
531
|
+
expect(Object.keys(query).sort()).toEqual(["projectId", "workspaceType"]);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
test("reads with root props so the connect flow is not permission-scoped", async () => {
|
|
535
|
+
await refresh();
|
|
536
|
+
|
|
537
|
+
expect(findOneBy.mock.calls[0][0].props).toEqual({ isRoot: true });
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
describe("WorkspaceProjectAuthTokenService.refreshAuthToken argument validation", () => {
|
|
542
|
+
test("throws when projectId is missing", async () => {
|
|
543
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
544
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
545
|
+
...refreshArgs(),
|
|
546
|
+
projectId: undefined as unknown as ObjectID,
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
551
|
+
expect(error.message).toBe("projectId is required");
|
|
552
|
+
expect(findOneBy).not.toHaveBeenCalled();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test("throws when workspaceType is missing", async () => {
|
|
556
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
557
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
558
|
+
...refreshArgs(),
|
|
559
|
+
workspaceType: undefined as unknown as WorkspaceType,
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
564
|
+
expect(error.message).toBe("workspaceType is required");
|
|
565
|
+
expect(findOneBy).not.toHaveBeenCalled();
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
test("throws when authToken is missing", async () => {
|
|
569
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
570
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
571
|
+
...refreshArgs(),
|
|
572
|
+
authToken: "",
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
577
|
+
expect(error.message).toBe("authToken is required");
|
|
578
|
+
expect(findOneBy).not.toHaveBeenCalled();
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("throws when workspaceProjectId is missing", async () => {
|
|
582
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
583
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
584
|
+
...refreshArgs(),
|
|
585
|
+
workspaceProjectId: "",
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
590
|
+
expect(error.message).toBe("workspaceProjectId is required");
|
|
591
|
+
expect(findOneBy).not.toHaveBeenCalled();
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("throws when miscData is missing", async () => {
|
|
595
|
+
const error: Exception = await captureError(async (): Promise<void> => {
|
|
596
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
597
|
+
...refreshArgs(),
|
|
598
|
+
miscData: undefined as unknown as WorkspaceMiscData,
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
603
|
+
expect(error.message).toBe("miscData is required");
|
|
604
|
+
expect(findOneBy).not.toHaveBeenCalled();
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
test("validation rejects before any write is attempted", async () => {
|
|
608
|
+
await captureError(async (): Promise<void> => {
|
|
609
|
+
return await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
610
|
+
...refreshArgs(),
|
|
611
|
+
authToken: "",
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
expect(create).not.toHaveBeenCalled();
|
|
616
|
+
expect(updateOneById).not.toHaveBeenCalled();
|
|
617
|
+
});
|
|
618
|
+
});
|
|
@@ -15,7 +15,8 @@ import { JSONObject } from "../../../../../Types/JSON";
|
|
|
15
15
|
import ObjectID from "../../../../../Types/ObjectID";
|
|
16
16
|
import ComponentID from "../../../../../Types/Workflow/ComponentID";
|
|
17
17
|
import API from "../../../../../Utils/API";
|
|
18
|
-
import { beforeEach, describe, expect, test } from "@jest/globals";
|
|
18
|
+
import { afterAll, beforeEach, describe, expect, test } from "@jest/globals";
|
|
19
|
+
import dns from "dns";
|
|
19
20
|
|
|
20
21
|
/*
|
|
21
22
|
* The default export of Utils/API is the only thing these components talk to,
|
|
@@ -141,8 +142,25 @@ const apiComponents: Array<[string, ApiMethodName, ComponentFactory, string]> =
|
|
|
141
142
|
],
|
|
142
143
|
];
|
|
143
144
|
|
|
145
|
+
/*
|
|
146
|
+
* sanitizeArgs now runs the URL through SSRFProtection, which resolves the
|
|
147
|
+
* hostname. Pin the lookup to a public address so these tests stay offline and
|
|
148
|
+
* deterministic - what they are about is port routing, not the blocklist.
|
|
149
|
+
*/
|
|
150
|
+
const lookupSpy: jest.SpiedFunction<
|
|
151
|
+
(
|
|
152
|
+
hostname: string,
|
|
153
|
+
options: { all: true },
|
|
154
|
+
) => Promise<Array<{ address: string; family: number }>>
|
|
155
|
+
> = jest.spyOn(dns.promises, "lookup") as never;
|
|
156
|
+
|
|
144
157
|
beforeEach(() => {
|
|
145
158
|
jest.clearAllMocks();
|
|
159
|
+
lookupSpy.mockResolvedValue([{ address: "93.184.216.34", family: 4 }]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
afterAll(() => {
|
|
163
|
+
jest.restoreAllMocks();
|
|
146
164
|
});
|
|
147
165
|
|
|
148
166
|
describe.each(apiComponents)(
|
|
@@ -219,10 +237,12 @@ describe.each(apiComponents)(
|
|
|
219
237
|
url: URL;
|
|
220
238
|
data: JSONObject;
|
|
221
239
|
headers: JSONObject;
|
|
240
|
+
options: { doNotFollowRedirects?: boolean };
|
|
222
241
|
} = getApiMock(apiMethodName).mock.calls[0]![0] as {
|
|
223
242
|
url: URL;
|
|
224
243
|
data: JSONObject;
|
|
225
244
|
headers: JSONObject;
|
|
245
|
+
options: { doNotFollowRedirects?: boolean };
|
|
226
246
|
};
|
|
227
247
|
expect(request.url.toString()).toBe(TEST_URL);
|
|
228
248
|
expect(request.data).toEqual({
|
|
@@ -230,6 +250,11 @@ describe.each(apiComponents)(
|
|
|
230
250
|
name: "Airflow-Token",
|
|
231
251
|
});
|
|
232
252
|
expect(request.headers).toEqual({ apikey: "a-project-api-key" });
|
|
253
|
+
/*
|
|
254
|
+
* Redirects have to stay off, or the SSRF check sanitizeArgs just did is
|
|
255
|
+
* worth nothing: a public host can 302 the server to an internal one.
|
|
256
|
+
*/
|
|
257
|
+
expect(request.options.doNotFollowRedirects).toBe(true);
|
|
233
258
|
});
|
|
234
259
|
|
|
235
260
|
test("routes a thrown transport error to the error port with an error message", async () => {
|