@oneuptime/common 13.0.0 → 13.0.1
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/Alert.ts +55 -0
- package/Models/DatabaseModels/CloudResource.ts +45 -7
- package/Models/DatabaseModels/CodeRepository.ts +85 -0
- package/Models/DatabaseModels/GoogleSecOpsConnection.ts +57 -7
- package/Models/DatabaseModels/GoogleSecOpsConnectionRun.ts +263 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/Index.ts +16 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
- package/Models/DatabaseModels/User.ts +51 -2
- package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +43 -2
- package/Models/DatabaseModels/UserWebAuthn.ts +17 -0
- package/Models/DatabaseModels/VMwareResource.ts +1149 -0
- package/Models/DatabaseModels/VMwareVCenter.ts +1122 -0
- package/Models/DatabaseModels/VMwareVCenterFeed.ts +625 -0
- package/Models/DatabaseModels/VMwareVCenterLabelRule.ts +514 -0
- package/Models/DatabaseModels/VMwareVCenterOwnerRule.ts +596 -0
- package/Models/DatabaseModels/VMwareVCenterOwnerTeam.ts +485 -0
- package/Models/DatabaseModels/VMwareVCenterOwnerUser.ts +484 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +22 -1
- package/Server/API/AIAgentDataAPI.ts +79 -4
- package/Server/API/AIAgentGitHubAPI.ts +523 -0
- package/Server/API/AIChatAPI.ts +20 -1
- package/Server/API/BillingAPI.ts +33 -0
- package/Server/API/DashboardAPI.ts +10 -0
- package/Server/API/GitHubAPI.ts +47 -5
- package/Server/API/GoogleSecOpsConnectionAPI.ts +79 -0
- package/Server/API/UserWebAuthnAPI.ts +21 -1
- package/Server/API/VMwareResourceAPI.ts +143 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.ts +636 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.ts +83 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Middleware/IdentityRateLimit.ts +101 -0
- package/Server/Services/AIRunService.ts +13 -0
- package/Server/Services/BillingPaymentMethodService.ts +99 -28
- package/Server/Services/BillingService.ts +287 -20
- package/Server/Services/CloudResourceInstanceService.ts +70 -0
- package/Server/Services/ExceptionAggregationService.ts +1 -0
- package/Server/Services/GoogleSecOpsConnectionRunService.ts +10 -0
- package/Server/Services/GoogleSecOpsConnectionService.ts +12 -0
- package/Server/Services/Index.ts +16 -0
- package/Server/Services/LogAggregationService.ts +1 -0
- package/Server/Services/MetricAggregationService.ts +1 -0
- package/Server/Services/MobilePasskeyLoginService.ts +216 -0
- package/Server/Services/MonitorService.ts +51 -1
- package/Server/Services/MonitorTemplateService.ts +91 -29
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +132 -1
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +176 -2
- package/Server/Services/OpenTelemetryIngestService.ts +17 -0
- package/Server/Services/PayAsYouGoBillingService.ts +446 -0
- package/Server/Services/ProjectService.ts +46 -2
- package/Server/Services/ServerlessFunctionInstanceService.ts +71 -0
- package/Server/Services/TeamMemberService.ts +96 -36
- package/Server/Services/TelemetryIngestionKeyService.ts +80 -10
- package/Server/Services/TelemetryUsageBillingService.ts +95 -5
- package/Server/Services/TraceAggregationService.ts +5 -4
- package/Server/Services/UserWebAuthnService.ts +592 -133
- package/Server/Services/VMwareResourceService.ts +648 -0
- package/Server/Services/VMwareVCenterFeedService.ts +99 -0
- package/Server/Services/VMwareVCenterLabelRuleEngineService.ts +240 -0
- package/Server/Services/VMwareVCenterLabelRuleService.ts +14 -0
- package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +252 -0
- package/Server/Services/VMwareVCenterOwnerRuleService.ts +14 -0
- package/Server/Services/VMwareVCenterOwnerTeamService.ts +139 -0
- package/Server/Services/VMwareVCenterOwnerUserService.ts +133 -0
- package/Server/Services/VMwareVCenterService.ts +629 -0
- package/Server/Services/WorkflowVariableService.ts +203 -0
- package/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.ts +17 -0
- package/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.ts +31 -1
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +7 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.ts +341 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.ts +274 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.ts +375 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubConversation.ts +846 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.ts +360 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubRunReply.ts +421 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.ts +140 -0
- package/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.ts +906 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +143 -0
- package/Server/Utils/DataSource/EgressGuard.ts +160 -15
- package/Server/Utils/Database/TruncateColumnValue.ts +1 -1
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +2 -0
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +33 -0
- package/Server/Utils/EmailRollup/EmailRollupWriter.ts +44 -0
- package/Server/Utils/LogRedaction.ts +2 -1
- package/Server/Utils/Monitor/MonitorAlert.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +302 -3
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +26 -4
- package/Server/Utils/Monitor/MonitorResourceContext.ts +5 -3
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +14 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +26 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +28 -5
- package/Server/Utils/ResourceFeed/ResourceFeedUtil.ts +3 -3
- package/Server/Utils/SecurityEvent/ConnectorErrorMessage.ts +14 -9
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +116 -84
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +594 -195
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +466 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +5 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +2 -1
- package/Server/Utils/Telemetry/TelemetryEntity.ts +181 -1
- package/Server/Utils/Telemetry/VMwareSnapshotScan.ts +1352 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +529 -0
- package/Tests/App/Accounts/MobilePasskey.test.tsx +620 -0
- package/Tests/App/Accounts/PasswordRequirements.test.tsx +254 -0
- package/Tests/App/Accounts/RegisterPassword.test.tsx +553 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +224 -0
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +432 -0
- package/Tests/App/Dashboard/BackupCodes.test.tsx +414 -0
- package/Tests/App/Dashboard/BillingPaidUsageFlow.test.tsx +530 -0
- package/Tests/App/Dashboard/BillingPaymentMethodForm.test.tsx +195 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +199 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +198 -0
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +215 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +183 -0
- package/Tests/App/Dashboard/CloudFleetSummaryComponent.test.tsx +248 -0
- package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +215 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +3 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +441 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +458 -0
- package/Tests/App/Dashboard/DashboardToolbarPopupLayering.test.tsx +566 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +376 -0
- package/Tests/App/Dashboard/DiscoveryScanLiveUpdates.test.tsx +642 -0
- package/Tests/App/Dashboard/DiscoveryScanProgress.test.tsx +267 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +69 -231
- package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +244 -0
- package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +724 -0
- package/Tests/App/Dashboard/MonitorTemplateSyncFields.test.tsx +539 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +24 -7
- package/Tests/App/Dashboard/OnCallOverridePresentation.test.ts +496 -0
- package/Tests/App/Dashboard/OnCallScheduleOverrideClarity.test.tsx +677 -0
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +31 -3
- package/Tests/App/Dashboard/PaidUsageConsent.test.tsx +180 -0
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +1220 -0
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +176 -275
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +380 -233
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +3 -23
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +1 -0
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
- package/Tests/App/Dashboard/TwoFactorStatus.test.tsx +260 -0
- package/Tests/App/Dashboard/VMwareDashboardWidgets.test.ts +573 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +768 -0
- package/Tests/App/StatusPage/StatusPageOverviewLiveAndSearch.test.tsx +532 -40
- package/Tests/App/StatusPage/StatusPageResourceSearchBox.test.tsx +19 -2
- package/Tests/Fixtures/MobilePasskeyUser.json +8 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +25 -6
- package/Tests/Models/DatabaseModels/VMwareModels.test.ts +1134 -0
- package/Tests/Models/RunnerVersionLabel.test.ts +4 -2
- package/Tests/Server/API/AIChatProvidersAIEnabled.test.ts +259 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +31 -2
- package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +116 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +29 -1
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +13 -2
- package/Tests/Server/API/GitHubWebhookSignature.test.ts +368 -0
- package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +248 -0
- package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +330 -0
- package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +172 -0
- package/Tests/Server/API/VMwareResourceAPI.test.ts +546 -0
- package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupMetadataMigration.test.ts +85 -0
- package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +34 -3
- package/Tests/Server/Middleware/PasskeyRateLimit.test.ts +141 -0
- package/Tests/Server/Services/AutoImportRuleRearmRecentScans.test.ts +343 -0
- package/Tests/Server/Services/AutoImportRuleWriteRearmsScans.test.ts +381 -0
- package/Tests/Server/Services/BillingPaymentMethodServiceSync.test.ts +175 -0
- package/Tests/Server/Services/BillingService.test.ts +26 -11
- package/Tests/Server/Services/BillingServiceMeteredPaymentAuthorization.test.ts +245 -0
- package/Tests/Server/Services/BillingServicePaymentMethodReads.test.ts +362 -0
- package/Tests/Server/Services/CloudResourceInstanceStalePrune.test.ts +188 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +16 -2
- package/Tests/Server/Services/GoogleSecOpsDiagnosticsModel.test.ts +153 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +13 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +5 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +36 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +151 -0
- package/Tests/Server/Services/MobilePasskeyLoginIntegration.test.ts +231 -0
- package/Tests/Server/Services/MobilePasskeyLoginService.test.ts +315 -0
- package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +302 -0
- package/Tests/Server/Services/MonitorTemplateServiceFieldSync.test.ts +994 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +22 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
- package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +857 -0
- package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +379 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +99 -1
- package/Tests/Server/Services/ProjectServiceSubscriptionSeats.test.ts +134 -0
- package/Tests/Server/Services/ResourceFeedServices.test.ts +3 -2
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +32 -6
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +363 -175
- package/Tests/Server/Services/ServerlessFunctionInstanceStalePrune.test.ts +200 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +3 -0
- package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +762 -0
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +3 -0
- package/Tests/Server/Services/TelemetryIngestionKeyPolicyResolution.test.ts +72 -0
- package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +17 -4
- package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +355 -0
- package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +41 -4
- package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +37 -0
- package/Tests/Server/Services/UserWebAuthnPasskeyIntegration.test.ts +401 -0
- package/Tests/Server/Services/UserWebAuthnPasskeyService.test.ts +769 -0
- package/Tests/Server/Services/UserWebAuthnUserVerification.test.ts +649 -0
- package/Tests/Server/Services/VMwareResourceService.test.ts +945 -0
- package/Tests/Server/Services/VMwareVCenterService.test.ts +722 -0
- package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +842 -0
- package/Tests/Server/TestingUtils/PasskeyHttpIntegration.ts +102 -0
- package/Tests/Server/TestingUtils/SecurityKey.ts +464 -0
- package/Tests/Server/TestingUtils/WebAuthnServiceMocks.ts +60 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +573 -0
- package/Tests/Server/Types/Database/Permissions/ProjectCustomerSupportAccessColumn.test.ts +261 -0
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +95 -0
- package/Tests/Server/Utils/CodeRepository/GitHubAgentTaskTrigger.test.ts +2101 -0
- package/Tests/Server/Utils/CodeRepository/GitHubCommandAuthorizer.test.ts +883 -0
- package/Tests/Server/Utils/CodeRepository/GitHubCommandParser.test.ts +1474 -0
- package/Tests/Server/Utils/CodeRepository/GitHubConversation.test.ts +1526 -0
- package/Tests/Server/Utils/CodeRepository/GitHubReplyComposer.test.ts +1389 -0
- package/Tests/Server/Utils/CodeRepository/GitHubRunReply.test.ts +1707 -0
- package/Tests/Server/Utils/CodeRepository/GitHubWebhookHandler.test.ts +2202 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +345 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +896 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupMetadataIntegration.test.ts +246 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +299 -1
- package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +6 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +185 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +33 -0
- package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +2 -1
- package/Tests/Server/Utils/Monitor/Criteria/TelemetryStepMetricAlias.test.ts +10 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +66 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +362 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +60 -1
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +60 -0
- package/Tests/Server/Utils/Monitor/MonitorRootCauseTargetResolution.test.ts +423 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +46 -2
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +81 -45
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +711 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClickhouseIntegration.test.ts +223 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +69 -1
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +838 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +241 -28
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +651 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +11 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +13 -12
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +112 -127
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +12 -18
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +43 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +580 -0
- package/Tests/Server/Utils/Telemetry/VMwareSnapshotScan.test.ts +1748 -0
- package/Tests/Types/Cloud/CloudPlatform.test.ts +574 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +2 -0
- package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +9 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
- package/Tests/Types/Monitor/MonitorStepInfraMonitorUtils.test.ts +53 -8
- package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +97 -0
- package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +1 -0
- package/Tests/Types/Monitor/MonitorStepTemplateSyncFields.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorTemplateSyncField.test.ts +172 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +2 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationAlertDebuggability.test.ts +1 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +9 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
- package/Tests/Types/Monitor/SeriesContext/SeriesDebugHints.test.ts +131 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +10 -0
- package/Tests/Types/Monitor/VMwareAlertTemplates.test.ts +1608 -0
- package/Tests/Types/Monitor/VMwareMetricCatalog.test.ts +488 -0
- package/Tests/Types/OnCallDutyPolicy/OverrideProvenance.test.ts +295 -0
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +2 -2
- package/Tests/Types/SignupPassword.test.ts +205 -0
- package/Tests/Types/StatusPage/StatusPageLanguage.test.ts +44 -0
- package/Tests/Types/SupportedLanguages.test.ts +119 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +19 -0
- package/Tests/UI/ColorPickerBuild.test.ts +86 -0
- package/Tests/UI/Components/Breadcrumbs.test.tsx +32 -0
- package/Tests/UI/Components/Forms/ModelFormPreservesUserInput.test.tsx +937 -0
- package/Tests/UI/Components/Forms/ModelFormWriteOnlyColumnSelect.test.tsx +230 -0
- package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +3 -4
- package/Tests/UI/Components/LabelRuleImportExport.test.tsx +907 -0
- package/Tests/UI/Components/ModelTable/ModelTableCreateAfterEdit.test.tsx +514 -0
- package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +301 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +2 -1
- package/Tests/UI/Components/TooltipLazyContent.test.tsx +373 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +43 -12
- package/Tests/UI/ReactDomBuild.test.ts +104 -0
- package/Tests/UI/ReactRouterSingletonBuild.test.ts +11 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +148 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +29 -0
- package/Tests/UI/Utils/DashboardStackingLayers.test.ts +197 -0
- package/Tests/UI/Utils/LabelRuleImportExport.test.ts +546 -0
- package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +109 -2
- package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +7 -2
- package/Tests/UI/Utils/UserEmailRegistrationStatus.test.tsx +295 -0
- package/Tests/UI/Utils/WebAuthn.test.ts +265 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +5 -0
- package/Tests/Utils/Dashboard/Components/DashboardVMwareListComponents.test.ts +548 -0
- package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +17 -3
- package/Tests/Utils/LabelRuleImportExport.test.ts +583 -0
- package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
- package/Tests/Utils/Monitor/MonitorTemplateSyncUtil.test.ts +590 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +325 -0
- package/Tests/Utils/Telemetry/CloudInstanceIdentity.test.ts +255 -0
- package/Tests/Utils/Telemetry/EntityRelationship.test.ts +167 -0
- package/Tests/Utils/WebAuthnTestUtil.ts +79 -0
- package/Tests/__mocks__/simplewebauthn.js +33 -4
- package/Types/AI/CodeFixTaskContext.ts +122 -0
- package/Types/AI/CodeFixTaskType.ts +62 -0
- package/Types/Accounts/AccountsLanguage.ts +1 -0
- package/Types/AdminDashboard/AdminDashboardLanguage.ts +1 -0
- package/Types/Calendar/CalendarEvent.ts +16 -0
- package/Types/Cloud/CloudPlatform.ts +372 -0
- package/Types/CodeRepository/GitHubCommand.ts +69 -0
- package/Types/Dashboard/DashboardComponentType.ts +2 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.ts +23 -0
- package/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.ts +30 -0
- package/Types/Dashboard/DashboardLanguage.ts +1 -0
- package/Types/Dashboard/DashboardTemplates.ts +314 -0
- package/Types/Docs/DocsLanguage.ts +1 -0
- package/Types/Exception/EgressGuardException.ts +65 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +36 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +1 -0
- package/Types/Monitor/MonitorStep.ts +78 -17
- package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +1 -0
- package/Types/Monitor/MonitorStepVMwareMonitor.ts +127 -0
- package/Types/Monitor/MonitorTemplateSyncField.ts +291 -0
- package/Types/Monitor/MonitorType.ts +24 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +33 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +2 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +3 -0
- package/Types/Monitor/SeriesContext/SeriesDebugHints.ts +158 -0
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +61 -0
- package/Types/Monitor/VMwareAlertTemplates.ts +1070 -0
- package/Types/Monitor/VMwareMetricCatalog.ts +914 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +29 -0
- package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +16 -0
- package/Types/Password.ts +84 -5
- package/Types/Permission.ts +265 -0
- package/Types/Probe/RequestFailedDetails.ts +7 -0
- package/Types/Runner/RunnerLiveStatus.ts +1 -1
- package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -0
- package/Types/StatusPage/StatusPageLanguage.ts +1 -0
- package/Types/Telemetry/EntityType.ts +21 -0
- package/Types/Telemetry/ServiceType.ts +1 -0
- package/Typings/Index.d.ts +63 -0
- package/UI/Components/Breadcrumbs/Breadcrumbs.tsx +2 -2
- package/UI/Components/Calendar/Calendar.css +78 -0
- package/UI/Components/Calendar/Calendar.tsx +28 -3
- package/UI/Components/Forms/BasicForm.tsx +44 -2
- package/UI/Components/Forms/Fields/ColorPicker.tsx +2 -1
- package/UI/Components/Forms/Fields/FormField.tsx +16 -3
- package/UI/Components/Forms/ModelForm.tsx +159 -5
- package/UI/Components/Forms/Types/Field.ts +8 -1
- package/UI/Components/Graphs/DayUptimeGraph.tsx +8 -10
- package/UI/Components/Icon/Icon.tsx +16 -0
- package/UI/Components/Input/Input.tsx +42 -7
- package/UI/Components/LabelRule/ImportLabelRulesModal.tsx +441 -0
- package/UI/Components/LabelRule/LabelRuleTable.tsx +142 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +22 -5
- package/UI/Components/ModelTable/ModelTable.tsx +14 -6
- package/UI/Components/MonitorGraphs/Uptime.tsx +17 -27
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +3 -1
- package/UI/Components/Tooltip/Tooltip.tsx +52 -6
- package/UI/Utils/DashboardStackingLayers.ts +85 -0
- package/UI/Utils/LabelRuleImportExport.ts +310 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +4 -3
- package/UI/Utils/TelemetryService.ts +4 -3
- package/UI/Utils/UseDashboardGridDnd.ts +4 -1
- package/UI/Utils/UserEmailRegistrationStatus.ts +33 -5
- package/UI/Utils/WebAuthn.ts +220 -0
- package/UI/esbuild-config.js +4 -0
- package/Utils/API.ts +34 -0
- package/Utils/Dashboard/Components/DashboardVMwareHostListComponent.ts +38 -0
- package/Utils/Dashboard/Components/DashboardVMwareResourceListShared.ts +70 -0
- package/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.ts +76 -0
- package/Utils/Dashboard/Components/Index.ts +16 -0
- package/Utils/LabelRuleImportExport.ts +496 -0
- package/Utils/Monitor/MonitorTemplateSyncUtil.ts +164 -0
- package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +8 -0
- package/Utils/Telemetry/CloudInstanceIdentity.ts +94 -0
- package/Utils/Telemetry/EntityKey.ts +19 -0
- package/Utils/Telemetry/EntityRelationship.ts +19 -0
- package/build/dist/Models/DatabaseModels/Alert.js +54 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +44 -7
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +87 -0
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +60 -7
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js +287 -0
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Incident.js +54 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +16 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +57 -4
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +47 -2
- package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +18 -0
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareResource.js +1201 -0
- package/build/dist/Models/DatabaseModels/VMwareResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +1149 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterFeed.js +648 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js +503 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js +502 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +22 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +63 -3
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentGitHubAPI.js +363 -0
- package/build/dist/Server/API/AIAgentGitHubAPI.js.map +1 -0
- package/build/dist/Server/API/AIChatAPI.js +19 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/BillingAPI.js +22 -0
- package/build/dist/Server/API/BillingAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +10 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +33 -5
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +56 -0
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -0
- package/build/dist/Server/API/UserWebAuthnAPI.js +17 -1
- package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
- package/build/dist/Server/API/VMwareResourceAPI.js +106 -0
- package/build/dist/Server/API/VMwareResourceAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.js +241 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +80 -0
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +14 -1
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/BillingPaymentMethodService.js +71 -21
- package/build/dist/Server/Services/BillingPaymentMethodService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +210 -22
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceInstanceService.js +66 -0
- package/build/dist/Server/Services/CloudResourceInstanceService.js.map +1 -1
- package/build/dist/Server/Services/ExceptionAggregationService.js +1 -0
- package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
- package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js +9 -0
- package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js.map +1 -0
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +7 -0
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +16 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +1 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +1 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MobilePasskeyLoginService.js +137 -0
- package/build/dist/Server/Services/MobilePasskeyLoginService.js.map +1 -0
- package/build/dist/Server/Services/MonitorService.js +34 -1
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +64 -26
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +124 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +142 -2
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +28 -13
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PayAsYouGoBillingService.js +297 -0
- package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -0
- package/build/dist/Server/Services/ProjectService.js +38 -2
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionInstanceService.js +66 -0
- package/build/dist/Server/Services/ServerlessFunctionInstanceService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +85 -28
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +61 -10
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +62 -5
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +5 -4
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/UserWebAuthnService.js +345 -113
- package/build/dist/Server/Services/UserWebAuthnService.js.map +1 -1
- package/build/dist/Server/Services/VMwareResourceService.js +395 -0
- package/build/dist/Server/Services/VMwareResourceService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterFeedService.js +89 -0
- package/build/dist/Server/Services/VMwareVCenterFeedService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js +197 -0
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterLabelRuleService.js +13 -0
- package/build/dist/Server/Services/VMwareVCenterLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +214 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerTeamService.js +129 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerUserService.js +122 -0
- package/build/dist/Server/Services/VMwareVCenterOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/VMwareVCenterService.js +533 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -0
- package/build/dist/Server/Services/WorkflowVariableService.js +169 -0
- package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.js +12 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js +25 -2
- package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.js +275 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.js +226 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.js +297 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubConversation.js +582 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubConversation.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.js +275 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubRunReply.js +365 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubRunReply.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.js +103 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.js +661 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +113 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +99 -15
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +2 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +18 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +31 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +2 -1
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +219 -4
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +12 -4
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +5 -3
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +22 -9
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +21 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +21 -6
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/ResourceFeed/ResourceFeedUtil.js +3 -3
- package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js +9 -9
- package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +92 -71
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +428 -143
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +376 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +136 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/VMwareSnapshotScan.js +933 -0
- package/build/dist/Server/Utils/Telemetry/VMwareSnapshotScan.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskContext.js +40 -0
- package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
- package/build/dist/Types/AI/CodeFixTaskType.js +57 -0
- package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
- package/build/dist/Types/Accounts/AccountsLanguage.js +1 -0
- package/build/dist/Types/Accounts/AccountsLanguage.js.map +1 -1
- package/build/dist/Types/AdminDashboard/AdminDashboardLanguage.js +1 -0
- package/build/dist/Types/AdminDashboard/AdminDashboardLanguage.js.map +1 -1
- package/build/dist/Types/Cloud/CloudPlatform.js +277 -0
- package/build/dist/Types/Cloud/CloudPlatform.js.map +1 -0
- package/build/dist/Types/CodeRepository/GitHubCommand.js +52 -0
- package/build/dist/Types/CodeRepository/GitHubCommand.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardLanguage.js +1 -0
- package/build/dist/Types/Dashboard/DashboardLanguage.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +286 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Docs/DocsLanguage.js +1 -0
- package/build/dist/Types/Docs/DocsLanguage.js.map +1 -1
- package/build/dist/Types/Exception/EgressGuardException.js +59 -0
- package/build/dist/Types/Exception/EgressGuardException.js.map +1 -0
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +1 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +61 -18
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +1 -0
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepVMwareMonitor.js +71 -0
- package/build/dist/Types/Monitor/MonitorStepVMwareMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorTemplateSyncField.js +211 -0
- package/build/dist/Types/Monitor/MonitorTemplateSyncField.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +23 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +28 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +2 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js +120 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +60 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
- package/build/dist/Types/Monitor/VMwareAlertTemplates.js +775 -0
- package/build/dist/Types/Monitor/VMwareAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/VMwareMetricCatalog.js +799 -0
- package/build/dist/Types/Monitor/VMwareMetricCatalog.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +25 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +5 -0
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
- package/build/dist/Types/Password.js +63 -5
- package/build/dist/Types/Password.js.map +1 -1
- package/build/dist/Types/Permission.js +234 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Probe/RequestFailedDetails.js +7 -0
- package/build/dist/Types/Probe/RequestFailedDetails.js.map +1 -1
- package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js +2 -0
- package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageLanguage.js +1 -0
- package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +21 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +1 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js +2 -2
- package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js +24 -4
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +28 -3
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +89 -6
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +5 -6
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +14 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +37 -8
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LabelRule/ImportLabelRulesModal.js +228 -0
- package/build/dist/UI/Components/LabelRule/ImportLabelRulesModal.js.map +1 -0
- package/build/dist/UI/Components/LabelRule/LabelRuleTable.js +102 -0
- package/build/dist/UI/Components/LabelRule/LabelRuleTable.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +15 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +13 -5
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +14 -15
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +2 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +33 -4
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Utils/DashboardStackingLayers.js +44 -0
- package/build/dist/UI/Utils/DashboardStackingLayers.js.map +1 -0
- package/build/dist/UI/Utils/LabelRuleImportExport.js +195 -0
- package/build/dist/UI/Utils/LabelRuleImportExport.js.map +1 -0
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +4 -4
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TelemetryService.js +3 -2
- package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
- package/build/dist/UI/Utils/UseDashboardGridDnd.js +2 -1
- package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -1
- package/build/dist/UI/Utils/UserEmailRegistrationStatus.js +21 -4
- package/build/dist/UI/Utils/UserEmailRegistrationStatus.js.map +1 -1
- package/build/dist/UI/Utils/WebAuthn.js +121 -0
- package/build/dist/UI/Utils/WebAuthn.js.map +1 -0
- package/build/dist/Utils/API.js +29 -0
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareHostListComponent.js +33 -0
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareHostListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareResourceListShared.js +54 -0
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareResourceListShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.js +60 -0
- package/build/dist/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +8 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +356 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateSyncUtil.js +110 -0
- package/build/dist/Utils/Monitor/MonitorTemplateSyncUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +6 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js.map +1 -1
- package/build/dist/Utils/Telemetry/CloudInstanceIdentity.js +88 -0
- package/build/dist/Utils/Telemetry/CloudInstanceIdentity.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +15 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityRelationship.js +13 -0
- package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
import dns from "dns";
|
|
1
2
|
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
2
3
|
import { IsBillingEnabled } from "../../../../Server/EnvironmentConfig";
|
|
3
4
|
import DataSourceEgressGuard, {
|
|
4
5
|
AddressVerdict,
|
|
6
|
+
DEFAULT_DNS_RESOLVE_ATTEMPTS,
|
|
7
|
+
DEFAULT_DNS_RESOLVE_BUDGET_IN_MS,
|
|
5
8
|
EgressGuardOptions,
|
|
6
9
|
EgressLookupFunction,
|
|
7
10
|
EgressResolveFunction,
|
|
8
11
|
ResolvedAddress,
|
|
9
12
|
} from "../../../../Server/Utils/DataSource/EgressGuard";
|
|
13
|
+
import logger from "../../../../Server/Utils/Logger";
|
|
10
14
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
15
|
+
import EgressGuardException, {
|
|
16
|
+
EgressFailureReason,
|
|
17
|
+
} from "../../../../Types/Exception/EgressGuardException";
|
|
11
18
|
|
|
12
19
|
const ENV_VAR_NAME: string = "DATA_SOURCE_BLOCK_PRIVATE_ADDRESSES";
|
|
13
20
|
|
|
@@ -1001,3 +1008,892 @@ describe("DataSourceEgressGuard.shouldBlockPrivateAddresses - BILLING_ENABLED is
|
|
|
1001
1008
|
);
|
|
1002
1009
|
});
|
|
1003
1010
|
});
|
|
1011
|
+
|
|
1012
|
+
/*
|
|
1013
|
+
* ---------------------------------------------------------------------------
|
|
1014
|
+
* Typed failure reasons.
|
|
1015
|
+
*
|
|
1016
|
+
* Callers downstream of the guard used to have nothing but its message string
|
|
1017
|
+
* to reason about. A shared probe suppresses that string down to one sanitized
|
|
1018
|
+
* sentence (see the security block further down), so
|
|
1019
|
+
* API.getRequestFailedDetails matched none of its patterns and reported the
|
|
1020
|
+
* customer's monitor failure as phase "Unknown" with no actionable detail.
|
|
1021
|
+
*
|
|
1022
|
+
* Every throw site now carries an EgressFailureReason. These tests pin the
|
|
1023
|
+
* reason AND the exact message at each site: the reason has to be usable, and
|
|
1024
|
+
* the message must not have drifted while the reason was added.
|
|
1025
|
+
* ---------------------------------------------------------------------------
|
|
1026
|
+
*/
|
|
1027
|
+
|
|
1028
|
+
type ExpectEgressFailureFunction = (
|
|
1029
|
+
error: Error,
|
|
1030
|
+
expectedReason: EgressFailureReason,
|
|
1031
|
+
) => EgressGuardException;
|
|
1032
|
+
|
|
1033
|
+
const expectEgressFailure: ExpectEgressFailureFunction = (
|
|
1034
|
+
error: Error,
|
|
1035
|
+
expectedReason: EgressFailureReason,
|
|
1036
|
+
): EgressGuardException => {
|
|
1037
|
+
/*
|
|
1038
|
+
* instanceof BadDataException is asserted as well: EgressGuardException
|
|
1039
|
+
* extends it precisely so every pre-existing `instanceof BadDataException`
|
|
1040
|
+
* check — including the fail-fast paths in the probe monitors — keeps
|
|
1041
|
+
* behaving exactly as it did before the type existed.
|
|
1042
|
+
*/
|
|
1043
|
+
expect(error).toBeInstanceOf(EgressGuardException);
|
|
1044
|
+
expect(error).toBeInstanceOf(BadDataException);
|
|
1045
|
+
|
|
1046
|
+
const egressError: EgressGuardException = error as EgressGuardException;
|
|
1047
|
+
expect(egressError.reason).toBe(expectedReason);
|
|
1048
|
+
|
|
1049
|
+
return egressError;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
const rejectingResolveFunction: EgressResolveFunction = (
|
|
1053
|
+
_hostname: string,
|
|
1054
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
1055
|
+
return Promise.reject(new Error("getaddrinfo EAI_AGAIN"));
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
describe("DataSourceEgressGuard - typed failure reasons", () => {
|
|
1059
|
+
test("isTargetUnreachable is false only for InvalidTarget", () => {
|
|
1060
|
+
/*
|
|
1061
|
+
* This mapping is what decides whether a probe double-checks its own
|
|
1062
|
+
* internet connectivity before believing a monitor is down. A malformed
|
|
1063
|
+
* target is the tenant's configuration and must surface immediately; every
|
|
1064
|
+
* other refusal is a reachability claim that deserves a second opinion.
|
|
1065
|
+
*/
|
|
1066
|
+
expect(
|
|
1067
|
+
new EgressGuardException(
|
|
1068
|
+
"x",
|
|
1069
|
+
EgressFailureReason.InvalidTarget,
|
|
1070
|
+
).isTargetUnreachable(),
|
|
1071
|
+
).toBe(false);
|
|
1072
|
+
|
|
1073
|
+
const unreachableReasons: Array<EgressFailureReason> = [
|
|
1074
|
+
EgressFailureReason.Unreachable,
|
|
1075
|
+
EgressFailureReason.ResolutionFailed,
|
|
1076
|
+
EgressFailureReason.AddressBlocked,
|
|
1077
|
+
];
|
|
1078
|
+
for (const reason of unreachableReasons) {
|
|
1079
|
+
expect(new EgressGuardException("x", reason).isTargetUnreachable()).toBe(
|
|
1080
|
+
true,
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
test("an empty hostname is InvalidTarget", async () => {
|
|
1086
|
+
const error: Error = await captureRejection(
|
|
1087
|
+
DataSourceEgressGuard.assertHostnameAllowed("", {
|
|
1088
|
+
blockPrivateAddresses: true,
|
|
1089
|
+
}),
|
|
1090
|
+
);
|
|
1091
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1092
|
+
error,
|
|
1093
|
+
EgressFailureReason.InvalidTarget,
|
|
1094
|
+
);
|
|
1095
|
+
expect(egressError.message).toBe("Data source host is required.");
|
|
1096
|
+
expect(egressError.isTargetUnreachable()).toBe(false);
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
test.each([
|
|
1100
|
+
[
|
|
1101
|
+
"127.0.0.1",
|
|
1102
|
+
"Data source host 127.0.0.1 is not allowed: loopback address.",
|
|
1103
|
+
],
|
|
1104
|
+
[
|
|
1105
|
+
"169.254.169.254",
|
|
1106
|
+
"Data source host 169.254.169.254 is not allowed: link-local address (cloud metadata range).",
|
|
1107
|
+
],
|
|
1108
|
+
])(
|
|
1109
|
+
"a blocked literal IP (%s) is AddressBlocked and keeps its full message",
|
|
1110
|
+
async (address: string, expectedMessage: string) => {
|
|
1111
|
+
const error: Error = await captureRejection(
|
|
1112
|
+
DataSourceEgressGuard.assertHostnameAllowed(address, {
|
|
1113
|
+
blockPrivateAddresses: false,
|
|
1114
|
+
}),
|
|
1115
|
+
);
|
|
1116
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1117
|
+
error,
|
|
1118
|
+
EgressFailureReason.AddressBlocked,
|
|
1119
|
+
);
|
|
1120
|
+
expect(egressError.message).toBe(expectedMessage);
|
|
1121
|
+
expect(egressError.isTargetUnreachable()).toBe(true);
|
|
1122
|
+
},
|
|
1123
|
+
);
|
|
1124
|
+
|
|
1125
|
+
test("a blocked literal IP keeps full detail even when detail is suppressed", async () => {
|
|
1126
|
+
/*
|
|
1127
|
+
* The literal path deliberately ignores includeResolvedAddressInError:
|
|
1128
|
+
* echoing back an address the caller typed itself reveals nothing about
|
|
1129
|
+
* the probe's network, and turning it into "could not be reached" would
|
|
1130
|
+
* hide a plain configuration mistake for no security gain.
|
|
1131
|
+
*/
|
|
1132
|
+
const error: Error = await captureRejection(
|
|
1133
|
+
DataSourceEgressGuard.assertHostnameAllowed("169.254.169.254", {
|
|
1134
|
+
blockPrivateAddresses: false,
|
|
1135
|
+
targetLabel: "Monitor target",
|
|
1136
|
+
includeResolvedAddressInError: false,
|
|
1137
|
+
}),
|
|
1138
|
+
);
|
|
1139
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1140
|
+
error,
|
|
1141
|
+
EgressFailureReason.AddressBlocked,
|
|
1142
|
+
);
|
|
1143
|
+
expect(egressError.message).toBe(
|
|
1144
|
+
"Monitor target host 169.254.169.254 is not allowed: link-local address (cloud metadata range).",
|
|
1145
|
+
);
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
test.each([
|
|
1149
|
+
["", "Data source URL is required."],
|
|
1150
|
+
["not a valid url", "Invalid data source URL: not a valid url"],
|
|
1151
|
+
[
|
|
1152
|
+
"ftp://example.com/file",
|
|
1153
|
+
"Data source URL must use http or https (got ftp).",
|
|
1154
|
+
],
|
|
1155
|
+
[
|
|
1156
|
+
"file:///etc/passwd",
|
|
1157
|
+
"Data source URL must use http or https (got file).",
|
|
1158
|
+
],
|
|
1159
|
+
])(
|
|
1160
|
+
"an unusable URL (%s) is InvalidTarget",
|
|
1161
|
+
async (urlString: string, expectedMessage: string) => {
|
|
1162
|
+
const error: Error = await captureRejection(
|
|
1163
|
+
DataSourceEgressGuard.assertUrlAllowed(urlString, {
|
|
1164
|
+
blockPrivateAddresses: true,
|
|
1165
|
+
}),
|
|
1166
|
+
);
|
|
1167
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1168
|
+
error,
|
|
1169
|
+
EgressFailureReason.InvalidTarget,
|
|
1170
|
+
);
|
|
1171
|
+
expect(egressError.message).toBe(expectedMessage);
|
|
1172
|
+
expect(egressError.isTargetUnreachable()).toBe(false);
|
|
1173
|
+
},
|
|
1174
|
+
);
|
|
1175
|
+
|
|
1176
|
+
test.each([undefined, true])(
|
|
1177
|
+
"a resolver rejection is ResolutionFailed when includeResolvedAddressInError is %s",
|
|
1178
|
+
async (includeResolvedAddressInError: boolean | undefined) => {
|
|
1179
|
+
const error: Error = await captureRejection(
|
|
1180
|
+
DataSourceEgressGuard.assertHostnameAllowed("missing.example.com", {
|
|
1181
|
+
blockPrivateAddresses: true,
|
|
1182
|
+
resolveFunction: rejectingResolveFunction,
|
|
1183
|
+
includeResolvedAddressInError: includeResolvedAddressInError,
|
|
1184
|
+
}),
|
|
1185
|
+
);
|
|
1186
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1187
|
+
error,
|
|
1188
|
+
EgressFailureReason.ResolutionFailed,
|
|
1189
|
+
);
|
|
1190
|
+
expect(egressError.message).toBe(
|
|
1191
|
+
"Could not resolve data source host missing.example.com: getaddrinfo EAI_AGAIN",
|
|
1192
|
+
);
|
|
1193
|
+
expect(egressError.isTargetUnreachable()).toBe(true);
|
|
1194
|
+
},
|
|
1195
|
+
);
|
|
1196
|
+
|
|
1197
|
+
test.each([undefined, true])(
|
|
1198
|
+
"an empty DNS answer is ResolutionFailed when includeResolvedAddressInError is %s",
|
|
1199
|
+
async (includeResolvedAddressInError: boolean | undefined) => {
|
|
1200
|
+
const resolver: Resolver = makeResolver([]);
|
|
1201
|
+
const error: Error = await captureRejection(
|
|
1202
|
+
DataSourceEgressGuard.assertHostnameAllowed("empty.example.com", {
|
|
1203
|
+
blockPrivateAddresses: true,
|
|
1204
|
+
resolveFunction: resolver.resolveFunction,
|
|
1205
|
+
includeResolvedAddressInError: includeResolvedAddressInError,
|
|
1206
|
+
}),
|
|
1207
|
+
);
|
|
1208
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1209
|
+
error,
|
|
1210
|
+
EgressFailureReason.ResolutionFailed,
|
|
1211
|
+
);
|
|
1212
|
+
expect(egressError.message).toBe(
|
|
1213
|
+
"Could not resolve data source host empty.example.com.",
|
|
1214
|
+
);
|
|
1215
|
+
expect(egressError.isTargetUnreachable()).toBe(true);
|
|
1216
|
+
},
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
test.each([undefined, true])(
|
|
1220
|
+
"a blocked resolved address is AddressBlocked when includeResolvedAddressInError is %s",
|
|
1221
|
+
async (includeResolvedAddressInError: boolean | undefined) => {
|
|
1222
|
+
const resolver: Resolver = makeResolver([
|
|
1223
|
+
{ address: "10.23.45.67", family: 4 },
|
|
1224
|
+
]);
|
|
1225
|
+
const error: Error = await captureRejection(
|
|
1226
|
+
DataSourceEgressGuard.assertHostnameAllowed("internal.example.com", {
|
|
1227
|
+
blockPrivateAddresses: true,
|
|
1228
|
+
resolveFunction: resolver.resolveFunction,
|
|
1229
|
+
includeResolvedAddressInError: includeResolvedAddressInError,
|
|
1230
|
+
}),
|
|
1231
|
+
);
|
|
1232
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1233
|
+
error,
|
|
1234
|
+
EgressFailureReason.AddressBlocked,
|
|
1235
|
+
);
|
|
1236
|
+
expect(egressError.message).toBe(
|
|
1237
|
+
"Data source host internal.example.com resolves to 10.23.45.67, which is not allowed: private network address.",
|
|
1238
|
+
);
|
|
1239
|
+
expect(egressError.isTargetUnreachable()).toBe(true);
|
|
1240
|
+
},
|
|
1241
|
+
);
|
|
1242
|
+
|
|
1243
|
+
test("all three resolve-path failures collapse to Unreachable when detail is suppressed", async () => {
|
|
1244
|
+
const blockedResolver: Resolver = makeResolver([
|
|
1245
|
+
{ address: "10.23.45.67", family: 4 },
|
|
1246
|
+
]);
|
|
1247
|
+
const emptyResolver: Resolver = makeResolver([]);
|
|
1248
|
+
const suppressedOptions: EgressGuardOptions = {
|
|
1249
|
+
blockPrivateAddresses: true,
|
|
1250
|
+
targetLabel: "Monitor target",
|
|
1251
|
+
includeResolvedAddressInError: false,
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
const resolveFunctions: Array<EgressResolveFunction> = [
|
|
1255
|
+
rejectingResolveFunction,
|
|
1256
|
+
emptyResolver.resolveFunction,
|
|
1257
|
+
blockedResolver.resolveFunction,
|
|
1258
|
+
];
|
|
1259
|
+
|
|
1260
|
+
for (const resolveFunction of resolveFunctions) {
|
|
1261
|
+
const error: Error = await captureRejection(
|
|
1262
|
+
DataSourceEgressGuard.assertHostnameAllowed("host.example.com", {
|
|
1263
|
+
...suppressedOptions,
|
|
1264
|
+
resolveFunction: resolveFunction,
|
|
1265
|
+
}),
|
|
1266
|
+
);
|
|
1267
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1268
|
+
error,
|
|
1269
|
+
EgressFailureReason.Unreachable,
|
|
1270
|
+
);
|
|
1271
|
+
expect(egressError.message).toBe(
|
|
1272
|
+
"Monitor target host host.example.com could not be reached.",
|
|
1273
|
+
);
|
|
1274
|
+
expect(egressError.isTargetUnreachable()).toBe(true);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
test("assertUrlAllowed forwards the hostname's reason unchanged", async () => {
|
|
1279
|
+
/*
|
|
1280
|
+
* assertUrlAllowed is the entry point the HTTP monitors use, so the reason
|
|
1281
|
+
* has to survive the extra call frame — otherwise the URL form of the same
|
|
1282
|
+
* failure would still report phase "Unknown".
|
|
1283
|
+
*/
|
|
1284
|
+
const blockedResolver: Resolver = makeResolver([
|
|
1285
|
+
{ address: "10.23.45.67", family: 4 },
|
|
1286
|
+
]);
|
|
1287
|
+
|
|
1288
|
+
const detailedError: Error = await captureRejection(
|
|
1289
|
+
DataSourceEgressGuard.assertUrlAllowed("https://internal.example.com", {
|
|
1290
|
+
blockPrivateAddresses: true,
|
|
1291
|
+
resolveFunction: blockedResolver.resolveFunction,
|
|
1292
|
+
}),
|
|
1293
|
+
);
|
|
1294
|
+
expectEgressFailure(detailedError, EgressFailureReason.AddressBlocked);
|
|
1295
|
+
|
|
1296
|
+
const suppressedError: Error = await captureRejection(
|
|
1297
|
+
DataSourceEgressGuard.assertUrlAllowed(
|
|
1298
|
+
"https://order.abbeysbakehouse.com/",
|
|
1299
|
+
{
|
|
1300
|
+
blockPrivateAddresses: true,
|
|
1301
|
+
targetLabel: "Monitor target",
|
|
1302
|
+
includeResolvedAddressInError: false,
|
|
1303
|
+
resolveFunction: rejectingResolveFunction,
|
|
1304
|
+
},
|
|
1305
|
+
),
|
|
1306
|
+
);
|
|
1307
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1308
|
+
suppressedError,
|
|
1309
|
+
EgressFailureReason.Unreachable,
|
|
1310
|
+
);
|
|
1311
|
+
expect(egressError.message).toBe(
|
|
1312
|
+
"Monitor target host order.abbeysbakehouse.com could not be reached.",
|
|
1313
|
+
);
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
test("createGuardedLookup hands the typed exception to its callback", async () => {
|
|
1317
|
+
/*
|
|
1318
|
+
* The guarded lookup is installed into third-party clients, which surface
|
|
1319
|
+
* whatever the callback was handed. The reason must survive that hop too.
|
|
1320
|
+
*/
|
|
1321
|
+
const lookup: EgressLookupFunction =
|
|
1322
|
+
DataSourceEgressGuard.createGuardedLookup({
|
|
1323
|
+
blockPrivateAddresses: true,
|
|
1324
|
+
resolveFunction: makeResolver([{ address: "10.23.45.67", family: 4 }])
|
|
1325
|
+
.resolveFunction,
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
const result: LookupResult = await callLookup(lookup, {});
|
|
1329
|
+
|
|
1330
|
+
expectEgressFailure(
|
|
1331
|
+
result.error as unknown as Error,
|
|
1332
|
+
EgressFailureReason.AddressBlocked,
|
|
1333
|
+
);
|
|
1334
|
+
});
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
/*
|
|
1338
|
+
* ---------------------------------------------------------------------------
|
|
1339
|
+
* THE SECURITY INVARIANT.
|
|
1340
|
+
*
|
|
1341
|
+
* On OneUptime Cloud a shared probe resolves every tenant's hostnames against
|
|
1342
|
+
* the probe network's own resolver. A tenant who could tell "did not resolve"
|
|
1343
|
+
* apart from "resolved, but the address is blocked" would own a DNS oracle for
|
|
1344
|
+
* the probe network: point a monitor at internal-name.corp and read the
|
|
1345
|
+
* difference to learn whether the name exists.
|
|
1346
|
+
*
|
|
1347
|
+
* So with includeResolvedAddressInError:false the three resolve-path branches
|
|
1348
|
+
* must be BYTE-IDENTICAL in everything a caller can observe. The reason enum
|
|
1349
|
+
* added for the diagnostics fix is the newest thing that could reopen this,
|
|
1350
|
+
* which is why it is compared here alongside the message.
|
|
1351
|
+
* ---------------------------------------------------------------------------
|
|
1352
|
+
*/
|
|
1353
|
+
describe("DataSourceEgressGuard - the sanitized failure is one indistinguishable failure", () => {
|
|
1354
|
+
const ORACLE_HOSTNAME: string = "internal-name.example.com";
|
|
1355
|
+
const SECRET_ADDRESS: string = "10.23.45.67";
|
|
1356
|
+
|
|
1357
|
+
interface EgressFailureFingerprint {
|
|
1358
|
+
constructorName: string;
|
|
1359
|
+
message: string;
|
|
1360
|
+
reason: string;
|
|
1361
|
+
code: number;
|
|
1362
|
+
isTargetUnreachable: boolean;
|
|
1363
|
+
ownProperties: string;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
type FingerprintFunction = (error: Error) => EgressFailureFingerprint;
|
|
1367
|
+
|
|
1368
|
+
/*
|
|
1369
|
+
* Everything a caller can see, minus the stack (whose frames legitimately
|
|
1370
|
+
* differ). Own enumerable properties are folded in wholesale so that a
|
|
1371
|
+
* future change which attaches, say, a `resolvedAddress` to only one branch
|
|
1372
|
+
* fails this test instead of quietly reopening the oracle.
|
|
1373
|
+
*/
|
|
1374
|
+
const fingerprint: FingerprintFunction = (
|
|
1375
|
+
error: Error,
|
|
1376
|
+
): EgressFailureFingerprint => {
|
|
1377
|
+
const egressError: EgressGuardException = error as EgressGuardException;
|
|
1378
|
+
const ownProperties: Record<string, unknown> = {};
|
|
1379
|
+
|
|
1380
|
+
for (const key of Object.keys(error).sort()) {
|
|
1381
|
+
if (key === "stack") {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
ownProperties[key] = (error as unknown as Record<string, unknown>)[key];
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
return {
|
|
1388
|
+
constructorName: error.constructor.name,
|
|
1389
|
+
message: error.message,
|
|
1390
|
+
reason: egressError.reason,
|
|
1391
|
+
code: egressError.code as unknown as number,
|
|
1392
|
+
isTargetUnreachable: egressError.isTargetUnreachable(),
|
|
1393
|
+
ownProperties: JSON.stringify(ownProperties),
|
|
1394
|
+
};
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
const suppressedOptions: EgressGuardOptions = {
|
|
1398
|
+
blockPrivateAddresses: true,
|
|
1399
|
+
targetLabel: "Monitor target",
|
|
1400
|
+
includeResolvedAddressInError: false,
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
type FailWithFunction = (
|
|
1404
|
+
resolveFunction: EgressResolveFunction,
|
|
1405
|
+
options?: EgressGuardOptions,
|
|
1406
|
+
) => Promise<Error>;
|
|
1407
|
+
|
|
1408
|
+
const failWith: FailWithFunction = (
|
|
1409
|
+
resolveFunction: EgressResolveFunction,
|
|
1410
|
+
options: EgressGuardOptions = suppressedOptions,
|
|
1411
|
+
): Promise<Error> => {
|
|
1412
|
+
return captureRejection(
|
|
1413
|
+
DataSourceEgressGuard.assertHostnameAllowed(ORACLE_HOSTNAME, {
|
|
1414
|
+
...options,
|
|
1415
|
+
resolveFunction: resolveFunction,
|
|
1416
|
+
}),
|
|
1417
|
+
);
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
/*
|
|
1421
|
+
* The resolver rejection deliberately carries the secret address in its own
|
|
1422
|
+
* message, so a branch that passed the underlying error through would be
|
|
1423
|
+
* caught by the leak assertions below as well as by the equality one.
|
|
1424
|
+
*/
|
|
1425
|
+
const resolverRejects: EgressResolveFunction = (
|
|
1426
|
+
_hostname: string,
|
|
1427
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
1428
|
+
return Promise.reject(
|
|
1429
|
+
new Error(`getaddrinfo ENOTFOUND ${ORACLE_HOSTNAME} ${SECRET_ADDRESS}`),
|
|
1430
|
+
);
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
const resolverReturnsNothing: EgressResolveFunction = (
|
|
1434
|
+
_hostname: string,
|
|
1435
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
1436
|
+
return Promise.resolve([]);
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
const resolverReturnsPrivateAddress: EgressResolveFunction = (
|
|
1440
|
+
_hostname: string,
|
|
1441
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
1442
|
+
return Promise.resolve([{ address: SECRET_ADDRESS, family: 4 }]);
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
test("the DNS-failure, empty-answer and blocked-address branches are indistinguishable", async () => {
|
|
1446
|
+
const resolverRejectedError: Error = await failWith(resolverRejects);
|
|
1447
|
+
const emptyAnswerError: Error = await failWith(resolverReturnsNothing);
|
|
1448
|
+
const blockedAddressError: Error = await failWith(
|
|
1449
|
+
resolverReturnsPrivateAddress,
|
|
1450
|
+
);
|
|
1451
|
+
|
|
1452
|
+
const expectedFingerprint: EgressFailureFingerprint = {
|
|
1453
|
+
constructorName: "EgressGuardException",
|
|
1454
|
+
message: `Monitor target host ${ORACLE_HOSTNAME} could not be reached.`,
|
|
1455
|
+
reason: EgressFailureReason.Unreachable,
|
|
1456
|
+
code: fingerprint(resolverRejectedError).code,
|
|
1457
|
+
isTargetUnreachable: true,
|
|
1458
|
+
ownProperties: fingerprint(resolverRejectedError).ownProperties,
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
for (const error of [
|
|
1462
|
+
resolverRejectedError,
|
|
1463
|
+
emptyAnswerError,
|
|
1464
|
+
blockedAddressError,
|
|
1465
|
+
]) {
|
|
1466
|
+
expect(fingerprint(error)).toEqual(expectedFingerprint);
|
|
1467
|
+
|
|
1468
|
+
/*
|
|
1469
|
+
* Pinned absolutely, not just relative to each other: mutual equality
|
|
1470
|
+
* alone would still pass if some future change attached the same new
|
|
1471
|
+
* leaky field (a cause, a resolved address) to every branch.
|
|
1472
|
+
*/
|
|
1473
|
+
expect(Object.keys(error).sort()).toEqual(["_code", "reason"]);
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
test("no sanitized message leaks the resolved address, the policy reason or the resolver error", async () => {
|
|
1478
|
+
const errors: Array<Error> = [
|
|
1479
|
+
await failWith(resolverRejects),
|
|
1480
|
+
await failWith(resolverReturnsNothing),
|
|
1481
|
+
await failWith(resolverReturnsPrivateAddress),
|
|
1482
|
+
];
|
|
1483
|
+
|
|
1484
|
+
for (const error of errors) {
|
|
1485
|
+
expect(error.message).not.toContain(SECRET_ADDRESS);
|
|
1486
|
+
expect(error.message).not.toContain("private network");
|
|
1487
|
+
expect(error.message).not.toContain("ENOTFOUND");
|
|
1488
|
+
expect(error.message).not.toContain("resolve");
|
|
1489
|
+
expect(JSON.stringify(error)).not.toContain(SECRET_ADDRESS);
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
test("the same three branches ARE distinguishable when detail is allowed", async () => {
|
|
1494
|
+
/*
|
|
1495
|
+
* Guards the guard: without this, a bug that made the sanitized path the
|
|
1496
|
+
* ONLY path would leave the invariant test above passing while every
|
|
1497
|
+
* operator-facing message quietly lost its diagnosis.
|
|
1498
|
+
*/
|
|
1499
|
+
const detailedOptions: EgressGuardOptions = {
|
|
1500
|
+
blockPrivateAddresses: true,
|
|
1501
|
+
targetLabel: "Monitor target",
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
const messages: Array<string> = [
|
|
1505
|
+
(await failWith(resolverRejects, detailedOptions)).message,
|
|
1506
|
+
(await failWith(resolverReturnsNothing, detailedOptions)).message,
|
|
1507
|
+
(await failWith(resolverReturnsPrivateAddress, detailedOptions)).message,
|
|
1508
|
+
];
|
|
1509
|
+
|
|
1510
|
+
expect(new Set(messages).size).toBe(3);
|
|
1511
|
+
expect(messages[2]).toContain(SECRET_ADDRESS);
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
test("operators still learn the real cause through the debug log", async () => {
|
|
1515
|
+
/*
|
|
1516
|
+
* Destroying the cause for the caller is the point, so the debug log is
|
|
1517
|
+
* the ONLY channel left for whoever has to fix the monitor. If this ever
|
|
1518
|
+
* stops firing, a sanitized failure becomes undiagnosable everywhere.
|
|
1519
|
+
*/
|
|
1520
|
+
const debugSpy: jest.SpyInstance = jest
|
|
1521
|
+
.spyOn(logger, "debug")
|
|
1522
|
+
.mockImplementation((): void => {
|
|
1523
|
+
// Swallow the output; only the call itself is under test.
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
await failWith(resolverRejects);
|
|
1527
|
+
await failWith(resolverReturnsNothing);
|
|
1528
|
+
await failWith(resolverReturnsPrivateAddress);
|
|
1529
|
+
|
|
1530
|
+
const loggedLines: Array<string> = debugSpy.mock.calls.map(
|
|
1531
|
+
(call: Array<unknown>) => {
|
|
1532
|
+
return String(call[0]);
|
|
1533
|
+
},
|
|
1534
|
+
);
|
|
1535
|
+
|
|
1536
|
+
expect(loggedLines).toHaveLength(3);
|
|
1537
|
+
expect(loggedLines[0]).toContain("ENOTFOUND");
|
|
1538
|
+
expect(loggedLines[1]).toContain("no addresses");
|
|
1539
|
+
expect(loggedLines[2]).toContain("private network address");
|
|
1540
|
+
|
|
1541
|
+
for (const line of loggedLines) {
|
|
1542
|
+
expect(line).toContain(ORACLE_HOSTNAME);
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
/*
|
|
1548
|
+
* ---------------------------------------------------------------------------
|
|
1549
|
+
* DNS resolution retries.
|
|
1550
|
+
*
|
|
1551
|
+
* The customer incident that started all of this was a single transient
|
|
1552
|
+
* getaddrinfo failure on ONE monitor out of ~75 on the same probe: one lookup,
|
|
1553
|
+
* no retry, straight to a monitor-down incident. getaddrinfo fails transiently
|
|
1554
|
+
* (EAI_AGAIN under resolver load, a nameserver restarting) far more often than
|
|
1555
|
+
* a host actually disappears, which is why the probe's Helm chart already
|
|
1556
|
+
* ships fallback nameservers.
|
|
1557
|
+
*
|
|
1558
|
+
* defaultResolve is private, so every test here drives the real path:
|
|
1559
|
+
* assertHostnameAllowed with NO resolveFunction and dns.promises.lookup
|
|
1560
|
+
* replaced.
|
|
1561
|
+
* ---------------------------------------------------------------------------
|
|
1562
|
+
*/
|
|
1563
|
+
describe("DataSourceEgressGuard - DNS resolution retries", () => {
|
|
1564
|
+
const PUBLIC_ANSWER: Array<ResolvedAddress> = [
|
|
1565
|
+
{ address: "93.184.216.34", family: 4 },
|
|
1566
|
+
];
|
|
1567
|
+
const HOSTNAME: string = "order.abbeysbakehouse.com";
|
|
1568
|
+
|
|
1569
|
+
type FakeLookupImplementation = (
|
|
1570
|
+
hostname: string,
|
|
1571
|
+
lookupOptions: { all?: boolean | undefined },
|
|
1572
|
+
) => Promise<Array<ResolvedAddress>>;
|
|
1573
|
+
|
|
1574
|
+
type InstallLookupSpyFunction = (
|
|
1575
|
+
implementation: FakeLookupImplementation,
|
|
1576
|
+
) => jest.Mock;
|
|
1577
|
+
|
|
1578
|
+
/*
|
|
1579
|
+
* `as never` sidesteps dns.promises.lookup's overload set, the same trick
|
|
1580
|
+
* the guard itself uses when handing a lookup to http.Agent.
|
|
1581
|
+
*/
|
|
1582
|
+
const installLookupSpy: InstallLookupSpyFunction = (
|
|
1583
|
+
implementation: FakeLookupImplementation,
|
|
1584
|
+
): jest.Mock => {
|
|
1585
|
+
const lookupMock: jest.Mock = jest.fn(implementation);
|
|
1586
|
+
jest.spyOn(dns.promises, "lookup").mockImplementation(lookupMock as never);
|
|
1587
|
+
return lookupMock;
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
type TransientLookupFunction = (
|
|
1591
|
+
failuresBeforeSuccess: number,
|
|
1592
|
+
) => FakeLookupImplementation;
|
|
1593
|
+
|
|
1594
|
+
const transientlyFailingLookup: TransientLookupFunction = (
|
|
1595
|
+
failuresBeforeSuccess: number,
|
|
1596
|
+
): FakeLookupImplementation => {
|
|
1597
|
+
let attempts: number = 0;
|
|
1598
|
+
return (): Promise<Array<ResolvedAddress>> => {
|
|
1599
|
+
attempts++;
|
|
1600
|
+
if (attempts <= failuresBeforeSuccess) {
|
|
1601
|
+
const error: NodeJS.ErrnoException = new Error(
|
|
1602
|
+
`getaddrinfo EAI_AGAIN ${HOSTNAME}`,
|
|
1603
|
+
);
|
|
1604
|
+
error.code = "EAI_AGAIN";
|
|
1605
|
+
return Promise.reject(error);
|
|
1606
|
+
}
|
|
1607
|
+
return Promise.resolve(PUBLIC_ANSWER);
|
|
1608
|
+
};
|
|
1609
|
+
};
|
|
1610
|
+
|
|
1611
|
+
const alwaysFailingLookup: FakeLookupImplementation = (): Promise<
|
|
1612
|
+
Array<ResolvedAddress>
|
|
1613
|
+
> => {
|
|
1614
|
+
const error: NodeJS.ErrnoException = new Error(
|
|
1615
|
+
`getaddrinfo EAI_AGAIN ${HOSTNAME}`,
|
|
1616
|
+
);
|
|
1617
|
+
error.code = "EAI_AGAIN";
|
|
1618
|
+
return Promise.reject(error);
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
const neverSettlingLookup: FakeLookupImplementation = (): Promise<
|
|
1622
|
+
Array<ResolvedAddress>
|
|
1623
|
+
> => {
|
|
1624
|
+
return new Promise<Array<ResolvedAddress>>((): void => {
|
|
1625
|
+
// Models a wedged resolver: it neither answers nor fails.
|
|
1626
|
+
});
|
|
1627
|
+
};
|
|
1628
|
+
|
|
1629
|
+
test("the tuning constants have not drifted", () => {
|
|
1630
|
+
/*
|
|
1631
|
+
* Both are load-bearing. The budget is what keeps worst-case latency equal
|
|
1632
|
+
* to the single 5s lookup this replaced, and callers (the probe) pass a
|
|
1633
|
+
* larger one deliberately; the attempt count is what absorbs a blip.
|
|
1634
|
+
*/
|
|
1635
|
+
expect(DEFAULT_DNS_RESOLVE_BUDGET_IN_MS).toBe(5000);
|
|
1636
|
+
expect(DEFAULT_DNS_RESOLVE_ATTEMPTS).toBe(3);
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
test("a lookup that fails twice and then succeeds no longer fails the caller", async () => {
|
|
1640
|
+
/*
|
|
1641
|
+
* THE regression test for the customer incident. Against the old
|
|
1642
|
+
* single-shot defaultResolve this rejects with
|
|
1643
|
+
* "could not be reached" and the lookup is called exactly once.
|
|
1644
|
+
*/
|
|
1645
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(2));
|
|
1646
|
+
|
|
1647
|
+
const addresses: Array<ResolvedAddress> =
|
|
1648
|
+
await DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1649
|
+
blockPrivateAddresses: true,
|
|
1650
|
+
targetLabel: "Monitor target",
|
|
1651
|
+
includeResolvedAddressInError: false,
|
|
1652
|
+
});
|
|
1653
|
+
|
|
1654
|
+
expect(addresses).toEqual(PUBLIC_ANSWER);
|
|
1655
|
+
expect(lookupMock).toHaveBeenCalledTimes(3);
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
test("the same transient failure DOES take the monitor down when retrying is off", async () => {
|
|
1659
|
+
/*
|
|
1660
|
+
* The other half of the pair above, and the closest this suite can get to
|
|
1661
|
+
* running the old code: resolveAttempts:1 IS the single-shot defaultResolve
|
|
1662
|
+
* that produced the customer's incident, driven by the identical lookup.
|
|
1663
|
+
* One knob is the whole difference between a resolved monitor and
|
|
1664
|
+
* "Monitor target host order.abbeysbakehouse.com could not be reached."
|
|
1665
|
+
*/
|
|
1666
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(2));
|
|
1667
|
+
|
|
1668
|
+
const error: Error = await captureRejection(
|
|
1669
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1670
|
+
blockPrivateAddresses: true,
|
|
1671
|
+
targetLabel: "Monitor target",
|
|
1672
|
+
includeResolvedAddressInError: false,
|
|
1673
|
+
resolveAttempts: 1,
|
|
1674
|
+
}),
|
|
1675
|
+
);
|
|
1676
|
+
|
|
1677
|
+
expect(lookupMock).toHaveBeenCalledTimes(1);
|
|
1678
|
+
expect(error.message).toBe(
|
|
1679
|
+
`Monitor target host ${HOSTNAME} could not be reached.`,
|
|
1680
|
+
);
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
test("asks getaddrinfo for every record, not just the first", () => {
|
|
1684
|
+
/*
|
|
1685
|
+
* all:true is a security property, not a detail: a multi-record answer
|
|
1686
|
+
* that mixed one public decoy with a private target would otherwise slip
|
|
1687
|
+
* past the per-address checks.
|
|
1688
|
+
*/
|
|
1689
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(0));
|
|
1690
|
+
|
|
1691
|
+
return DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1692
|
+
blockPrivateAddresses: true,
|
|
1693
|
+
}).then(() => {
|
|
1694
|
+
expect(lookupMock).toHaveBeenCalledWith(HOSTNAME, { all: true });
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
test("the happy path costs exactly one lookup", async () => {
|
|
1699
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(0));
|
|
1700
|
+
|
|
1701
|
+
await DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1702
|
+
blockPrivateAddresses: true,
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
expect(lookupMock).toHaveBeenCalledTimes(1);
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
test("a persistently failing lookup is tried DEFAULT_DNS_RESOLVE_ATTEMPTS times and then reported", async () => {
|
|
1709
|
+
const lookupMock: jest.Mock = installLookupSpy(alwaysFailingLookup);
|
|
1710
|
+
|
|
1711
|
+
const error: Error = await captureRejection(
|
|
1712
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1713
|
+
blockPrivateAddresses: true,
|
|
1714
|
+
}),
|
|
1715
|
+
);
|
|
1716
|
+
|
|
1717
|
+
expect(lookupMock).toHaveBeenCalledTimes(DEFAULT_DNS_RESOLVE_ATTEMPTS);
|
|
1718
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1719
|
+
error,
|
|
1720
|
+
EgressFailureReason.ResolutionFailed,
|
|
1721
|
+
);
|
|
1722
|
+
// The LAST attempt's error is what surfaces, not a synthesized one.
|
|
1723
|
+
expect(egressError.message).toBe(
|
|
1724
|
+
`Could not resolve data source host ${HOSTNAME}: getaddrinfo EAI_AGAIN ${HOSTNAME}`,
|
|
1725
|
+
);
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
test("resolveAttempts: 1 disables retrying entirely", async () => {
|
|
1729
|
+
const lookupMock: jest.Mock = installLookupSpy(alwaysFailingLookup);
|
|
1730
|
+
|
|
1731
|
+
await captureRejection(
|
|
1732
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1733
|
+
blockPrivateAddresses: true,
|
|
1734
|
+
resolveAttempts: 1,
|
|
1735
|
+
}),
|
|
1736
|
+
);
|
|
1737
|
+
|
|
1738
|
+
expect(lookupMock).toHaveBeenCalledTimes(1);
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
test("a caller-supplied resolveAttempts is honoured", async () => {
|
|
1742
|
+
const lookupMock: jest.Mock = installLookupSpy(alwaysFailingLookup);
|
|
1743
|
+
|
|
1744
|
+
await captureRejection(
|
|
1745
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1746
|
+
blockPrivateAddresses: true,
|
|
1747
|
+
resolveAttempts: 5,
|
|
1748
|
+
}),
|
|
1749
|
+
);
|
|
1750
|
+
|
|
1751
|
+
expect(lookupMock).toHaveBeenCalledTimes(5);
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
test("retries stop as soon as the answer is good, even with a large attempt budget", async () => {
|
|
1755
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(1));
|
|
1756
|
+
|
|
1757
|
+
await DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1758
|
+
blockPrivateAddresses: true,
|
|
1759
|
+
resolveAttempts: 5,
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
expect(lookupMock).toHaveBeenCalledTimes(2);
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
test("a retried answer is still subject to the address policy", async () => {
|
|
1766
|
+
/*
|
|
1767
|
+
* Retrying must not become a way around the guard: the addresses the last
|
|
1768
|
+
* attempt returned go through exactly the same checks.
|
|
1769
|
+
*/
|
|
1770
|
+
let attempts: number = 0;
|
|
1771
|
+
installLookupSpy((): Promise<Array<ResolvedAddress>> => {
|
|
1772
|
+
attempts++;
|
|
1773
|
+
if (attempts === 1) {
|
|
1774
|
+
return Promise.reject(new Error("getaddrinfo EAI_AGAIN"));
|
|
1775
|
+
}
|
|
1776
|
+
return Promise.resolve([{ address: "169.254.169.254", family: 4 }]);
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
const error: Error = await captureRejection(
|
|
1780
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1781
|
+
blockPrivateAddresses: false,
|
|
1782
|
+
}),
|
|
1783
|
+
);
|
|
1784
|
+
|
|
1785
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1786
|
+
error,
|
|
1787
|
+
EgressFailureReason.AddressBlocked,
|
|
1788
|
+
);
|
|
1789
|
+
expect(egressError.message).toContain("169.254.169.254");
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
test("the total budget caps the retries below the attempt count", async () => {
|
|
1793
|
+
/*
|
|
1794
|
+
* resolveAttempts is a ceiling inside the budget, never an extension of
|
|
1795
|
+
* it: a 200ms budget cannot fit three attempts once the inter-attempt
|
|
1796
|
+
* delay is paid, so fewer are made.
|
|
1797
|
+
*/
|
|
1798
|
+
const budgetInMs: number = 200;
|
|
1799
|
+
const lookupMock: jest.Mock = installLookupSpy(alwaysFailingLookup);
|
|
1800
|
+
|
|
1801
|
+
const startedAt: number = Date.now();
|
|
1802
|
+
const error: Error = await captureRejection(
|
|
1803
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1804
|
+
blockPrivateAddresses: true,
|
|
1805
|
+
resolveTimeoutInMs: budgetInMs,
|
|
1806
|
+
}),
|
|
1807
|
+
);
|
|
1808
|
+
const elapsedInMs: number = Date.now() - startedAt;
|
|
1809
|
+
|
|
1810
|
+
expect(lookupMock.mock.calls.length).toBeGreaterThanOrEqual(1);
|
|
1811
|
+
expect(lookupMock.mock.calls.length).toBeLessThan(
|
|
1812
|
+
DEFAULT_DNS_RESOLVE_ATTEMPTS,
|
|
1813
|
+
);
|
|
1814
|
+
expect(elapsedInMs).toBeLessThan(budgetInMs + 500);
|
|
1815
|
+
expectEgressFailure(error, EgressFailureReason.ResolutionFailed);
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
test("a wedged resolver gives up at the budget instead of hanging", async () => {
|
|
1819
|
+
const budgetInMs: number = 25;
|
|
1820
|
+
const lookupMock: jest.Mock = installLookupSpy(neverSettlingLookup);
|
|
1821
|
+
|
|
1822
|
+
const error: Error = await captureRejection(
|
|
1823
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1824
|
+
blockPrivateAddresses: true,
|
|
1825
|
+
resolveTimeoutInMs: budgetInMs,
|
|
1826
|
+
}),
|
|
1827
|
+
);
|
|
1828
|
+
|
|
1829
|
+
expect(lookupMock).toHaveBeenCalledTimes(1);
|
|
1830
|
+
const egressError: EgressGuardException = expectEgressFailure(
|
|
1831
|
+
error,
|
|
1832
|
+
EgressFailureReason.ResolutionFailed,
|
|
1833
|
+
);
|
|
1834
|
+
expect(egressError.message).toContain("DNS lookup timed out");
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
test("retrying does not make the worst case any slower than the single lookup it replaced", async () => {
|
|
1838
|
+
/*
|
|
1839
|
+
* The retries live INSIDE one budget. A resolver that hangs burns the
|
|
1840
|
+
* whole budget on its first attempt, so it must not be retried — otherwise
|
|
1841
|
+
* every genuinely dead host would cost attempts x budget and the probe's
|
|
1842
|
+
* own execution timeout would start firing instead.
|
|
1843
|
+
*/
|
|
1844
|
+
const budgetInMs: number = 400;
|
|
1845
|
+
const lookupMock: jest.Mock = installLookupSpy(neverSettlingLookup);
|
|
1846
|
+
|
|
1847
|
+
const startedAt: number = Date.now();
|
|
1848
|
+
await captureRejection(
|
|
1849
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1850
|
+
blockPrivateAddresses: true,
|
|
1851
|
+
resolveTimeoutInMs: budgetInMs,
|
|
1852
|
+
resolveAttempts: DEFAULT_DNS_RESOLVE_ATTEMPTS,
|
|
1853
|
+
}),
|
|
1854
|
+
);
|
|
1855
|
+
const elapsedInMs: number = Date.now() - startedAt;
|
|
1856
|
+
|
|
1857
|
+
expect(lookupMock).toHaveBeenCalledTimes(1);
|
|
1858
|
+
expect(elapsedInMs).toBeLessThan(budgetInMs * 2);
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
test("an injected resolveFunction replaces the retry path entirely and is called once", async () => {
|
|
1862
|
+
/*
|
|
1863
|
+
* Pinning the real behaviour rather than an aspiration: resolveFunction is
|
|
1864
|
+
* a full substitution for defaultResolve, so callers that inject one own
|
|
1865
|
+
* their own retry policy and see exactly one call. Existing callers and
|
|
1866
|
+
* every other test in this file depend on that.
|
|
1867
|
+
*/
|
|
1868
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(0));
|
|
1869
|
+
|
|
1870
|
+
let resolveFunctionCalls: number = 0;
|
|
1871
|
+
const resolveFunction: EgressResolveFunction = (
|
|
1872
|
+
_hostname: string,
|
|
1873
|
+
): Promise<Array<ResolvedAddress>> => {
|
|
1874
|
+
resolveFunctionCalls++;
|
|
1875
|
+
return Promise.reject(new Error("getaddrinfo EAI_AGAIN"));
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
await captureRejection(
|
|
1879
|
+
DataSourceEgressGuard.assertHostnameAllowed(HOSTNAME, {
|
|
1880
|
+
blockPrivateAddresses: true,
|
|
1881
|
+
resolveFunction: resolveFunction,
|
|
1882
|
+
resolveAttempts: 5,
|
|
1883
|
+
}),
|
|
1884
|
+
);
|
|
1885
|
+
|
|
1886
|
+
expect(resolveFunctionCalls).toBe(1);
|
|
1887
|
+
expect(lookupMock).not.toHaveBeenCalled();
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
test("a literal IP never touches the resolver at all", async () => {
|
|
1891
|
+
const lookupMock: jest.Mock = installLookupSpy(transientlyFailingLookup(0));
|
|
1892
|
+
|
|
1893
|
+
await DataSourceEgressGuard.assertHostnameAllowed("93.184.216.34", {
|
|
1894
|
+
blockPrivateAddresses: true,
|
|
1895
|
+
});
|
|
1896
|
+
|
|
1897
|
+
expect(lookupMock).not.toHaveBeenCalled();
|
|
1898
|
+
});
|
|
1899
|
+
});
|