@oneuptime/common 12.0.5 → 12.0.6
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/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.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/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.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/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.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 +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import ConnectorRegistry from "../../../../Server/Utils/DataSource/ConnectorRegistry";
|
|
2
|
+
import { DataSourceConnector } from "../../../../Server/Utils/DataSource/Types";
|
|
3
|
+
import DataSourceType, {
|
|
4
|
+
DataSourceTypeUtil,
|
|
5
|
+
} from "../../../../Types/DataSource/DataSourceType";
|
|
6
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
7
|
+
import { describe, expect, test } from "@jest/globals";
|
|
8
|
+
|
|
9
|
+
describe("ConnectorRegistry", () => {
|
|
10
|
+
test("registers a connector for EVERY data source type", () => {
|
|
11
|
+
/*
|
|
12
|
+
* A type added to the enum without a connector would surface as a
|
|
13
|
+
* runtime error on first query — catch it at test time instead.
|
|
14
|
+
*/
|
|
15
|
+
for (const dataSourceType of DataSourceTypeUtil.getAllDataSourceTypes()) {
|
|
16
|
+
const connector: DataSourceConnector =
|
|
17
|
+
ConnectorRegistry.getConnector(dataSourceType);
|
|
18
|
+
expect(connector.dataSourceType).toBe(dataSourceType);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("every registered connector implements the full interface", () => {
|
|
23
|
+
for (const dataSourceType of ConnectorRegistry.getRegisteredTypes()) {
|
|
24
|
+
const connector: DataSourceConnector =
|
|
25
|
+
ConnectorRegistry.getConnector(dataSourceType);
|
|
26
|
+
expect(typeof connector.testConnection).toBe("function");
|
|
27
|
+
expect(typeof connector.queryTimeSeries).toBe("function");
|
|
28
|
+
expect(typeof connector.queryTable).toBe("function");
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("throws a friendly error for an unregistered type", () => {
|
|
33
|
+
expect(() => {
|
|
34
|
+
ConnectorRegistry.getConnector("Not A Type" as DataSourceType);
|
|
35
|
+
}).toThrow(BadDataException);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { IsBillingEnabled } from "../../../../Server/EnvironmentConfig";
|
|
3
|
+
import DataSourceEgressGuard, {
|
|
4
|
+
AddressVerdict,
|
|
5
|
+
EgressResolveFunction,
|
|
6
|
+
ResolvedAddress,
|
|
7
|
+
} from "../../../../Server/Utils/DataSource/EgressGuard";
|
|
8
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
9
|
+
|
|
10
|
+
const ENV_VAR_NAME: string = "DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES";
|
|
11
|
+
|
|
12
|
+
let savedEnvValue: string | undefined = undefined;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
savedEnvValue = process.env[ENV_VAR_NAME];
|
|
16
|
+
delete process.env[ENV_VAR_NAME];
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
if (savedEnvValue === undefined) {
|
|
21
|
+
delete process.env[ENV_VAR_NAME];
|
|
22
|
+
} else {
|
|
23
|
+
process.env[ENV_VAR_NAME] = savedEnvValue;
|
|
24
|
+
}
|
|
25
|
+
jest.restoreAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type CheckFunction = (
|
|
29
|
+
address: string,
|
|
30
|
+
blockPrivateAddresses: boolean,
|
|
31
|
+
) => AddressVerdict;
|
|
32
|
+
|
|
33
|
+
const check: CheckFunction = (
|
|
34
|
+
address: string,
|
|
35
|
+
blockPrivateAddresses: boolean,
|
|
36
|
+
): AddressVerdict => {
|
|
37
|
+
return DataSourceEgressGuard.checkAddress(address, {
|
|
38
|
+
blockPrivateAddresses: blockPrivateAddresses,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
interface Resolver {
|
|
43
|
+
resolveFunction: EgressResolveFunction;
|
|
44
|
+
calls: Array<string>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type MakeResolverFunction = (addresses: Array<ResolvedAddress>) => Resolver;
|
|
48
|
+
|
|
49
|
+
const makeResolver: MakeResolverFunction = (
|
|
50
|
+
addresses: Array<ResolvedAddress>,
|
|
51
|
+
): Resolver => {
|
|
52
|
+
const calls: Array<string> = [];
|
|
53
|
+
const resolveFunction: EgressResolveFunction = (
|
|
54
|
+
hostname: string,
|
|
55
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
56
|
+
calls.push(hostname);
|
|
57
|
+
return Promise.resolve(addresses);
|
|
58
|
+
};
|
|
59
|
+
return { resolveFunction: resolveFunction, calls: calls };
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type CaptureRejectionFunction = (promise: Promise<unknown>) => Promise<Error>;
|
|
63
|
+
|
|
64
|
+
const captureRejection: CaptureRejectionFunction = async (
|
|
65
|
+
promise: Promise<unknown>,
|
|
66
|
+
): Promise<Error> => {
|
|
67
|
+
try {
|
|
68
|
+
await promise;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return error as Error;
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Expected promise to reject, but it resolved.");
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const bothFlags: Array<boolean> = [true, false];
|
|
76
|
+
|
|
77
|
+
describe("DataSourceEgressGuard.checkAddress - IPv4 always blocked", () => {
|
|
78
|
+
const alwaysBlockedIpv4: Array<[string, string]> = [
|
|
79
|
+
["127.0.0.1", "loopback address"],
|
|
80
|
+
["127.255.255.254", "loopback address"],
|
|
81
|
+
["169.254.169.254", "link-local address (cloud metadata range)"],
|
|
82
|
+
["0.0.0.0", "unspecified address"],
|
|
83
|
+
["224.0.0.1", "multicast address"],
|
|
84
|
+
["240.0.0.1", "reserved address"],
|
|
85
|
+
["255.255.255.255", "broadcast address"],
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const entry of alwaysBlockedIpv4) {
|
|
89
|
+
const address: string = entry[0];
|
|
90
|
+
const expectedReason: string = entry[1];
|
|
91
|
+
for (const flag of bothFlags) {
|
|
92
|
+
test(`${address} is blocked when blockPrivateAddresses=${flag}`, () => {
|
|
93
|
+
const verdict: AddressVerdict = check(address, flag);
|
|
94
|
+
expect(verdict.blocked).toBe(true);
|
|
95
|
+
expect(verdict.reason).toBe(expectedReason);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("DataSourceEgressGuard.checkAddress - IPv4 private ranges", () => {
|
|
102
|
+
const privateIpv4: Array<[string, string]> = [
|
|
103
|
+
["10.0.0.5", "private network address"],
|
|
104
|
+
["172.16.0.1", "private network address"],
|
|
105
|
+
["172.31.255.255", "private network address"],
|
|
106
|
+
["192.168.1.1", "private network address"],
|
|
107
|
+
["100.64.0.1", "carrier-grade NAT address"],
|
|
108
|
+
["192.0.0.10", "IETF protocol assignment address"],
|
|
109
|
+
["198.18.0.1", "benchmarking address"],
|
|
110
|
+
["192.0.2.5", "documentation address"],
|
|
111
|
+
["198.51.100.7", "documentation address"],
|
|
112
|
+
["203.0.113.9", "documentation address"],
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const entry of privateIpv4) {
|
|
116
|
+
const address: string = entry[0];
|
|
117
|
+
const expectedReason: string = entry[1];
|
|
118
|
+
|
|
119
|
+
test(`${address} is blocked when blockPrivateAddresses=true`, () => {
|
|
120
|
+
const verdict: AddressVerdict = check(address, true);
|
|
121
|
+
expect(verdict.blocked).toBe(true);
|
|
122
|
+
expect(verdict.reason).toBe(expectedReason);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test(`${address} is allowed when blockPrivateAddresses=false`, () => {
|
|
126
|
+
const verdict: AddressVerdict = check(address, false);
|
|
127
|
+
expect(verdict.blocked).toBe(false);
|
|
128
|
+
expect(verdict.reason).toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const boundaryPublicIpv4: Array<string> = [
|
|
133
|
+
"172.15.255.255",
|
|
134
|
+
"172.32.0.0",
|
|
135
|
+
"100.128.0.0",
|
|
136
|
+
"9.255.255.255",
|
|
137
|
+
"11.0.0.0",
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const address of boundaryPublicIpv4) {
|
|
141
|
+
for (const flag of bothFlags) {
|
|
142
|
+
test(`boundary address ${address} is public when blockPrivateAddresses=${flag}`, () => {
|
|
143
|
+
const verdict: AddressVerdict = check(address, flag);
|
|
144
|
+
expect(verdict.blocked).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("DataSourceEgressGuard.checkAddress - IPv4 public addresses", () => {
|
|
151
|
+
const publicIpv4: Array<string> = ["8.8.8.8", "1.1.1.1", "93.184.216.34"];
|
|
152
|
+
|
|
153
|
+
for (const address of publicIpv4) {
|
|
154
|
+
for (const flag of bothFlags) {
|
|
155
|
+
test(`${address} is allowed when blockPrivateAddresses=${flag}`, () => {
|
|
156
|
+
const verdict: AddressVerdict = check(address, flag);
|
|
157
|
+
expect(verdict.blocked).toBe(false);
|
|
158
|
+
expect(verdict.reason).toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("DataSourceEgressGuard.checkAddress - IPv6 always blocked", () => {
|
|
165
|
+
const alwaysBlockedIpv6: Array<[string, string]> = [
|
|
166
|
+
["::1", "loopback address"],
|
|
167
|
+
["::", "unspecified address"],
|
|
168
|
+
["fe80::1", "link-local address"],
|
|
169
|
+
["ff02::1", "multicast address"],
|
|
170
|
+
["::ffff:127.0.0.1", "loopback address"],
|
|
171
|
+
["64:ff9b::7f00:0001", "loopback address"],
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
for (const entry of alwaysBlockedIpv6) {
|
|
175
|
+
const address: string = entry[0];
|
|
176
|
+
const expectedReason: string = entry[1];
|
|
177
|
+
for (const flag of bothFlags) {
|
|
178
|
+
test(`${address} is blocked when blockPrivateAddresses=${flag}`, () => {
|
|
179
|
+
const verdict: AddressVerdict = check(address, flag);
|
|
180
|
+
expect(verdict.blocked).toBe(true);
|
|
181
|
+
expect(verdict.reason).toBe(expectedReason);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const flag of bothFlags) {
|
|
187
|
+
test(`zone-id address fe80::1%en0 is blocked when blockPrivateAddresses=${flag}`, () => {
|
|
188
|
+
/*
|
|
189
|
+
* Node's net.isIP rejects zone ids, so this is refused as an invalid
|
|
190
|
+
* IP rather than reaching the IPv6 range checks — blocked either way.
|
|
191
|
+
*/
|
|
192
|
+
const verdict: AddressVerdict = check("fe80::1%en0", flag);
|
|
193
|
+
expect(verdict.blocked).toBe(true);
|
|
194
|
+
expect(verdict.reason).toBeDefined();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("DataSourceEgressGuard.checkAddress - IPv6 private ranges", () => {
|
|
200
|
+
const privateIpv6: Array<[string, string]> = [
|
|
201
|
+
["fc00::1", "private network address"],
|
|
202
|
+
["fd12:3456::1", "private network address"],
|
|
203
|
+
["2001:db8::1", "documentation address"],
|
|
204
|
+
["::ffff:10.0.0.1", "private network address"],
|
|
205
|
+
["64:ff9b::0a00:0001", "private network address"],
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
for (const entry of privateIpv6) {
|
|
209
|
+
const address: string = entry[0];
|
|
210
|
+
const expectedReason: string = entry[1];
|
|
211
|
+
|
|
212
|
+
test(`${address} is blocked when blockPrivateAddresses=true`, () => {
|
|
213
|
+
const verdict: AddressVerdict = check(address, true);
|
|
214
|
+
expect(verdict.blocked).toBe(true);
|
|
215
|
+
expect(verdict.reason).toBe(expectedReason);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test(`${address} is allowed when blockPrivateAddresses=false`, () => {
|
|
219
|
+
const verdict: AddressVerdict = check(address, false);
|
|
220
|
+
expect(verdict.blocked).toBe(false);
|
|
221
|
+
expect(verdict.reason).toBeUndefined();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const flag of bothFlags) {
|
|
226
|
+
test(`public IPv6 2607:f8b0::1 is allowed when blockPrivateAddresses=${flag}`, () => {
|
|
227
|
+
const verdict: AddressVerdict = check("2607:f8b0::1", flag);
|
|
228
|
+
expect(verdict.blocked).toBe(false);
|
|
229
|
+
expect(verdict.reason).toBeUndefined();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("DataSourceEgressGuard.checkAddress - invalid input", () => {
|
|
235
|
+
const garbageAddresses: Array<string> = ["not-an-ip", "1.2.3.4.5"];
|
|
236
|
+
|
|
237
|
+
for (const address of garbageAddresses) {
|
|
238
|
+
for (const flag of bothFlags) {
|
|
239
|
+
test(`${address} is blocked as invalid when blockPrivateAddresses=${flag}`, () => {
|
|
240
|
+
const verdict: AddressVerdict = check(address, flag);
|
|
241
|
+
expect(verdict.blocked).toBe(true);
|
|
242
|
+
expect(verdict.reason).toBe("not a valid IP address");
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe("DataSourceEgressGuard.assertHostnameAllowed", () => {
|
|
249
|
+
test("returns the literal IPv4 address without calling the resolver", async () => {
|
|
250
|
+
const resolver: Resolver = makeResolver([
|
|
251
|
+
{ address: "203.0.113.10", family: 4 },
|
|
252
|
+
]);
|
|
253
|
+
const addresses: Array<ResolvedAddress> =
|
|
254
|
+
await DataSourceEgressGuard.assertHostnameAllowed("8.8.8.8", {
|
|
255
|
+
blockPrivateAddresses: true,
|
|
256
|
+
resolveFunction: resolver.resolveFunction,
|
|
257
|
+
});
|
|
258
|
+
expect(addresses).toEqual([{ address: "8.8.8.8", family: 4 }]);
|
|
259
|
+
expect(resolver.calls).toHaveLength(0);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("returns the literal IPv6 address with family 6", async () => {
|
|
263
|
+
const addresses: Array<ResolvedAddress> =
|
|
264
|
+
await DataSourceEgressGuard.assertHostnameAllowed("2607:f8b0::1", {
|
|
265
|
+
blockPrivateAddresses: true,
|
|
266
|
+
});
|
|
267
|
+
expect(addresses).toEqual([{ address: "2607:f8b0::1", family: 6 }]);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("strips brackets from a bracketed IPv6 literal", async () => {
|
|
271
|
+
const addresses: Array<ResolvedAddress> =
|
|
272
|
+
await DataSourceEgressGuard.assertHostnameAllowed("[2607:f8b0::1]", {
|
|
273
|
+
blockPrivateAddresses: true,
|
|
274
|
+
});
|
|
275
|
+
expect(addresses).toEqual([{ address: "2607:f8b0::1", family: 6 }]);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("throws BadDataException for a blocked literal IPv4", async () => {
|
|
279
|
+
const error: Error = await captureRejection(
|
|
280
|
+
DataSourceEgressGuard.assertHostnameAllowed("169.254.169.254", {
|
|
281
|
+
blockPrivateAddresses: false,
|
|
282
|
+
}),
|
|
283
|
+
);
|
|
284
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
285
|
+
expect(error.message).toContain("169.254.169.254");
|
|
286
|
+
expect(error.message).toContain("link-local");
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("throws BadDataException for bracketed [::1]", async () => {
|
|
290
|
+
const error: Error = await captureRejection(
|
|
291
|
+
DataSourceEgressGuard.assertHostnameAllowed("[::1]", {
|
|
292
|
+
blockPrivateAddresses: false,
|
|
293
|
+
}),
|
|
294
|
+
);
|
|
295
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
296
|
+
expect(error.message).toContain("::1");
|
|
297
|
+
expect(error.message).toContain("loopback");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("allows a literal private IPv4 when blockPrivateAddresses is false", async () => {
|
|
301
|
+
const addresses: Array<ResolvedAddress> =
|
|
302
|
+
await DataSourceEgressGuard.assertHostnameAllowed("10.0.0.5", {
|
|
303
|
+
blockPrivateAddresses: false,
|
|
304
|
+
});
|
|
305
|
+
expect(addresses).toEqual([{ address: "10.0.0.5", family: 4 }]);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test("throws BadDataException for a literal private IPv4 when the flag is on", async () => {
|
|
309
|
+
const error: Error = await captureRejection(
|
|
310
|
+
DataSourceEgressGuard.assertHostnameAllowed("10.0.0.5", {
|
|
311
|
+
blockPrivateAddresses: true,
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
314
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
315
|
+
expect(error.message).toContain("10.0.0.5");
|
|
316
|
+
expect(error.message).toContain("private network");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("resolves a hostname through the injected resolver and returns all public addresses", async () => {
|
|
320
|
+
const resolvedAddresses: Array<ResolvedAddress> = [
|
|
321
|
+
{ address: "93.184.216.34", family: 4 },
|
|
322
|
+
{ address: "2607:f8b0::1", family: 6 },
|
|
323
|
+
];
|
|
324
|
+
const resolver: Resolver = makeResolver(resolvedAddresses);
|
|
325
|
+
const addresses: Array<ResolvedAddress> =
|
|
326
|
+
await DataSourceEgressGuard.assertHostnameAllowed("db.example.com", {
|
|
327
|
+
blockPrivateAddresses: true,
|
|
328
|
+
resolveFunction: resolver.resolveFunction,
|
|
329
|
+
});
|
|
330
|
+
expect(addresses).toEqual(resolvedAddresses);
|
|
331
|
+
expect(resolver.calls).toEqual(["db.example.com"]);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test("throws when ANY resolved address is private and the flag is on", async () => {
|
|
335
|
+
const resolver: Resolver = makeResolver([
|
|
336
|
+
{ address: "93.184.216.34", family: 4 },
|
|
337
|
+
{ address: "10.0.0.5", family: 4 },
|
|
338
|
+
]);
|
|
339
|
+
const error: Error = await captureRejection(
|
|
340
|
+
DataSourceEgressGuard.assertHostnameAllowed("db.example.com", {
|
|
341
|
+
blockPrivateAddresses: true,
|
|
342
|
+
resolveFunction: resolver.resolveFunction,
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
346
|
+
expect(error.message).toContain("db.example.com");
|
|
347
|
+
expect(error.message).toContain("10.0.0.5");
|
|
348
|
+
expect(error.message).toContain("private network");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("allows a mixed public/private answer when the flag is off", async () => {
|
|
352
|
+
const resolver: Resolver = makeResolver([
|
|
353
|
+
{ address: "93.184.216.34", family: 4 },
|
|
354
|
+
{ address: "10.0.0.5", family: 4 },
|
|
355
|
+
]);
|
|
356
|
+
const addresses: Array<ResolvedAddress> =
|
|
357
|
+
await DataSourceEgressGuard.assertHostnameAllowed("db.example.com", {
|
|
358
|
+
blockPrivateAddresses: false,
|
|
359
|
+
resolveFunction: resolver.resolveFunction,
|
|
360
|
+
});
|
|
361
|
+
expect(addresses).toHaveLength(2);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("always blocks a hostname resolving to loopback even with the flag off", async () => {
|
|
365
|
+
const resolver: Resolver = makeResolver([
|
|
366
|
+
{ address: "127.0.0.1", family: 4 },
|
|
367
|
+
]);
|
|
368
|
+
const error: Error = await captureRejection(
|
|
369
|
+
DataSourceEgressGuard.assertHostnameAllowed("evil.example.com", {
|
|
370
|
+
blockPrivateAddresses: false,
|
|
371
|
+
resolveFunction: resolver.resolveFunction,
|
|
372
|
+
}),
|
|
373
|
+
);
|
|
374
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
375
|
+
expect(error.message).toContain("evil.example.com");
|
|
376
|
+
expect(error.message).toContain("127.0.0.1");
|
|
377
|
+
expect(error.message).toContain("loopback");
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test("throws when the resolver returns no addresses", async () => {
|
|
381
|
+
const resolver: Resolver = makeResolver([]);
|
|
382
|
+
const error: Error = await captureRejection(
|
|
383
|
+
DataSourceEgressGuard.assertHostnameAllowed("db.example.com", {
|
|
384
|
+
blockPrivateAddresses: true,
|
|
385
|
+
resolveFunction: resolver.resolveFunction,
|
|
386
|
+
}),
|
|
387
|
+
);
|
|
388
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
389
|
+
expect(error.message).toContain(
|
|
390
|
+
"Could not resolve data source host db.example.com",
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("surfaces resolver rejections as BadDataException naming the host", async () => {
|
|
395
|
+
const resolveFunction: EgressResolveFunction = (
|
|
396
|
+
_hostname: string,
|
|
397
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
398
|
+
return Promise.reject(new Error("getaddrinfo ENOTFOUND"));
|
|
399
|
+
};
|
|
400
|
+
const error: Error = await captureRejection(
|
|
401
|
+
DataSourceEgressGuard.assertHostnameAllowed("missing.example.com", {
|
|
402
|
+
blockPrivateAddresses: true,
|
|
403
|
+
resolveFunction: resolveFunction,
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
407
|
+
expect(error.message).toContain("missing.example.com");
|
|
408
|
+
expect(error.message).toContain("getaddrinfo ENOTFOUND");
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test("stringifies non-Error resolver rejections", async () => {
|
|
412
|
+
const resolveFunction: EgressResolveFunction = (
|
|
413
|
+
_hostname: string,
|
|
414
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
415
|
+
return Promise.reject("plain-string-failure");
|
|
416
|
+
};
|
|
417
|
+
const error: Error = await captureRejection(
|
|
418
|
+
DataSourceEgressGuard.assertHostnameAllowed("missing.example.com", {
|
|
419
|
+
blockPrivateAddresses: true,
|
|
420
|
+
resolveFunction: resolveFunction,
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
424
|
+
expect(error.message).toContain("plain-string-failure");
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("throws when the hostname is empty", async () => {
|
|
428
|
+
const error: Error = await captureRejection(
|
|
429
|
+
DataSourceEgressGuard.assertHostnameAllowed("", {
|
|
430
|
+
blockPrivateAddresses: true,
|
|
431
|
+
}),
|
|
432
|
+
);
|
|
433
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
434
|
+
expect(error.message).toContain("Data source host is required");
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe("DataSourceEgressGuard.assertUrlAllowed", () => {
|
|
439
|
+
test("rejects ftp:// URLs", async () => {
|
|
440
|
+
const error: Error = await captureRejection(
|
|
441
|
+
DataSourceEgressGuard.assertUrlAllowed("ftp://example.com/file", {
|
|
442
|
+
blockPrivateAddresses: true,
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
446
|
+
expect(error.message).toContain("http or https");
|
|
447
|
+
expect(error.message).toContain("ftp");
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("rejects file:// URLs", async () => {
|
|
451
|
+
const error: Error = await captureRejection(
|
|
452
|
+
DataSourceEgressGuard.assertUrlAllowed("file:///etc/passwd", {
|
|
453
|
+
blockPrivateAddresses: true,
|
|
454
|
+
}),
|
|
455
|
+
);
|
|
456
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
457
|
+
expect(error.message).toContain("http or https");
|
|
458
|
+
expect(error.message).toContain("file");
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("rejects an unparseable URL string", async () => {
|
|
462
|
+
const error: Error = await captureRejection(
|
|
463
|
+
DataSourceEgressGuard.assertUrlAllowed("not a valid url", {
|
|
464
|
+
blockPrivateAddresses: true,
|
|
465
|
+
}),
|
|
466
|
+
);
|
|
467
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
468
|
+
expect(error.message).toContain("Invalid data source URL");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("rejects an empty URL string", async () => {
|
|
472
|
+
const error: Error = await captureRejection(
|
|
473
|
+
DataSourceEgressGuard.assertUrlAllowed("", {
|
|
474
|
+
blockPrivateAddresses: true,
|
|
475
|
+
}),
|
|
476
|
+
);
|
|
477
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
478
|
+
expect(error.message).toContain("Data source URL is required");
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test("allows an http URL and validates the extracted hostname via the resolver", async () => {
|
|
482
|
+
const resolvedAddresses: Array<ResolvedAddress> = [
|
|
483
|
+
{ address: "93.184.216.34", family: 4 },
|
|
484
|
+
];
|
|
485
|
+
const resolver: Resolver = makeResolver(resolvedAddresses);
|
|
486
|
+
const result: { url: URL; addresses: Array<ResolvedAddress> } =
|
|
487
|
+
await DataSourceEgressGuard.assertUrlAllowed("http://api.example.com", {
|
|
488
|
+
blockPrivateAddresses: true,
|
|
489
|
+
resolveFunction: resolver.resolveFunction,
|
|
490
|
+
});
|
|
491
|
+
expect(result.url.hostname).toBe("api.example.com");
|
|
492
|
+
expect(result.addresses).toEqual(resolvedAddresses);
|
|
493
|
+
expect(resolver.calls).toEqual(["api.example.com"]);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("preserves port, path and query in the returned URL", async () => {
|
|
497
|
+
const resolver: Resolver = makeResolver([
|
|
498
|
+
{ address: "93.184.216.34", family: 4 },
|
|
499
|
+
]);
|
|
500
|
+
const result: { url: URL; addresses: Array<ResolvedAddress> } =
|
|
501
|
+
await DataSourceEgressGuard.assertUrlAllowed(
|
|
502
|
+
"https://api.example.com:8443/v1/data?limit=10&from=abc",
|
|
503
|
+
{
|
|
504
|
+
blockPrivateAddresses: true,
|
|
505
|
+
resolveFunction: resolver.resolveFunction,
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
expect(result.url.protocol).toBe("https:");
|
|
509
|
+
expect(result.url.port).toBe("8443");
|
|
510
|
+
expect(result.url.pathname).toBe("/v1/data");
|
|
511
|
+
expect(result.url.search).toBe("?limit=10&from=abc");
|
|
512
|
+
expect(result.url.href).toBe(
|
|
513
|
+
"https://api.example.com:8443/v1/data?limit=10&from=abc",
|
|
514
|
+
);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("rejects a URL whose host resolves to a private address when the flag is on", async () => {
|
|
518
|
+
const resolver: Resolver = makeResolver([
|
|
519
|
+
{ address: "192.168.1.1", family: 4 },
|
|
520
|
+
]);
|
|
521
|
+
const error: Error = await captureRejection(
|
|
522
|
+
DataSourceEgressGuard.assertUrlAllowed("https://internal.example.com", {
|
|
523
|
+
blockPrivateAddresses: true,
|
|
524
|
+
resolveFunction: resolver.resolveFunction,
|
|
525
|
+
}),
|
|
526
|
+
);
|
|
527
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
528
|
+
expect(error.message).toContain("internal.example.com");
|
|
529
|
+
expect(error.message).toContain("192.168.1.1");
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("always rejects the cloud metadata endpoint as a literal URL host", async () => {
|
|
533
|
+
const error: Error = await captureRejection(
|
|
534
|
+
DataSourceEgressGuard.assertUrlAllowed(
|
|
535
|
+
"http://169.254.169.254/latest/meta-data",
|
|
536
|
+
{ blockPrivateAddresses: false },
|
|
537
|
+
),
|
|
538
|
+
);
|
|
539
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
540
|
+
expect(error.message).toContain("169.254.169.254");
|
|
541
|
+
expect(error.message).toContain("link-local");
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
test("rejects a bracketed IPv6 loopback URL host", async () => {
|
|
545
|
+
const error: Error = await captureRejection(
|
|
546
|
+
DataSourceEgressGuard.assertUrlAllowed("http://[::1]:8080/status", {
|
|
547
|
+
blockPrivateAddresses: false,
|
|
548
|
+
}),
|
|
549
|
+
);
|
|
550
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
551
|
+
expect(error.message).toContain("loopback");
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
describe("DataSourceEgressGuard.assertHostnameAllowed - numeric hostname encodings", () => {
|
|
556
|
+
/*
|
|
557
|
+
* These hostnames are not IP literals to net.isIP, so they take the
|
|
558
|
+
* RESOLVER path — and getaddrinfo parses numeric encodings of 127.0.0.1
|
|
559
|
+
* without any network I/O, which is exactly the classic SSRF filter
|
|
560
|
+
* bypass. No resolveFunction is injected: the point is to prove the real
|
|
561
|
+
* system resolver path catches them. blockPrivateAddresses is OFF so the
|
|
562
|
+
* block can only come from the always-on loopback rule (or from the form
|
|
563
|
+
* failing to resolve on this platform — either way the call must throw).
|
|
564
|
+
*/
|
|
565
|
+
const numericLoopbackEncodings: Array<[string, string]> = [
|
|
566
|
+
["2130706433", "decimal encoding of 127.0.0.1"],
|
|
567
|
+
["0x7f000001", "hex encoding of 127.0.0.1"],
|
|
568
|
+
["017700000001", "octal encoding of 127.0.0.1"],
|
|
569
|
+
["0x7f.0.0.1", "mixed hex/dotted encoding of 127.0.0.1"],
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
for (const entry of numericLoopbackEncodings) {
|
|
573
|
+
const hostname: string = entry[0];
|
|
574
|
+
const label: string = entry[1];
|
|
575
|
+
test(`${hostname} (${label}) is rejected through the real system resolver`, async () => {
|
|
576
|
+
const error: Error = await captureRejection(
|
|
577
|
+
DataSourceEgressGuard.assertHostnameAllowed(hostname, {
|
|
578
|
+
blockPrivateAddresses: false,
|
|
579
|
+
}),
|
|
580
|
+
);
|
|
581
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
582
|
+
expect(error.message).toContain(hostname);
|
|
583
|
+
/*
|
|
584
|
+
* getaddrinfo variance: on platforms where the form parses, it
|
|
585
|
+
* resolves to 127.0.0.1 and is blocked as loopback; where it does
|
|
586
|
+
* not parse, resolution fails. Both are rejections.
|
|
587
|
+
*/
|
|
588
|
+
expect(error.message).toMatch(
|
|
589
|
+
/loopback address|Could not resolve data source host/,
|
|
590
|
+
);
|
|
591
|
+
}, 15000);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
for (const flag of bothFlags) {
|
|
595
|
+
test(`expanded IPv6 loopback 0:0:0:0:0:0:0:1 is blocked when blockPrivateAddresses=${flag}`, async () => {
|
|
596
|
+
/*
|
|
597
|
+
* The unabbreviated form of ::1 IS an IP literal, so it takes the
|
|
598
|
+
* literal path — but must land on the same loopback verdict.
|
|
599
|
+
*/
|
|
600
|
+
const error: Error = await captureRejection(
|
|
601
|
+
DataSourceEgressGuard.assertHostnameAllowed("0:0:0:0:0:0:0:1", {
|
|
602
|
+
blockPrivateAddresses: flag,
|
|
603
|
+
}),
|
|
604
|
+
);
|
|
605
|
+
expect(error instanceof BadDataException).toBe(true);
|
|
606
|
+
expect(error.message).toContain("0:0:0:0:0:0:0:1");
|
|
607
|
+
expect(error.message).toContain("loopback");
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
describe("DataSourceEgressGuard.shouldBlockPrivateAddresses", () => {
|
|
613
|
+
test("returns true when DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES=true", () => {
|
|
614
|
+
process.env[ENV_VAR_NAME] = "true";
|
|
615
|
+
expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(true);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
test("falls back to the billing flag when the env var is unset", () => {
|
|
619
|
+
delete process.env[ENV_VAR_NAME];
|
|
620
|
+
expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(
|
|
621
|
+
IsBillingEnabled,
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test("treats any value other than 'true' as unset", () => {
|
|
626
|
+
process.env[ENV_VAR_NAME] = "false";
|
|
627
|
+
expect(DataSourceEgressGuard.shouldBlockPrivateAddresses()).toBe(
|
|
628
|
+
IsBillingEnabled,
|
|
629
|
+
);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test("checkAddress derives the policy from the env var when options omit the flag", () => {
|
|
633
|
+
process.env[ENV_VAR_NAME] = "true";
|
|
634
|
+
const verdict: AddressVerdict =
|
|
635
|
+
DataSourceEgressGuard.checkAddress("10.0.0.5");
|
|
636
|
+
expect(verdict.blocked).toBe(true);
|
|
637
|
+
expect(verdict.reason).toBe("private network address");
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
test("an explicit blockPrivateAddresses=false overrides the env var", () => {
|
|
641
|
+
process.env[ENV_VAR_NAME] = "true";
|
|
642
|
+
const verdict: AddressVerdict = check("10.0.0.5", false);
|
|
643
|
+
expect(verdict.blocked).toBe(false);
|
|
644
|
+
});
|
|
645
|
+
});
|