@oneuptime/common 14.0.8 → 14.0.10
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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +213 -0
- package/Models/DatabaseModels/DatabaseServer.ts +213 -0
- package/Models/DatabaseModels/DockerHost.ts +213 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Host.ts +213 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +219 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +598 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +51 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/CephClusterService.ts +109 -1
- package/Server/Services/DatabaseServerService.ts +116 -1
- package/Server/Services/DockerHostService.ts +109 -1
- package/Server/Services/DockerSwarmClusterService.ts +113 -1
- package/Server/Services/HostService.ts +126 -4
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/ResourceAiAccessService.ts +1439 -0
- package/Server/Services/ResourceAiAgentJobService.ts +202 -0
- package/Server/Services/ResourceAiAgentService.ts +2432 -0
- package/Server/Services/RunnerJobService.ts +852 -108
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Services/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/VMwareVCenterService.ts +110 -1
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1475 -51
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1000 -43
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +120 -7
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +402 -14
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
- package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +1073 -213
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +184 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
- package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
- package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +119 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +196 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +123 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
- package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
- package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
- package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
- package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
- package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
- package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
- package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
- package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
- package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
- package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +153 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
- package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +75 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
- package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
- package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
- package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
- package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
- package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
- package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
- package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
- package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
- package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
- package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
- package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +197 -18
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
- package/Types/Monitor/RumAlertTemplates.ts +351 -4
- package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -0
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +40 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AI/InvestigationReport.ts +121 -15
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
- package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
- package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
- package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
- package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
- package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
- package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
- package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
- package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
- package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
- package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +219 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js +219 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +374 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
- package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +41 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +104 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerService.js +99 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +104 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +114 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +104 -0
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
- package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/RunnerJobService.js +522 -97
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1056 -43
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +683 -42
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +89 -9
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +294 -15
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +768 -152
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +126 -45
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +36 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +71 -11
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,2259 @@
|
|
|
1
|
+
import GovcCommandPolicy, {
|
|
2
|
+
GOVC_MAX_RECORD_COUNT,
|
|
3
|
+
govcInventoryName,
|
|
4
|
+
isCollectableGovcProperty,
|
|
5
|
+
isProtectedGovcTarget,
|
|
6
|
+
} from "../../../../Utils/AiRemediation/Resource/GovcCommandPolicy";
|
|
7
|
+
import ResourceCommandPolicy from "../../../../Utils/AiRemediation/Resource/ResourceCommandPolicy";
|
|
8
|
+
import {
|
|
9
|
+
MAX_RESOURCE_COMMAND_TOKENS,
|
|
10
|
+
ResourceAutoExecutionVerdict,
|
|
11
|
+
ResourceCommandPolicyResult,
|
|
12
|
+
tokenizeResourceCommand,
|
|
13
|
+
} from "../../../../Utils/AiRemediation/Resource/ResourceCommandPolicyCore";
|
|
14
|
+
import {
|
|
15
|
+
RESOURCE_REDACTED_MARKER,
|
|
16
|
+
getResourceOutputRedactionHooks,
|
|
17
|
+
redactResourceCommandOutput,
|
|
18
|
+
redactResourceCommandOutputWithCount,
|
|
19
|
+
} from "../../../../Utils/AiRemediation/Resource/ResourceOutputRedactor";
|
|
20
|
+
import AiResourceType, {
|
|
21
|
+
AI_RESOURCE_TYPE_INFO,
|
|
22
|
+
} from "../../../../Types/ResourceAiAgent/AiResourceType";
|
|
23
|
+
import {
|
|
24
|
+
RESOURCE_AI_ALLOW_WRITES_ENV,
|
|
25
|
+
RESOURCE_AI_WRITE_TARGETS_ENV,
|
|
26
|
+
ResourceCommandTier,
|
|
27
|
+
} from "../../../../Types/ResourceAiAgent/ResourceAiAccess";
|
|
28
|
+
import {
|
|
29
|
+
AiRemediationCommandPolicyVerdict,
|
|
30
|
+
MAX_COMMAND_LENGTH_CHARS,
|
|
31
|
+
} from "../../../../Types/AutoRemediation/AiRemediationCommandPolicyVerdict";
|
|
32
|
+
import { describe, expect, test } from "@jest/globals";
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* Contract under test — GovcCommandPolicy, the tier of every govc command
|
|
36
|
+
* OneUptime AI composes for a VMware vCenter.
|
|
37
|
+
*
|
|
38
|
+
* - govc 0.53's grammar as Go's flag package reads it: the command first,
|
|
39
|
+
* flags only before the first argument, `-flag`/`--flag`/`-flag=value`,
|
|
40
|
+
* no short-flag clusters, a value flag taking the next word whatever it
|
|
41
|
+
* is (so a flag-looking value is refused), switches only as -x / -x=true
|
|
42
|
+
* / -x=false, "--" ending the flags.
|
|
43
|
+
* - Read: the modelled read commands with their own flag tables; secrets
|
|
44
|
+
* stay out of reach (vm.info -e/-json, host.info -json, object.collect
|
|
45
|
+
* without an allowlisted property, find filters outside the allowlist).
|
|
46
|
+
* - SafeWrite: one VM, named by its inventory path, powered on or
|
|
47
|
+
* guest-rebooted. RiskyWrite: every other power operation, several VMs,
|
|
48
|
+
* a bare VM name (govc acts on every VM with that name),
|
|
49
|
+
* host.maintenance.exit. requiresHuman: vm.migrate,
|
|
50
|
+
* host.maintenance.enter. The maintenance commands take a bare host
|
|
51
|
+
* name only (a path can name a whole cluster).
|
|
52
|
+
* - Denied: every other command, the endpoint/credential/debug/dump flags,
|
|
53
|
+
* patterns as write targets, look-alike and invisible characters.
|
|
54
|
+
* - Totality, the dispatcher's ladder / allowlist / write scope for
|
|
55
|
+
* VMwareVCenter, the guides, and the govc output redaction hook.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
const VCENTER: AiResourceType = AiResourceType.VMwareVCenter;
|
|
59
|
+
|
|
60
|
+
function govc(command: string): ResourceCommandPolicyResult {
|
|
61
|
+
return ResourceCommandPolicy.evaluateCommand({
|
|
62
|
+
resourceType: VCENTER,
|
|
63
|
+
command,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function direct(argv: unknown): ResourceCommandPolicyResult {
|
|
68
|
+
return GovcCommandPolicy.evaluateArgv(argv as Array<string>);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function autoExecution(
|
|
72
|
+
command: string,
|
|
73
|
+
options: { allowlistPatterns?: Array<string>; bypassApproval?: boolean } = {},
|
|
74
|
+
): ResourceAutoExecutionVerdict {
|
|
75
|
+
return ResourceCommandPolicy.evaluateForAutoExecution({
|
|
76
|
+
resourceType: VCENTER,
|
|
77
|
+
command,
|
|
78
|
+
allowlistPatterns: options.allowlistPatterns || [],
|
|
79
|
+
bypassApproval: options.bypassApproval === true,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function expectDenied(
|
|
84
|
+
command: string,
|
|
85
|
+
mentions?: string,
|
|
86
|
+
): ResourceCommandPolicyResult {
|
|
87
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
88
|
+
|
|
89
|
+
expect(result.tier).toBe(ResourceCommandTier.Denied);
|
|
90
|
+
expect(result.targets).toEqual([]);
|
|
91
|
+
expect(result.reason.length).toBeGreaterThan(0);
|
|
92
|
+
|
|
93
|
+
if (mentions !== undefined) {
|
|
94
|
+
expect(result.reason).toContain(mentions);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const ALL_TIERS: Array<string> = Object.values(ResourceCommandTier);
|
|
101
|
+
|
|
102
|
+
// The characters govc expands in a name; a write target never holds one.
|
|
103
|
+
const PATTERN_CHARACTERS: RegExp = /[*?[\]\\]/;
|
|
104
|
+
|
|
105
|
+
describe("identity and routing", () => {
|
|
106
|
+
test("names the tool and its one program", () => {
|
|
107
|
+
expect(GovcCommandPolicy.name).toBe("govc");
|
|
108
|
+
expect([...GovcCommandPolicy.programs]).toEqual(["govc"]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("the dispatcher routes VMware vCenters to it", () => {
|
|
112
|
+
expect(ResourceCommandPolicy.getToolPolicy(VCENTER)).toBe(
|
|
113
|
+
GovcCommandPolicy,
|
|
114
|
+
);
|
|
115
|
+
expect([...GovcCommandPolicy.programs]).toEqual([
|
|
116
|
+
...AI_RESOURCE_TYPE_INFO[VCENTER].programs,
|
|
117
|
+
]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("every Test connection command is a Read", () => {
|
|
121
|
+
for (const command of AI_RESOURCE_TYPE_INFO[VCENTER].testCommands) {
|
|
122
|
+
expect(govc(command).tier).toBe(ResourceCommandTier.Read);
|
|
123
|
+
expect(
|
|
124
|
+
ResourceCommandPolicy.isReadOnly({ resourceType: VCENTER, command }),
|
|
125
|
+
).toBe(true);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("another resource's program is refused before the tool reads it", () => {
|
|
130
|
+
for (const command of [
|
|
131
|
+
"docker ps",
|
|
132
|
+
"pvesh get /version",
|
|
133
|
+
"kubectl get pods",
|
|
134
|
+
"ceph health",
|
|
135
|
+
]) {
|
|
136
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
137
|
+
command,
|
|
138
|
+
"is not a program the VMware AI agent runs",
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(result.reason).toContain('"govc"');
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("the tool itself refuses a program other than govc", () => {
|
|
146
|
+
const result: ResourceCommandPolicyResult = direct(["kubectl", "get"]);
|
|
147
|
+
|
|
148
|
+
expect(result.tier).toBe(ResourceCommandTier.Denied);
|
|
149
|
+
expect(result.reason).toContain('start with "govc"');
|
|
150
|
+
expect(direct(["/usr/bin/govc", "about"]).tier).toBe(
|
|
151
|
+
ResourceCommandTier.Denied,
|
|
152
|
+
);
|
|
153
|
+
expect(direct(["GOVC", "about"]).tier).toBe(ResourceCommandTier.Denied);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("Read commands", () => {
|
|
158
|
+
test.each([
|
|
159
|
+
["govc about", "about"],
|
|
160
|
+
["govc about -json", "about"],
|
|
161
|
+
["govc about --json", "about"],
|
|
162
|
+
["govc about -json=true", "about"],
|
|
163
|
+
["govc about -json=false", "about"],
|
|
164
|
+
["govc about -l", "about"],
|
|
165
|
+
["govc about -l -json", "about"],
|
|
166
|
+
["govc about --", "about"],
|
|
167
|
+
["govc version", "version"],
|
|
168
|
+
["govc version -l", "version"],
|
|
169
|
+
["govc datacenter.info", "datacenter.info"],
|
|
170
|
+
["govc datacenter.info -json DC1", "datacenter.info"],
|
|
171
|
+
["govc datacenter.info -dc DC1", "datacenter.info"],
|
|
172
|
+
["govc datacenter.info DC1 DC2", "datacenter.info"],
|
|
173
|
+
["govc ls", "ls"],
|
|
174
|
+
["govc ls /", "ls"],
|
|
175
|
+
["govc ls -l /DC1/vm", "ls"],
|
|
176
|
+
["govc ls -L VirtualMachine:vm-42", "ls"],
|
|
177
|
+
["govc ls -i -l /DC1/host", "ls"],
|
|
178
|
+
["govc ls -t VirtualMachine /DC1/vm", "ls"],
|
|
179
|
+
["govc ls -t=HostSystem '/DC1/host/*'", "ls"],
|
|
180
|
+
["govc ls -dc DC1 vm", "ls"],
|
|
181
|
+
["govc ls -dc=DC1 host", "ls"],
|
|
182
|
+
["govc ls '/DC1/vm/My Folder'", "ls"],
|
|
183
|
+
["govc ls -- /DC1/vm", "ls"],
|
|
184
|
+
["govc ls /DC1/vm /DC1/host /DC1/datastore", "ls"],
|
|
185
|
+
["govc find", "find"],
|
|
186
|
+
["govc find .", "find"],
|
|
187
|
+
["govc find / -type m", "find"],
|
|
188
|
+
["govc find -type m -name 'web-*' /DC1/vm", "find"],
|
|
189
|
+
["govc find . -type m -runtime.powerState poweredOff", "find"],
|
|
190
|
+
["govc find . -type h -runtime.connectionState disconnected", "find"],
|
|
191
|
+
["govc find . -type m -summary.runtime.powerState poweredOn", "find"],
|
|
192
|
+
["govc find . -type h -runtime.inMaintenanceMode true", "find"],
|
|
193
|
+
[
|
|
194
|
+
"govc find . -type m -guest.toolsRunningStatus guestToolsNotRunning",
|
|
195
|
+
"find",
|
|
196
|
+
],
|
|
197
|
+
["govc find . -type m -overallStatus red", "find"],
|
|
198
|
+
["govc find /DC1 -type m -type h", "find"],
|
|
199
|
+
["govc find -type m -type h /", "find"],
|
|
200
|
+
["govc find -type m / -type h", "find"],
|
|
201
|
+
["govc find -l -i -maxdepth 2 /DC1", "find"],
|
|
202
|
+
["govc find -maxdepth 0 /DC1", "find"],
|
|
203
|
+
["govc find . -name web-01", "find"],
|
|
204
|
+
["govc find -json .", "find"],
|
|
205
|
+
["govc find -dc DC1 . -type s", "find"],
|
|
206
|
+
["govc vm.info web-01", "vm.info"],
|
|
207
|
+
["govc vm.info -r web-01", "vm.info"],
|
|
208
|
+
["govc vm.info -r -t /DC1/vm/web-01", "vm.info"],
|
|
209
|
+
["govc vm.info -g=false -r web-01", "vm.info"],
|
|
210
|
+
["govc vm.info -dc DC1 'web-*'", "vm.info"],
|
|
211
|
+
["govc vm.info web-01 web-02", "vm.info"],
|
|
212
|
+
["govc vm.info 'Windows Server 2019'", "vm.info"],
|
|
213
|
+
["govc host.info", "host.info"],
|
|
214
|
+
["govc host.info esx-01.example.com", "host.info"],
|
|
215
|
+
["govc host.info -host esx-01", "host.info"],
|
|
216
|
+
["govc host.info /DC1/host/Cluster1/esx-01", "host.info"],
|
|
217
|
+
["govc host.service.ls -host esx-01", "host.service.ls"],
|
|
218
|
+
["govc host.service.ls -json -host esx-01", "host.service.ls"],
|
|
219
|
+
["govc host.service.ls", "host.service.ls"],
|
|
220
|
+
["govc host.date.info -host esx-01", "host.date.info"],
|
|
221
|
+
["govc datastore.info", "datastore.info"],
|
|
222
|
+
["govc datastore.info -json datastore1", "datastore.info"],
|
|
223
|
+
["govc pool.info /DC1/host/Cluster1/Resources", "pool.info"],
|
|
224
|
+
["govc pool.info -json Resources/Prod", "pool.info"],
|
|
225
|
+
["govc events", "events"],
|
|
226
|
+
["govc events -n 50 /DC1/vm/web-01", "events"],
|
|
227
|
+
[`govc events -n ${GOVC_MAX_RECORD_COUNT}`, "events"],
|
|
228
|
+
["govc events -n 1", "events"],
|
|
229
|
+
["govc events -l -type VmPoweredOffEvent -type VmPoweredOnEvent", "events"],
|
|
230
|
+
["govc events -type com.vmware.vc.HA.DasHostFailedEvent", "events"],
|
|
231
|
+
["govc events -json -n=10 vm/web-01 vm/web-02", "events"],
|
|
232
|
+
["govc tasks", "tasks"],
|
|
233
|
+
["govc tasks -n 20 -l /DC1/vm/web-01", "tasks"],
|
|
234
|
+
["govc metric.ls /DC1/vm/web-01", "metric.ls"],
|
|
235
|
+
["govc metric.ls -l -L -i 300 /DC1/host/Cluster1/esx-01", "metric.ls"],
|
|
236
|
+
["govc metric.sample /DC1/vm/web-01 cpu.usage.average", "metric.sample"],
|
|
237
|
+
[
|
|
238
|
+
"govc metric.sample -n 12 -i real -t /DC1/vm/web-01 cpu.usage.average mem.usage.average",
|
|
239
|
+
"metric.sample",
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
"govc metric.sample -instance - vm/web-01 net.bytesTx.average",
|
|
243
|
+
"metric.sample",
|
|
244
|
+
],
|
|
245
|
+
[
|
|
246
|
+
"govc metric.sample -instance vmnic0 -i day host/esx-01 net.bytesRx.average",
|
|
247
|
+
"metric.sample",
|
|
248
|
+
],
|
|
249
|
+
[
|
|
250
|
+
"govc metric.sample -json -n 5 'host/Cluster1/*' cpu.usage.average",
|
|
251
|
+
"metric.sample",
|
|
252
|
+
],
|
|
253
|
+
[
|
|
254
|
+
"govc object.collect -s /DC1/vm/web-01 runtime.powerState",
|
|
255
|
+
"object.collect",
|
|
256
|
+
],
|
|
257
|
+
["govc object.collect /DC1/vm/web-01 runtime", "object.collect"],
|
|
258
|
+
[
|
|
259
|
+
"govc object.collect -s VirtualMachine:vm-42 summary.runtime.powerState summary.quickStats.overallCpuUsage",
|
|
260
|
+
"object.collect",
|
|
261
|
+
],
|
|
262
|
+
["govc object.collect -type m / name runtime.powerState", "object.collect"],
|
|
263
|
+
[
|
|
264
|
+
"govc object.collect -json /DC1/host/Cluster1/esx-01 overallStatus triggeredAlarmState",
|
|
265
|
+
"object.collect",
|
|
266
|
+
],
|
|
267
|
+
[
|
|
268
|
+
"govc object.collect -s vm/web-01 guest.toolsRunningStatus guest.guestState",
|
|
269
|
+
"object.collect",
|
|
270
|
+
],
|
|
271
|
+
[
|
|
272
|
+
"govc object.collect -s vm/web-01 summary.overallStatus",
|
|
273
|
+
"object.collect",
|
|
274
|
+
],
|
|
275
|
+
["govc object.collect -s vm/web-01 summary.quickStats", "object.collect"],
|
|
276
|
+
["govc tags.ls", "tags.ls"],
|
|
277
|
+
["govc tags.ls -c env -json", "tags.ls"],
|
|
278
|
+
])("%s is a Read", (command: string, verb: string) => {
|
|
279
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
280
|
+
|
|
281
|
+
expect(result.tier).toBe(ResourceCommandTier.Read);
|
|
282
|
+
expect(result.verb).toBe(verb);
|
|
283
|
+
expect(result.program).toBe("govc");
|
|
284
|
+
expect(result.targets).toEqual([]);
|
|
285
|
+
expect(result.requiresHuman).toBeUndefined();
|
|
286
|
+
expect(result.args).toEqual(
|
|
287
|
+
tokenizeResourceCommand(command).argv!.slice(1),
|
|
288
|
+
);
|
|
289
|
+
expect(result.reason.length).toBeGreaterThan(0);
|
|
290
|
+
expect(result.reason.endsWith(".")).toBe(false);
|
|
291
|
+
expect(
|
|
292
|
+
ResourceCommandPolicy.isReadOnly({ resourceType: VCENTER, command }),
|
|
293
|
+
).toBe(true);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("a read's reason says what it shows", () => {
|
|
297
|
+
expect(govc("govc vm.info web-01").reason).toContain("virtual machine");
|
|
298
|
+
expect(govc("govc events").reason).toContain("events");
|
|
299
|
+
expect(govc("govc about").reason).toContain("version");
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe("SafeWrite: one VM named by its path, a reversible power operation", () => {
|
|
304
|
+
test.each([
|
|
305
|
+
["govc vm.power -on /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
306
|
+
["govc vm.power -r /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
307
|
+
["govc vm.power --on /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
308
|
+
["govc vm.power -on=true /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
309
|
+
["govc vm.power --r=true /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
310
|
+
["govc vm.power -on /DC1/vm/Prod/web-01", "/DC1/vm/Prod/web-01"],
|
|
311
|
+
["govc vm.power -dc DC1 -on Prod/web-01", "Prod/web-01"],
|
|
312
|
+
["govc vm.power -on -- /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
313
|
+
[
|
|
314
|
+
"govc vm.power -on '/DC1/vm/Windows Server 2019'",
|
|
315
|
+
"/DC1/vm/Windows Server 2019",
|
|
316
|
+
],
|
|
317
|
+
["govc vm.power -r -force=false /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
318
|
+
["govc vm.power -on -off=false /DC1/vm/web-01", "/DC1/vm/web-01"],
|
|
319
|
+
["govc vm.power -on '/DC1/vm/web;01'", "/DC1/vm/web;01"],
|
|
320
|
+
])("%s", (command: string, target: string) => {
|
|
321
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
322
|
+
|
|
323
|
+
expect(result.tier).toBe(ResourceCommandTier.SafeWrite);
|
|
324
|
+
expect(result.verb).toBe("vm.power");
|
|
325
|
+
expect(result.targets).toEqual([target]);
|
|
326
|
+
expect(result.requiresHuman).toBeUndefined();
|
|
327
|
+
expect(result.reason).toContain(`one virtual machine (${target})`);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("the reasons say what happens", () => {
|
|
331
|
+
expect(govc("govc vm.power -on /DC1/vm/web-01").reason).toBe(
|
|
332
|
+
"powers on one virtual machine (/DC1/vm/web-01)",
|
|
333
|
+
);
|
|
334
|
+
expect(govc("govc vm.power -r /DC1/vm/web-01").reason).toContain(
|
|
335
|
+
"gracefully through VMware Tools",
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe("a bare VM name is every VM with that name", () => {
|
|
341
|
+
/*
|
|
342
|
+
* govc searches the whole VM folder tree for a bare name and powers on
|
|
343
|
+
* (or reboots) every VM that has it — web-01 in /DC1/vm/prod and in
|
|
344
|
+
* /DC1/vm/staging alike — so it is not one VM, and never unattended.
|
|
345
|
+
*/
|
|
346
|
+
test.each([
|
|
347
|
+
["govc vm.power -on web-01", "web-01"],
|
|
348
|
+
["govc vm.power -r web-01", "web-01"],
|
|
349
|
+
["govc vm.power -dc DC1 -on web-01", "web-01"],
|
|
350
|
+
["govc vm.power -on 'Windows Server 2019'", "Windows Server 2019"],
|
|
351
|
+
["govc vm.power -on -- web-01", "web-01"],
|
|
352
|
+
])("%s is RiskyWrite", (command: string, target: string) => {
|
|
353
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
354
|
+
|
|
355
|
+
expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
|
|
356
|
+
expect(result.targets).toEqual([target]);
|
|
357
|
+
expect(result.requiresHuman).toBeUndefined();
|
|
358
|
+
expect(result.reason).toContain(
|
|
359
|
+
`every virtual machine named ${target} (in any folder)`,
|
|
360
|
+
);
|
|
361
|
+
expect(result.reason).toContain("name the VM by its inventory path");
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("Automatic mode asks before it acts on a bare name", () => {
|
|
365
|
+
expect(autoExecution("govc vm.power -r web-01").verdict).toBe(
|
|
366
|
+
AiRemediationCommandPolicyVerdict.RequiresApproval,
|
|
367
|
+
);
|
|
368
|
+
expect(autoExecution("govc vm.power -r /DC1/vm/prod/web-01").verdict).toBe(
|
|
369
|
+
AiRemediationCommandPolicyVerdict.AutoApproved,
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("several VMs with a bare name among them say so", () => {
|
|
374
|
+
expect(govc("govc vm.power -off /DC1/vm/a web-01").reason).toContain(
|
|
375
|
+
"each bare name covers every VM that has it",
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe("RiskyWrite", () => {
|
|
381
|
+
test.each([
|
|
382
|
+
["govc vm.power -s web-01", ["web-01"], "shuts down the guest OS"],
|
|
383
|
+
["govc vm.power -off web-01", ["web-01"], "powers off"],
|
|
384
|
+
["govc vm.power -off -force web-01", ["web-01"], "-force"],
|
|
385
|
+
["govc vm.power -force -off web-01", ["web-01"], "-force"],
|
|
386
|
+
["govc vm.power -reset web-01", ["web-01"], "hard-resets"],
|
|
387
|
+
["govc vm.power -reset -force web-01", ["web-01"], "-force"],
|
|
388
|
+
["govc vm.power -suspend web-01", ["web-01"], "suspends"],
|
|
389
|
+
[
|
|
390
|
+
"govc vm.power -on web-01 web-02",
|
|
391
|
+
["web-01", "web-02"],
|
|
392
|
+
"more than one VM needs approval",
|
|
393
|
+
],
|
|
394
|
+
[
|
|
395
|
+
"govc vm.power -r web-01 web-02",
|
|
396
|
+
["web-01", "web-02"],
|
|
397
|
+
"more than one VM needs approval",
|
|
398
|
+
],
|
|
399
|
+
[
|
|
400
|
+
"govc vm.power -off web-01 web-02 web-03",
|
|
401
|
+
["web-01", "web-02", "web-03"],
|
|
402
|
+
"3 virtual machines (web-01, web-02, web-03)",
|
|
403
|
+
],
|
|
404
|
+
// Go reads a separate "true" as an argument: two VMs, as govc sees it.
|
|
405
|
+
["govc vm.power -on true web-01", ["true", "web-01"], "2 virtual machines"],
|
|
406
|
+
[
|
|
407
|
+
"govc host.maintenance.exit esx-01",
|
|
408
|
+
["esx-01"],
|
|
409
|
+
"out of maintenance mode",
|
|
410
|
+
],
|
|
411
|
+
[
|
|
412
|
+
"govc host.maintenance.exit -dc DC1 esx-01",
|
|
413
|
+
["esx-01"],
|
|
414
|
+
"out of maintenance mode",
|
|
415
|
+
],
|
|
416
|
+
])("%s", (command: string, targets: Array<string>, mentions: string) => {
|
|
417
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
418
|
+
|
|
419
|
+
expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
|
|
420
|
+
expect(result.targets).toEqual(targets);
|
|
421
|
+
expect(result.requiresHuman).toBeUndefined();
|
|
422
|
+
expect(result.reason).toContain(mentions);
|
|
423
|
+
expect(
|
|
424
|
+
ResourceCommandPolicy.isReadOnly({ resourceType: VCENTER, command }),
|
|
425
|
+
).toBe(false);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
describe("RiskyWrite that always needs a human", () => {
|
|
430
|
+
test.each([
|
|
431
|
+
["govc vm.migrate -host esx-02 web-01", ["web-01"], "host esx-02"],
|
|
432
|
+
[
|
|
433
|
+
"govc vm.migrate -pool /DC1/host/Cluster1/Resources/Prod web-01",
|
|
434
|
+
["web-01"],
|
|
435
|
+
"resource pool /DC1/host/Cluster1/Resources/Prod",
|
|
436
|
+
],
|
|
437
|
+
[
|
|
438
|
+
"govc vm.migrate -ds datastore2 web-01",
|
|
439
|
+
["web-01"],
|
|
440
|
+
"datastore datastore2",
|
|
441
|
+
],
|
|
442
|
+
[
|
|
443
|
+
"govc vm.migrate -host esx-02 -ds datastore2 web-01 web-02",
|
|
444
|
+
["web-01", "web-02"],
|
|
445
|
+
"host esx-02, datastore datastore2",
|
|
446
|
+
],
|
|
447
|
+
["govc vm.migrate -dc DC1 -host=esx-02 web-01", ["web-01"], "host esx-02"],
|
|
448
|
+
["govc host.maintenance.enter esx-01", ["esx-01"], "maintenance mode"],
|
|
449
|
+
])("%s", (command: string, targets: Array<string>, mentions: string) => {
|
|
450
|
+
const result: ResourceCommandPolicyResult = govc(command);
|
|
451
|
+
|
|
452
|
+
expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
|
|
453
|
+
expect(result.requiresHuman).toBe(true);
|
|
454
|
+
expect(result.targets).toEqual(targets);
|
|
455
|
+
expect(result.reason).toContain(mentions);
|
|
456
|
+
expect(result.reason).toContain("always needs a human");
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe("Denied commands", () => {
|
|
461
|
+
test.each([
|
|
462
|
+
["govc vm.destroy web-01", "reconfigures a virtual machine"],
|
|
463
|
+
["govc vm.create -m 1024 new-vm", "reconfigures a virtual machine"],
|
|
464
|
+
["govc vm.clone -vm web-01 web-02", "reconfigures a virtual machine"],
|
|
465
|
+
["govc vm.change -vm web-01 -c 4", "reconfigures a virtual machine"],
|
|
466
|
+
["govc vm.upgrade -vm web-01", "reconfigures a virtual machine"],
|
|
467
|
+
["govc vm.register /ds/web.vmx", "reconfigures a virtual machine"],
|
|
468
|
+
["govc vm.unregister web-01", "reconfigures a virtual machine"],
|
|
469
|
+
["govc vm.markastemplate web-01", "reconfigures a virtual machine"],
|
|
470
|
+
["govc vm.customize -vm web-01", "reconfigures a virtual machine"],
|
|
471
|
+
["govc vm.question -vm web-01", "reconfigures a virtual machine"],
|
|
472
|
+
["govc vm.disk.create -vm web-01", "reconfigures a virtual machine"],
|
|
473
|
+
["govc vm.network.add -vm web-01", "reconfigures a virtual machine"],
|
|
474
|
+
["govc vm.console web-01", "console"],
|
|
475
|
+
["govc vm.vnc.enable web-01", "console"],
|
|
476
|
+
["govc vm.keystrokes -vm web-01 -s x", "console"],
|
|
477
|
+
["govc vm.guest.tools -vm web-01", "inside a VM's guest OS"],
|
|
478
|
+
["govc guest.run -vm web-01 /bin/sh", "inside a VM's guest OS"],
|
|
479
|
+
["govc guest.ls -vm web-01 /etc", "inside a VM's guest OS"],
|
|
480
|
+
["govc guest.download -vm web-01 /etc/shadow -", "inside a VM's guest OS"],
|
|
481
|
+
["govc guest.upload -vm web-01 x /tmp/x", "inside a VM's guest OS"],
|
|
482
|
+
["govc guest.start -vm web-01 /bin/rm", "inside a VM's guest OS"],
|
|
483
|
+
["govc snapshot.create -vm web-01 s1", "snapshot"],
|
|
484
|
+
["govc snapshot.revert -vm web-01 s1", "snapshot"],
|
|
485
|
+
["govc snapshot.remove -vm web-01 '*'", "snapshot"],
|
|
486
|
+
["govc snapshot.tree -vm web-01", "snapshot"],
|
|
487
|
+
["govc device.remove -vm web-01 disk-1000-0", "virtual hardware"],
|
|
488
|
+
["govc device.ls -vm web-01", "virtual hardware"],
|
|
489
|
+
["govc disk.create -size 10G d1", "virtual hardware"],
|
|
490
|
+
["govc datastore.rm web-01/web-01.vmx", "datastore files"],
|
|
491
|
+
["govc datastore.upload x.iso iso/x.iso", "datastore files"],
|
|
492
|
+
["govc datastore.download web-01/web-01.vmx -", "datastore files"],
|
|
493
|
+
["govc datastore.cp a b", "datastore files"],
|
|
494
|
+
["govc datastore.mv a b", "datastore files"],
|
|
495
|
+
["govc datastore.mkdir x", "datastore files"],
|
|
496
|
+
["govc datastore.ls", "only datastore.info is allowed"],
|
|
497
|
+
["govc datastore.tail -n 100 web-01/vmware.log", "datastore files"],
|
|
498
|
+
["govc host.remove esx-01", "ESXi host"],
|
|
499
|
+
["govc host.add -hostname esx-09", "ESXi host"],
|
|
500
|
+
["govc host.shutdown esx-01", "ESXi host"],
|
|
501
|
+
["govc host.reboot esx-01", "ESXi host"],
|
|
502
|
+
["govc host.disconnect esx-01", "ESXi host"],
|
|
503
|
+
["govc host.reconnect esx-01", "ESXi host"],
|
|
504
|
+
["govc host.esxcli system version get", "esxcli"],
|
|
505
|
+
["govc host.esxcli.model", "esxcli"],
|
|
506
|
+
["govc host.service -host esx-01 restart TSM-SSH", "ESXi host"],
|
|
507
|
+
["govc host.account.create -id x", "ESXi host"],
|
|
508
|
+
["govc host.cert.info -host esx-01", "ESXi host"],
|
|
509
|
+
["govc host.date.change -host esx-01", "ESXi host"],
|
|
510
|
+
["govc host.vswitch.add vs1", "ESXi host"],
|
|
511
|
+
["govc host.portgroup.remove pg1", "ESXi host"],
|
|
512
|
+
["govc host.autostart.add web-01", "ESXi host"],
|
|
513
|
+
["govc host.option.set Config.X 1", "ESXi host"],
|
|
514
|
+
["govc host.storage.info", "ESXi host"],
|
|
515
|
+
["govc permissions.set -principal x -role Admin", "who may do what"],
|
|
516
|
+
["govc permissions.ls", "who may do what"],
|
|
517
|
+
["govc role.create r1", "who may do what"],
|
|
518
|
+
["govc role.ls", "who may do what"],
|
|
519
|
+
["govc sso.user.create bob", "who may do what"],
|
|
520
|
+
["govc sso.service.ls", "who may do what"],
|
|
521
|
+
["govc session.ls", "who may do what"],
|
|
522
|
+
["govc session.rm key", "who may do what"],
|
|
523
|
+
["govc session.login", "who may do what"],
|
|
524
|
+
["govc license.add key", "licensing"],
|
|
525
|
+
["govc license.ls", "licensing"],
|
|
526
|
+
["govc import.ova x.ova", "in or out of vCenter"],
|
|
527
|
+
["govc import.ovf x.ovf", "in or out of vCenter"],
|
|
528
|
+
["govc export.ovf -vm web-01 .", "in or out of vCenter"],
|
|
529
|
+
["govc library.deploy lib/item vm", "in or out of vCenter"],
|
|
530
|
+
["govc library.ls", "in or out of vCenter"],
|
|
531
|
+
["govc env", "password"],
|
|
532
|
+
["govc option.set config.x 1", "advanced settings"],
|
|
533
|
+
["govc option.ls", "advanced settings"],
|
|
534
|
+
["govc object.destroy /DC1/vm/web-01", "inventory objects"],
|
|
535
|
+
["govc object.rename /DC1/vm/web-01 x", "inventory objects"],
|
|
536
|
+
["govc object.mv /DC1/vm/web-01 /DC1/vm/x", "inventory objects"],
|
|
537
|
+
["govc object.method -name Destroy_Task vm/x", "inventory objects"],
|
|
538
|
+
["govc object.reload vm/x", "inventory objects"],
|
|
539
|
+
])("%s", (command: string, mentions: string) => {
|
|
540
|
+
const result: ResourceCommandPolicyResult = expectDenied(command, mentions);
|
|
541
|
+
|
|
542
|
+
// Every refusal tells the model what it may run instead.
|
|
543
|
+
expect(result.reason).toContain("vm.info");
|
|
544
|
+
expect(result.reason).toContain("vm.power");
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test.each([
|
|
548
|
+
["govc cluster.usage Cluster1"],
|
|
549
|
+
["govc cluster.change -drs-enabled Cluster1"],
|
|
550
|
+
["govc logs -host esx-01"],
|
|
551
|
+
["govc task.cancel task-1"],
|
|
552
|
+
["govc tags.attach env prod vm/web-01"],
|
|
553
|
+
["govc tags.create -c env prod"],
|
|
554
|
+
["govc fields.set owner x vm/web-01"],
|
|
555
|
+
["govc vapp.power -off app"],
|
|
556
|
+
["govc pool.create /DC1/host/C1/Resources/p"],
|
|
557
|
+
["govc folder.create /DC1/vm/x"],
|
|
558
|
+
["govc metric.reset vm/x"],
|
|
559
|
+
["govc extension.register x"],
|
|
560
|
+
["govc vm.power2 -on x"],
|
|
561
|
+
["govc vm.infox web-01"],
|
|
562
|
+
["govc vm web-01"],
|
|
563
|
+
["govc help"],
|
|
564
|
+
["govc vm.p\u043ewer -on web-01"], // Cyrillic "\u043e"
|
|
565
|
+
["govc constructor"],
|
|
566
|
+
["govc __proto__"],
|
|
567
|
+
["govc toString"],
|
|
568
|
+
["govc hasOwnProperty"],
|
|
569
|
+
])("%s is not a command this policy allows", (command: string) => {
|
|
570
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
571
|
+
command,
|
|
572
|
+
"is not a govc command this policy allows",
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
expect(result.reason).toContain("read commands: about");
|
|
576
|
+
expect(result.reason).toContain("changes: vm.power");
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test.each([
|
|
580
|
+
["govc VM.INFO web-01", "vm.info"],
|
|
581
|
+
["govc About", "about"],
|
|
582
|
+
["govc Vm.Power -on web-01", "vm.power"],
|
|
583
|
+
["govc LS /", "ls"],
|
|
584
|
+
["govc Host.Maintenance.Enter esx-01", "host.maintenance.enter"],
|
|
585
|
+
])(
|
|
586
|
+
"%s: command names are case-sensitive",
|
|
587
|
+
(command: string, fixed: string) => {
|
|
588
|
+
expectDenied(command, `write "${fixed}"`);
|
|
589
|
+
},
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
test("no command at all", () => {
|
|
593
|
+
expectDenied("govc", "name a govc command");
|
|
594
|
+
expect(direct(["govc", ""]).reason).toContain("name a govc command");
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
describe("Denied flags", () => {
|
|
599
|
+
const deniedEverywhere: Array<[string, string]> = [
|
|
600
|
+
["-u https://root:pw@evil.example.com/sdk", "endpoint"],
|
|
601
|
+
["-u=https://evil/sdk", "endpoint"],
|
|
602
|
+
["--u=https://evil/sdk", "endpoint"],
|
|
603
|
+
["-k", "TLS"],
|
|
604
|
+
["-k=true", "TLS"],
|
|
605
|
+
["-cert /tmp/client.pem", "credentials"],
|
|
606
|
+
["-key /tmp/client.key", "credentials"],
|
|
607
|
+
["-tls-ca-certs /tmp/ca.pem", "TLS"],
|
|
608
|
+
["-tls-known-hosts /tmp/hosts", "TLS"],
|
|
609
|
+
["-tls-handshake-timeout 1s", "TLS"],
|
|
610
|
+
["-persist-session=false", "session"],
|
|
611
|
+
["-vim-namespace urn:vim25", "protocol"],
|
|
612
|
+
["-vim-version 8.0", "protocol"],
|
|
613
|
+
["-debug", "session cookies"],
|
|
614
|
+
["-debug.path /tmp/x", "session cookies"],
|
|
615
|
+
["-trace", "session cookies"],
|
|
616
|
+
["-verbose", "session cookies"],
|
|
617
|
+
["-dump", "every property"],
|
|
618
|
+
["-xml", "every property"],
|
|
619
|
+
["-h", "usage"],
|
|
620
|
+
["-help", "usage"],
|
|
621
|
+
["--help", "usage"],
|
|
622
|
+
];
|
|
623
|
+
|
|
624
|
+
test.each(deniedEverywhere)(
|
|
625
|
+
"govc ls %s / is refused",
|
|
626
|
+
(flag: string, mentions: string) => {
|
|
627
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
628
|
+
`govc ls ${flag} /`,
|
|
629
|
+
mentions,
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
expect(result.reason).toContain("refused on every govc command");
|
|
633
|
+
},
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
test.each(deniedEverywhere)(
|
|
637
|
+
"govc vm.power %s -on web-01 is refused",
|
|
638
|
+
(flag: string, mentions: string) => {
|
|
639
|
+
expectDenied(`govc vm.power ${flag} -on web-01`, mentions);
|
|
640
|
+
},
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
test.each(deniedEverywhere)(
|
|
644
|
+
"govc find -l %s . is refused",
|
|
645
|
+
(flag: string, mentions: string) => {
|
|
646
|
+
expectDenied(`govc find -l ${flag} .`, mentions);
|
|
647
|
+
},
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
test.each([
|
|
651
|
+
["govc -json about", "comes before the command"],
|
|
652
|
+
["govc --json ls /", "comes before the command"],
|
|
653
|
+
["govc -dc DC1 ls", "comes before the command"],
|
|
654
|
+
["govc -- about", "comes before the command"],
|
|
655
|
+
["govc -u https://evil/sdk about", "refused on every govc command"],
|
|
656
|
+
["govc -k about", "refused on every govc command"],
|
|
657
|
+
["govc --debug=true about", "refused on every govc command"],
|
|
658
|
+
])("%s: flags before the command", (command: string, mentions: string) => {
|
|
659
|
+
expectDenied(command, mentions);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
test.each([
|
|
663
|
+
["govc vm.info -e web-01", "extraConfig"],
|
|
664
|
+
["govc vm.info -e=true web-01", "extraConfig"],
|
|
665
|
+
["govc vm.info -json web-01", "EVERY property"],
|
|
666
|
+
["govc vm.info -r -json web-01", "EVERY property"],
|
|
667
|
+
["govc vm.info -waitip web-01", "IP address"],
|
|
668
|
+
["govc vm.info -vm.ipath /DC1/vm/web-01", "as an argument"],
|
|
669
|
+
["govc vm.info -vm.uuid 4210-aa", "as an argument"],
|
|
670
|
+
["govc host.info -json esx-01", "EVERY property"],
|
|
671
|
+
// ls -json loads every property of each VM it lists (extraConfig, vApp properties)
|
|
672
|
+
["govc ls -json /DC1/vm", "EVERY property of each object it lists"],
|
|
673
|
+
["govc ls -json -dc DC1 vm", "EVERY property of each object it lists"],
|
|
674
|
+
["govc ls --json=true /DC1/vm", "EVERY property of each object it lists"],
|
|
675
|
+
["govc ls -l -json /DC1/host", "EVERY property of each object it lists"],
|
|
676
|
+
["govc events -f", "follows the event stream"],
|
|
677
|
+
["govc tasks -f", "follows task updates"],
|
|
678
|
+
["govc metric.sample -plot png vm/x cpu.usage.average", "gnuplot"],
|
|
679
|
+
["govc metric.sample -plot=- vm/x cpu.usage.average", "gnuplot"],
|
|
680
|
+
["govc object.collect -n 1 vm/x runtime.powerState", "waits"],
|
|
681
|
+
["govc object.collect -wait 1m vm/x runtime.powerState", "waits"],
|
|
682
|
+
["govc object.collect -R request.xml", "file"],
|
|
683
|
+
["govc object.collect -O vm/x runtime", "request"],
|
|
684
|
+
["govc object.collect -o vm/x", "structure"],
|
|
685
|
+
["govc vm.power -standby web-01", "standby"],
|
|
686
|
+
["govc vm.power -M -on web-01", "batch"],
|
|
687
|
+
["govc vm.power -wait=false -on web-01", "wait"],
|
|
688
|
+
["govc vm.power -vm.ipath /DC1/vm/web-01 -on", "as an argument"],
|
|
689
|
+
["govc vm.power -vm web-01 -on", "as an argument"],
|
|
690
|
+
["govc vm.migrate -vm.uuid x -host h", "as an argument"],
|
|
691
|
+
["govc host.maintenance.enter -evacuate esx-01", "evacuating"],
|
|
692
|
+
["govc host.maintenance.enter -host esx-01", "as the argument"],
|
|
693
|
+
[
|
|
694
|
+
"govc host.maintenance.exit -host.ipath /DC1/host/esx-01",
|
|
695
|
+
"as the argument",
|
|
696
|
+
],
|
|
697
|
+
])(
|
|
698
|
+
"%s: a flag this command has, refused",
|
|
699
|
+
(command: string, mentions: string) => {
|
|
700
|
+
expectDenied(command, mentions);
|
|
701
|
+
},
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
/*
|
|
705
|
+
* govc applies host.maintenance.* to every host an argument resolves to,
|
|
706
|
+
* and an inventory path resolves to a cluster as readily as to a host:
|
|
707
|
+
* only a bare host name is sure to name hosts and nothing else.
|
|
708
|
+
*/
|
|
709
|
+
test.each([
|
|
710
|
+
["govc host.maintenance.exit /DC1/host/Cluster1"],
|
|
711
|
+
["govc host.maintenance.enter /DC1/host/Cluster1"],
|
|
712
|
+
["govc host.maintenance.exit /DC1/host/Cluster1/esx-01"],
|
|
713
|
+
["govc host.maintenance.enter /DC1/host/Cluster1/esx-01"],
|
|
714
|
+
["govc host.maintenance.exit Cluster1/esx-01"],
|
|
715
|
+
["govc host.maintenance.exit -dc DC1 host/Cluster1"],
|
|
716
|
+
])(
|
|
717
|
+
"%s: a maintenance command takes a bare host name only",
|
|
718
|
+
(command: string) => {
|
|
719
|
+
expectDenied(command, "takes the ESXi host's own name");
|
|
720
|
+
},
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
test.each([
|
|
724
|
+
["govc ls -x /", "-x is not a flag this policy allows for govc ls"],
|
|
725
|
+
["govc ls -lL /", "-lL is not a flag"],
|
|
726
|
+
["govc ls -Li /", "-Li is not a flag"],
|
|
727
|
+
["govc ls -li /", "-li is not a flag"],
|
|
728
|
+
["govc ls -JSON /", "-JSON is not a flag"],
|
|
729
|
+
["govc ls -T VirtualMachine", "-T is not a flag"],
|
|
730
|
+
["govc about -c", "-c is not a flag this policy allows for govc about"],
|
|
731
|
+
["govc about -dc DC1", "-dc is not a flag"],
|
|
732
|
+
["govc version -json", "-json is not a flag"],
|
|
733
|
+
["govc tags.ls -dc DC1", "-dc is not a flag"],
|
|
734
|
+
["govc vm.info -a web-01", "-a is not a flag"],
|
|
735
|
+
["govc vm.power -on -json web-01", "-json is not a flag"],
|
|
736
|
+
["govc vm.power -onn web-01", "-onn is not a flag"],
|
|
737
|
+
["govc vm.power -offforce web-01", "-offforce is not a flag"],
|
|
738
|
+
["govc host.maintenance.exit -timeout 60 esx-01", "-timeout is not a flag"],
|
|
739
|
+
[
|
|
740
|
+
"govc vm.migrate -priority highPriority -host h web",
|
|
741
|
+
"-priority is not a flag",
|
|
742
|
+
],
|
|
743
|
+
["govc events -force", "-force is not a flag"],
|
|
744
|
+
["govc tasks -r /DC1", "-r is not a flag"],
|
|
745
|
+
["govc datastore.info -H", "-H is not a flag"],
|
|
746
|
+
])("%s: unknown flags", (command: string, mentions: string) => {
|
|
747
|
+
const result: ResourceCommandPolicyResult = expectDenied(command, mentions);
|
|
748
|
+
|
|
749
|
+
// The refusal lists the flags that ARE allowed.
|
|
750
|
+
expect(result.reason).toContain("which takes");
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test("the unknown-flag refusal lists what the command takes", () => {
|
|
754
|
+
expect(govc("govc ls -x").reason).toContain(
|
|
755
|
+
"-dc DATACENTER, -l, -L, -i and -t TYPE",
|
|
756
|
+
);
|
|
757
|
+
expect(govc("govc vm.power -x web").reason).toContain("-on");
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
test("a property filter before ROOT gets a hint", () => {
|
|
761
|
+
expectDenied(
|
|
762
|
+
"govc find -type m -runtime.powerState poweredOn",
|
|
763
|
+
"property filters go after the ROOT path",
|
|
764
|
+
);
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
describe("flag syntax as Go's flag package reads it", () => {
|
|
769
|
+
test.each([
|
|
770
|
+
["govc ls ---l /", "not valid flag syntax"],
|
|
771
|
+
["govc ls -=x /", "not valid flag syntax"],
|
|
772
|
+
["govc ls --=x /", "not valid flag syntax"],
|
|
773
|
+
["govc ls '-=' /", "not valid flag syntax"],
|
|
774
|
+
])("%s: bad syntax", (command: string, mentions: string) => {
|
|
775
|
+
expectDenied(command, mentions);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test.each([
|
|
779
|
+
["govc ls -l=yes /"],
|
|
780
|
+
["govc ls -l=1 /"],
|
|
781
|
+
["govc ls -l=TRUE /"],
|
|
782
|
+
["govc ls -l=True /"],
|
|
783
|
+
["govc ls -l=t /"],
|
|
784
|
+
["govc ls -l= /"],
|
|
785
|
+
["govc vm.power -on=1 web-01"],
|
|
786
|
+
["govc vm.power -on=on web-01"],
|
|
787
|
+
])("%s: a switch takes only =true or =false", (command: string) => {
|
|
788
|
+
expectDenied(command, "is a switch");
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
test.each([
|
|
792
|
+
["govc ls -t", "-t needs a value"],
|
|
793
|
+
["govc events -n", "-n needs a value"],
|
|
794
|
+
["govc vm.migrate web-01 -host", "comes after the argument"],
|
|
795
|
+
["govc vm.migrate -host", "-host needs a value"],
|
|
796
|
+
["govc ls -t= /", "needs a non-empty value"],
|
|
797
|
+
["govc ls -dc '' /", "needs a non-empty value"],
|
|
798
|
+
["govc vm.migrate -ds '' web-01", "needs a non-empty value"],
|
|
799
|
+
])(
|
|
800
|
+
"%s: a value flag needs its value",
|
|
801
|
+
(command: string, mentions: string) => {
|
|
802
|
+
expectDenied(command, mentions);
|
|
803
|
+
},
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
test.each([
|
|
807
|
+
["govc ls -t -u /", '"-u"'],
|
|
808
|
+
["govc ls -t=-k /", '"-k"'],
|
|
809
|
+
["govc ls -dc -debug /", '"-debug"'],
|
|
810
|
+
["govc events -n -debug", '"-debug"'],
|
|
811
|
+
["govc events -n -5", '"-5"'],
|
|
812
|
+
["govc vm.migrate -host -u web-01", '"-u"'],
|
|
813
|
+
["govc vm.migrate -host=-k web-01", '"-k"'],
|
|
814
|
+
["govc vm.power -dc -k -on web-01", '"-k"'],
|
|
815
|
+
["govc metric.sample -instance -plot vm/x cpu.usage.average", '"-plot"'],
|
|
816
|
+
["govc find -name -u .", '"-u"'],
|
|
817
|
+
])(
|
|
818
|
+
"%s: a denied token never hides as a flag's value",
|
|
819
|
+
(command: string, shown: string) => {
|
|
820
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
821
|
+
command,
|
|
822
|
+
"looks like a flag",
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
expect(result.reason).toContain(shown);
|
|
826
|
+
},
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
test.each([
|
|
830
|
+
["govc events -n 0"],
|
|
831
|
+
[`govc events -n ${GOVC_MAX_RECORD_COUNT + 1}`],
|
|
832
|
+
["govc events -n 010"],
|
|
833
|
+
["govc events -n 0x10"],
|
|
834
|
+
["govc events -n 1e3"],
|
|
835
|
+
["govc events -n +5"],
|
|
836
|
+
["govc events -n ' 5'"],
|
|
837
|
+
["govc tasks -n 99999999999999999999999"],
|
|
838
|
+
["govc metric.sample -n 5.5 vm/x cpu.usage.average"],
|
|
839
|
+
])("%s: counts are plain decimal from 1 to the cap", (command: string) => {
|
|
840
|
+
expectDenied(command, `from 1 to ${GOVC_MAX_RECORD_COUNT}`);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
test.each([
|
|
844
|
+
["govc ls -t 'Virtual Machine' /", "not a type name"],
|
|
845
|
+
["govc ls -t 9Type /", "not a type name"],
|
|
846
|
+
["govc find -type 'm;h' .", "not a type name"],
|
|
847
|
+
["govc find . -type 'Virtual Machine'", "not a type name"],
|
|
848
|
+
["govc events -type 'Vm Event'", "not an event type"],
|
|
849
|
+
["govc metric.sample -i hourly vm/x cpu.usage.average", "not an interval"],
|
|
850
|
+
["govc metric.ls -i 1d vm/x", "not an interval"],
|
|
851
|
+
["govc find -maxdepth 100 .", "not a depth"],
|
|
852
|
+
["govc find -maxdepth 01 .", "not a depth"],
|
|
853
|
+
])("%s: values are checked", (command: string, mentions: string) => {
|
|
854
|
+
expectDenied(command, mentions);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
test.each([
|
|
858
|
+
["govc ls -l -l /"],
|
|
859
|
+
["govc ls -l -l=false /"],
|
|
860
|
+
["govc ls -l --l /"],
|
|
861
|
+
["govc vm.power -on -on web-01"],
|
|
862
|
+
["govc vm.power -on --on=true web-01"],
|
|
863
|
+
["govc vm.power -dc a -dc b -on web-01"],
|
|
864
|
+
["govc vm.migrate -host a -host b web-01"],
|
|
865
|
+
["govc events -n 5 -n 10"],
|
|
866
|
+
["govc find -name a . -name b"],
|
|
867
|
+
[
|
|
868
|
+
"govc find . -runtime.powerState poweredOn -runtime.powerState poweredOff",
|
|
869
|
+
],
|
|
870
|
+
["govc find . -name a -name b"],
|
|
871
|
+
])("%s: each flag once", (command: string) => {
|
|
872
|
+
expectDenied(command, "more than once");
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
test("list flags govc appends to may repeat", () => {
|
|
876
|
+
expect(govc("govc events -type A -type B -type C").tier).toBe(
|
|
877
|
+
ResourceCommandTier.Read,
|
|
878
|
+
);
|
|
879
|
+
expect(govc("govc find -type m . -type h -type s").tier).toBe(
|
|
880
|
+
ResourceCommandTier.Read,
|
|
881
|
+
);
|
|
882
|
+
expect(govc("govc object.collect -type m -type h / name").tier).toBe(
|
|
883
|
+
ResourceCommandTier.Read,
|
|
884
|
+
);
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
test.each([
|
|
888
|
+
["govc ls /DC1 -l", '"-l" comes after the argument "/DC1"'],
|
|
889
|
+
["govc vm.info web-01 -e", '"-e" comes after the argument "web-01"'],
|
|
890
|
+
["govc vm.info web-01 -json", '"-json" comes after the argument'],
|
|
891
|
+
["govc vm.power web-01 -off", '"-off" comes after the argument "web-01"'],
|
|
892
|
+
["govc vm.power -on web-01 -off", '"-off" comes after the argument'],
|
|
893
|
+
["govc vm.power -on web-01 -force", '"-force" comes after the argument'],
|
|
894
|
+
["govc vm.power -on web-01 -u https://evil/sdk", '"-u" comes after'],
|
|
895
|
+
["govc events /DC1/vm/x -n 5", '"-n" comes after'],
|
|
896
|
+
["govc host.maintenance.exit esx-01 -u x", '"-u" comes after'],
|
|
897
|
+
[
|
|
898
|
+
"govc metric.sample vm/x cpu.usage.average -plot png",
|
|
899
|
+
'"-plot" comes after',
|
|
900
|
+
],
|
|
901
|
+
["govc datastore.info ds1 -json", '"-json" comes after'],
|
|
902
|
+
["govc vm.migrate web-01 -host esx-02", '"-host" comes after'],
|
|
903
|
+
[
|
|
904
|
+
"govc host.maintenance.enter esx-01 --evacuate",
|
|
905
|
+
'"--evacuate" comes after',
|
|
906
|
+
],
|
|
907
|
+
])(
|
|
908
|
+
"%s: govc reads flags only before the first argument",
|
|
909
|
+
(command: string, mentions: string) => {
|
|
910
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
911
|
+
command,
|
|
912
|
+
mentions,
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
expect(result.reason).toContain(
|
|
916
|
+
"reads flags only before the first argument",
|
|
917
|
+
);
|
|
918
|
+
},
|
|
919
|
+
);
|
|
920
|
+
|
|
921
|
+
test.each([
|
|
922
|
+
["govc ls -"],
|
|
923
|
+
["govc ls -- -l"],
|
|
924
|
+
["govc vm.info -- -e"],
|
|
925
|
+
["govc vm.power -on -- -web"],
|
|
926
|
+
["govc vm.power -on -"],
|
|
927
|
+
["govc host.maintenance.exit -- -esx"],
|
|
928
|
+
])("%s: an argument that starts with - is refused", (command: string) => {
|
|
929
|
+
expectDenied(command);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
test("after -- a name is a name", () => {
|
|
933
|
+
expect(govc("govc vm.power -on -- /DC1/vm/web-01").tier).toBe(
|
|
934
|
+
ResourceCommandTier.SafeWrite,
|
|
935
|
+
);
|
|
936
|
+
expect(govc("govc vm.info -r -- web-01").tier).toBe(
|
|
937
|
+
ResourceCommandTier.Read,
|
|
938
|
+
);
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
test.each([
|
|
942
|
+
["govc ls ''"],
|
|
943
|
+
["govc ls / ''"],
|
|
944
|
+
["govc vm.info ''"],
|
|
945
|
+
["govc vm.power -on ''"],
|
|
946
|
+
["govc host.maintenance.exit ''"],
|
|
947
|
+
])("%s: an empty argument is refused", (command: string) => {
|
|
948
|
+
expectDenied(command);
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
describe("argument counts", () => {
|
|
953
|
+
test.each([
|
|
954
|
+
["govc about extra", "takes no arguments"],
|
|
955
|
+
["govc version x", "takes no arguments"],
|
|
956
|
+
["govc tags.ls x", "takes no arguments"],
|
|
957
|
+
["govc host.service.ls esx-01", "takes no arguments"],
|
|
958
|
+
["govc host.date.info esx-01", "takes no arguments"],
|
|
959
|
+
["govc tasks a b", "takes at most 1 argument"],
|
|
960
|
+
["govc vm.info", "needs the VM to show"],
|
|
961
|
+
["govc vm.info -r", "needs the VM to show"],
|
|
962
|
+
["govc pool.info", "needs the pool to show"],
|
|
963
|
+
["govc metric.ls", "needs the object whose metrics"],
|
|
964
|
+
["govc metric.sample vm/x", "needs an object and at least one metric"],
|
|
965
|
+
["govc host.maintenance.enter", "exactly one host"],
|
|
966
|
+
["govc host.maintenance.enter esx-01 esx-02", "exactly one host"],
|
|
967
|
+
["govc host.maintenance.exit a b", "exactly one host"],
|
|
968
|
+
["govc vm.power -on", "needs the VM to act on"],
|
|
969
|
+
["govc vm.migrate -host h", "needs the VM to move"],
|
|
970
|
+
])("%s", (command: string, mentions: string) => {
|
|
971
|
+
expectDenied(command, mentions);
|
|
972
|
+
});
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
describe("vm.power semantics", () => {
|
|
976
|
+
test.each([
|
|
977
|
+
["govc vm.power web-01", "needs exactly one operation"],
|
|
978
|
+
["govc vm.power -on=false web-01", "needs exactly one operation"],
|
|
979
|
+
["govc vm.power -force web-01", "needs exactly one operation"],
|
|
980
|
+
["govc vm.power -dc DC1 web-01", "needs exactly one operation"],
|
|
981
|
+
["govc vm.power -on -off web-01", "not -on and -off"],
|
|
982
|
+
["govc vm.power -r -s web-01", "not -r and -s"],
|
|
983
|
+
[
|
|
984
|
+
"govc vm.power -off -reset -suspend web-01",
|
|
985
|
+
"not -off and -reset and -suspend",
|
|
986
|
+
],
|
|
987
|
+
["govc vm.power -on -force web-01", "-force goes only with -off or -reset"],
|
|
988
|
+
["govc vm.power -r -force web-01", "falls back to a hard reset"],
|
|
989
|
+
[
|
|
990
|
+
"govc vm.power -s -force web-01",
|
|
991
|
+
"falls back to a hard reset or power-off",
|
|
992
|
+
],
|
|
993
|
+
[
|
|
994
|
+
"govc vm.power -suspend -force web-01",
|
|
995
|
+
"-force goes only with -off or -reset",
|
|
996
|
+
],
|
|
997
|
+
[
|
|
998
|
+
"govc vm.power -force=true -r web-01",
|
|
999
|
+
"-force goes only with -off or -reset",
|
|
1000
|
+
],
|
|
1001
|
+
])("%s", (command: string, mentions: string) => {
|
|
1002
|
+
expectDenied(command, mentions);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
test("an operation turned off with =false does not count", () => {
|
|
1006
|
+
const result: ResourceCommandPolicyResult = govc(
|
|
1007
|
+
"govc vm.power -off=false -reset=false -on /DC1/vm/web-01",
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
expect(result.tier).toBe(ResourceCommandTier.SafeWrite);
|
|
1011
|
+
expect(result.reason).toContain("powers on");
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
test("-force=false is no force", () => {
|
|
1015
|
+
expect(govc("govc vm.power -on -force=false /DC1/vm/web-01").tier).toBe(
|
|
1016
|
+
ResourceCommandTier.SafeWrite,
|
|
1017
|
+
);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
test("vm.migrate needs a destination", () => {
|
|
1021
|
+
expectDenied("govc vm.migrate web-01", "needs a destination");
|
|
1022
|
+
expectDenied("govc vm.migrate -dc DC1 web-01", "needs a destination");
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
describe("write targets name exactly one object each", () => {
|
|
1027
|
+
test.each([
|
|
1028
|
+
["govc vm.power -on 'web-*'", "is a pattern"],
|
|
1029
|
+
["govc vm.power -on 'web-0?'", "is a pattern"],
|
|
1030
|
+
["govc vm.power -on 'web-[12]'", "is a pattern"],
|
|
1031
|
+
["govc vm.power -on '*'", "is a pattern"],
|
|
1032
|
+
["govc vm.power -on '/DC1/vm/*/web'", "is a pattern"],
|
|
1033
|
+
["govc vm.power -on 'web\\01'", "is a pattern"],
|
|
1034
|
+
["govc vm.power -off 'web-*'", "is a pattern"],
|
|
1035
|
+
["govc vm.power -on /DC1/vm/../web", 'a ".." path segment'],
|
|
1036
|
+
["govc vm.power -on ./web", 'a "." path segment'],
|
|
1037
|
+
["govc vm.power -on /DC1/vm/...", 'a "..." path segment'],
|
|
1038
|
+
["govc vm.power -on /DC1//vm/web", "an empty path segment"],
|
|
1039
|
+
["govc vm.power -on web/", "an empty path segment"],
|
|
1040
|
+
["govc vm.power -on /", "an empty path segment"],
|
|
1041
|
+
["govc vm.power -on ' web'", "leading or trailing spaces"],
|
|
1042
|
+
["govc vm.power -on 'web '", "leading or trailing spaces"],
|
|
1043
|
+
["govc vm.power -on web-01 web-01", "named twice"],
|
|
1044
|
+
["govc vm.migrate -host 'esx-*' web-01", "is a pattern"],
|
|
1045
|
+
["govc vm.migrate -ds 'ds[0-9]' web-01", "is a pattern"],
|
|
1046
|
+
["govc vm.migrate -pool ../p web-01", 'a ".." path segment'],
|
|
1047
|
+
["govc vm.migrate -host esx-02 'web-*'", "is a pattern"],
|
|
1048
|
+
["govc host.maintenance.enter 'esx-*'", "is a pattern"],
|
|
1049
|
+
["govc host.maintenance.exit 'esx-0?'", "is a pattern"],
|
|
1050
|
+
["govc vm.power -dc 'DC*' -on web-01", "is a pattern"],
|
|
1051
|
+
])("%s", (command: string, mentions: string) => {
|
|
1052
|
+
expectDenied(command, mentions);
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
test("names with spaces, colons and punctuation are one object", () => {
|
|
1056
|
+
expect(govc("govc vm.power -on 'My VM (prod) #1'").targets).toEqual([
|
|
1057
|
+
"My VM (prod) #1",
|
|
1058
|
+
]);
|
|
1059
|
+
expect(govc("govc vm.power -on 'web.example.com'").targets).toEqual([
|
|
1060
|
+
"web.example.com",
|
|
1061
|
+
]);
|
|
1062
|
+
expect(
|
|
1063
|
+
govc("govc host.maintenance.exit esx-01.example.com").targets,
|
|
1064
|
+
).toEqual(["esx-01.example.com"]);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
/*
|
|
1068
|
+
* govc resolves a slash-free word that reads as a managed object
|
|
1069
|
+
* reference to that object before it looks for a name, so the protected
|
|
1070
|
+
* targets (names) could never see which VM or host such a write changes.
|
|
1071
|
+
*/
|
|
1072
|
+
test.each([
|
|
1073
|
+
"govc vm.power -r vm-42",
|
|
1074
|
+
"govc vm.power -on vm-1001",
|
|
1075
|
+
"govc vm.power -off VirtualMachine:vm-42",
|
|
1076
|
+
"govc vm.power -reset virtualmachine:vm-42",
|
|
1077
|
+
"govc vm.power -s web-01 vm-42",
|
|
1078
|
+
"govc host.maintenance.exit host-12",
|
|
1079
|
+
"govc host.maintenance.enter HostSystem:host-12",
|
|
1080
|
+
"govc host.maintenance.exit HostSystem:ha-host",
|
|
1081
|
+
"govc vm.migrate -host host-12 web-01",
|
|
1082
|
+
"govc vm.migrate -pool resgroup-v10 web-01",
|
|
1083
|
+
"govc vm.migrate -ds datastore-15 web-01",
|
|
1084
|
+
"govc vm.migrate -host esx-02 VirtualMachine:vm-7",
|
|
1085
|
+
"govc vm.power -dc datacenter-3 -on web-01",
|
|
1086
|
+
"govc vm.power -on domain-c7",
|
|
1087
|
+
"govc vm.power -on group-v3",
|
|
1088
|
+
])(
|
|
1089
|
+
"a managed object reference is never a write's object: %s",
|
|
1090
|
+
(command: string) => {
|
|
1091
|
+
expectDenied(command, "is a managed object reference");
|
|
1092
|
+
},
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1095
|
+
test.each([
|
|
1096
|
+
["govc vm.power -r /DC1/vm/vm-42", "/DC1/vm/vm-42"],
|
|
1097
|
+
["govc vm.power -on vm-web-01", "vm-web-01"],
|
|
1098
|
+
["govc vm.power -on host-esx-mgmt", "host-esx-mgmt"],
|
|
1099
|
+
["govc vm.power -on vm42", "vm42"],
|
|
1100
|
+
["govc host.maintenance.exit esx-12.example.com", "esx-12.example.com"],
|
|
1101
|
+
])(
|
|
1102
|
+
"a name or a path is still one object: %s",
|
|
1103
|
+
(command: string, target: string) => {
|
|
1104
|
+
expect(govc(command).targets).toEqual([target]);
|
|
1105
|
+
},
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
test("reads may still name managed object references", () => {
|
|
1109
|
+
expect(govc("govc vm.info vm-42").tier).toBe(ResourceCommandTier.Read);
|
|
1110
|
+
expect(
|
|
1111
|
+
govc("govc object.collect -s VirtualMachine:vm-42 runtime.powerState")
|
|
1112
|
+
.tier,
|
|
1113
|
+
).toBe(ResourceCommandTier.Read);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
test("reads may still use patterns", () => {
|
|
1117
|
+
expect(govc("govc vm.info 'web-*'").tier).toBe(ResourceCommandTier.Read);
|
|
1118
|
+
expect(govc("govc ls '/DC1/vm/*'").tier).toBe(ResourceCommandTier.Read);
|
|
1119
|
+
expect(govc("govc events '/DC1/vm/web-[12]'").tier).toBe(
|
|
1120
|
+
ResourceCommandTier.Read,
|
|
1121
|
+
);
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
describe("find reads its own -KEY VALUE pairs after ROOT", () => {
|
|
1126
|
+
test.each([
|
|
1127
|
+
["govc find . -config.extraConfig x", "not a filter this policy allows"],
|
|
1128
|
+
[
|
|
1129
|
+
"govc find . -type m -config.extraConfig.guestinfo.password '*a*'",
|
|
1130
|
+
"not a filter this policy allows",
|
|
1131
|
+
],
|
|
1132
|
+
["govc find . -summary.config.annotation '*pw*'", "not a filter"],
|
|
1133
|
+
["govc find . -config.guestFullName '*'", "not a filter"],
|
|
1134
|
+
["govc find . -guest.ipAddress '10.*'", "not a filter"],
|
|
1135
|
+
["govc find . -Runtime.powerState poweredOn", "not a filter"],
|
|
1136
|
+
["govc find . -dc DC1", "not a filter"],
|
|
1137
|
+
["govc find . -l true", "not a filter"],
|
|
1138
|
+
["govc find . -maxdepth 1", "not a filter"],
|
|
1139
|
+
["govc find . --type m", "not a filter"],
|
|
1140
|
+
["govc find . -type=m x", "not a filter"],
|
|
1141
|
+
["govc find . -u https://evil/sdk", "applies it even after the ROOT path"],
|
|
1142
|
+
["govc find . -k true", "applies it even after the ROOT path"],
|
|
1143
|
+
["govc find . -debug true", "applies it even after the ROOT path"],
|
|
1144
|
+
["govc find . -type", "has no value"],
|
|
1145
|
+
["govc find . -type m -name", "has no value"],
|
|
1146
|
+
["govc find . runtime.powerState poweredOn", "is not a -KEY"],
|
|
1147
|
+
["govc find . /DC2", "has no value"],
|
|
1148
|
+
["govc find . /DC2 /DC3", "is not a -KEY"],
|
|
1149
|
+
["govc find -- -type m", "write the ROOT path"],
|
|
1150
|
+
["govc find '' -type m", "needs a ROOT path"],
|
|
1151
|
+
["govc find . -runtime.powerState -u", "needs a value"],
|
|
1152
|
+
["govc find . -name ''", "needs a value"],
|
|
1153
|
+
])("%s", (command: string, mentions: string) => {
|
|
1154
|
+
expectDenied(command, mentions);
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
test("the filter refusal lists the allowed properties", () => {
|
|
1158
|
+
expect(govc("govc find . -config.extraConfig x").reason).toContain(
|
|
1159
|
+
"runtime[.X]",
|
|
1160
|
+
);
|
|
1161
|
+
});
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
describe("object.collect reads only allowlisted properties", () => {
|
|
1165
|
+
test.each([
|
|
1166
|
+
["govc object.collect vm/web-01", "at least one property"],
|
|
1167
|
+
["govc object.collect -s vm/web-01", "at least one property"],
|
|
1168
|
+
["govc object.collect", "at least one property"],
|
|
1169
|
+
[
|
|
1170
|
+
"govc object.collect vm/web-01 config.extraConfig",
|
|
1171
|
+
"config.extraConfig can hold secrets",
|
|
1172
|
+
],
|
|
1173
|
+
["govc object.collect vm/web-01 config", "not a property"],
|
|
1174
|
+
["govc object.collect vm/web-01 summary", "not a property"],
|
|
1175
|
+
[
|
|
1176
|
+
"govc object.collect vm/web-01 summary.config.annotation",
|
|
1177
|
+
"not a property",
|
|
1178
|
+
],
|
|
1179
|
+
["govc object.collect vm/web-01 guest", "not a property"],
|
|
1180
|
+
["govc object.collect vm/web-01 guest.ipAddress", "not a property"],
|
|
1181
|
+
["govc object.collect vm/web-01 Runtime.powerState", "not a property"],
|
|
1182
|
+
["govc object.collect vm/web-01 runtime..powerState", "not a property"],
|
|
1183
|
+
["govc object.collect vm/web-01 'runtime.device[0]'", "not a property"],
|
|
1184
|
+
[
|
|
1185
|
+
"govc object.collect vm/web-01 runtime.powerState config",
|
|
1186
|
+
"not a property",
|
|
1187
|
+
],
|
|
1188
|
+
[
|
|
1189
|
+
"govc object.collect SessionManager:SessionManager sessionList",
|
|
1190
|
+
"not a property",
|
|
1191
|
+
],
|
|
1192
|
+
["govc object.collect - content", "managed object reference"],
|
|
1193
|
+
["govc object.collect -- - runtime", "managed object reference"],
|
|
1194
|
+
["govc object.collect '' runtime", "managed object reference"],
|
|
1195
|
+
[
|
|
1196
|
+
"govc object.collect -s vm/web-01 runtime.powerState -guest.guestOperationsReady true",
|
|
1197
|
+
"property filter",
|
|
1198
|
+
],
|
|
1199
|
+
])("%s", (command: string, mentions: string) => {
|
|
1200
|
+
expectDenied(command, mentions);
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
test.each([
|
|
1204
|
+
["name", true],
|
|
1205
|
+
["overallStatus", true],
|
|
1206
|
+
["summary.overallStatus", true],
|
|
1207
|
+
["triggeredAlarmState", true],
|
|
1208
|
+
["guest.toolsRunningStatus", true],
|
|
1209
|
+
["guest.guestState", true],
|
|
1210
|
+
["runtime", true],
|
|
1211
|
+
["runtime.powerState", true],
|
|
1212
|
+
["runtime.host", true],
|
|
1213
|
+
["summary.runtime", true],
|
|
1214
|
+
["summary.runtime.connectionState", true],
|
|
1215
|
+
["summary.quickStats", true],
|
|
1216
|
+
["summary.quickStats.guestMemoryUsage", true],
|
|
1217
|
+
["config", false],
|
|
1218
|
+
["config.extraConfig", false],
|
|
1219
|
+
["summary", false],
|
|
1220
|
+
["summary.config", false],
|
|
1221
|
+
["guest", false],
|
|
1222
|
+
["guest.net", false],
|
|
1223
|
+
["runtimeX", false],
|
|
1224
|
+
["runtime.", false],
|
|
1225
|
+
[".runtime", false],
|
|
1226
|
+
["summary.quickStatsX", false],
|
|
1227
|
+
["", false],
|
|
1228
|
+
["runtime powerState", false],
|
|
1229
|
+
])("isCollectableGovcProperty(%p) is %p", (property: string, ok: boolean) => {
|
|
1230
|
+
expect(isCollectableGovcProperty(property)).toBe(ok);
|
|
1231
|
+
});
|
|
1232
|
+
|
|
1233
|
+
test("a non-string property is not collectable", () => {
|
|
1234
|
+
expect(isCollectableGovcProperty(null as unknown as string)).toBe(false);
|
|
1235
|
+
expect(isCollectableGovcProperty(42 as unknown as string)).toBe(false);
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
describe("characters that could fool a reader", () => {
|
|
1240
|
+
test.each([
|
|
1241
|
+
["govc vm.power \u2013on web-01", "U+2013"],
|
|
1242
|
+
["govc vm.power \u2014off web-01", "U+2014"],
|
|
1243
|
+
["govc vm.power \u2212off web-01", "U+2212"],
|
|
1244
|
+
["govc vm.power \u2010on web-01", "U+2010"],
|
|
1245
|
+
["govc vm.power \uff0don web-01", "U+FF0D"],
|
|
1246
|
+
["govc vm.power \ufe63on web-01", "U+FE63"],
|
|
1247
|
+
["govc ls \u2013l /", "U+2013"],
|
|
1248
|
+
["govc vm.power -on \u2013force web-01", "U+2013"],
|
|
1249
|
+
["govc \u2013json about", "U+2013"],
|
|
1250
|
+
])("%s: a dash look-alike", (command: string, codePoint: string) => {
|
|
1251
|
+
const result: ResourceCommandPolicyResult = expectDenied(
|
|
1252
|
+
command,
|
|
1253
|
+
codePoint,
|
|
1254
|
+
);
|
|
1255
|
+
|
|
1256
|
+
expect(result.reason).toContain("type a plain hyphen");
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
test.each([
|
|
1260
|
+
["govc vm.power -on 'web\u200b01'", "U+200B"],
|
|
1261
|
+
["govc vm.power -on 'web\u202e10-bew'", "U+202E"],
|
|
1262
|
+
["govc vm.power -on 'web\u2066x'", "U+2066"],
|
|
1263
|
+
["govc vm.power -on\ufeff web-01", "U+FEFF"],
|
|
1264
|
+
["govc ls \u00ad-l /", "U+00AD"],
|
|
1265
|
+
["govc vm.power -on 'web\t01'", "U+0009"],
|
|
1266
|
+
["govc vm.power -on 'web\u007f'", "U+007F"],
|
|
1267
|
+
["govc vm.power -on 'web\u0085'", "U+0085"],
|
|
1268
|
+
["govc vm.info 'web\u200d01'", "U+200D"],
|
|
1269
|
+
["govc vm.power -on 'web\u{e0041}'", "U+E0041"],
|
|
1270
|
+
["govc vm.power -on 'web\u2028x'", "U+2028"],
|
|
1271
|
+
["govc vm.power -on 'web\u3164'", "U+3164"],
|
|
1272
|
+
["govc vm.power -on 'web\ufe0f'", "U+FE0F"],
|
|
1273
|
+
])(
|
|
1274
|
+
"%s: an invisible or control character",
|
|
1275
|
+
(command: string, codePoint: string) => {
|
|
1276
|
+
expectDenied(command, codePoint);
|
|
1277
|
+
},
|
|
1278
|
+
);
|
|
1279
|
+
|
|
1280
|
+
test("the reason never echoes the invisible character", () => {
|
|
1281
|
+
const result: ResourceCommandPolicyResult = govc(
|
|
1282
|
+
"govc vm.power -on 'web\u202e10-bew'",
|
|
1283
|
+
);
|
|
1284
|
+
|
|
1285
|
+
expect(result.reason).not.toContain("\u202e");
|
|
1286
|
+
expect(result.reason).toContain("a word of the command");
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
test("a newline or NUL in an argv word is refused by the tool too", () => {
|
|
1290
|
+
expect(direct(["govc", "vm.power", "-on", "web\n01"]).tier).toBe(
|
|
1291
|
+
ResourceCommandTier.Denied,
|
|
1292
|
+
);
|
|
1293
|
+
expect(direct(["govc", "vm.power", "-on", "web\u000001"]).tier).toBe(
|
|
1294
|
+
ResourceCommandTier.Denied,
|
|
1295
|
+
);
|
|
1296
|
+
expect(direct(["govc", "vm.power", "-on", "web\r"]).tier).toBe(
|
|
1297
|
+
ResourceCommandTier.Denied,
|
|
1298
|
+
);
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
test("ordinary non-ASCII names are fine", () => {
|
|
1302
|
+
expect(govc("govc vm.power -on '/DC1/vm/B\u00fcro-Server'").tier).toBe(
|
|
1303
|
+
ResourceCommandTier.SafeWrite,
|
|
1304
|
+
);
|
|
1305
|
+
expect(govc("govc vm.info '\u30b5\u30fc\u30d0\u30fc'").tier).toBe(
|
|
1306
|
+
ResourceCommandTier.Read,
|
|
1307
|
+
);
|
|
1308
|
+
});
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
describe("shell syntax, quoting and size", () => {
|
|
1312
|
+
test.each([
|
|
1313
|
+
["govc ls | grep web", "pipe"],
|
|
1314
|
+
["govc ls; govc env", "command separator"],
|
|
1315
|
+
[
|
|
1316
|
+
"govc vm.power -on web-01 && govc vm.power -off db",
|
|
1317
|
+
"background or chaining",
|
|
1318
|
+
],
|
|
1319
|
+
["govc ls > /tmp/out", "output redirect"],
|
|
1320
|
+
["govc ls < /tmp/in", "input redirect"],
|
|
1321
|
+
["govc ls $(whoami)", "command substitution"],
|
|
1322
|
+
["govc ls `id`", "command substitution"],
|
|
1323
|
+
['govc ls "$(id)"', "command substitution"],
|
|
1324
|
+
["govc vm.power -on 'web", "Unbalanced quotes"],
|
|
1325
|
+
["sudo govc about", "sudo"],
|
|
1326
|
+
["", "Empty command"],
|
|
1327
|
+
[" ", "Empty command"],
|
|
1328
|
+
])("%s", (command: string, mentions: string) => {
|
|
1329
|
+
expectDenied(command, mentions);
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
test("a quoted metacharacter is part of the name", () => {
|
|
1333
|
+
const result: ResourceCommandPolicyResult = govc(
|
|
1334
|
+
"govc vm.power -on '/DC1/vm/web|01'",
|
|
1335
|
+
);
|
|
1336
|
+
|
|
1337
|
+
expect(result.tier).toBe(ResourceCommandTier.SafeWrite);
|
|
1338
|
+
expect(result.targets).toEqual(["/DC1/vm/web|01"]);
|
|
1339
|
+
expect(result.displayCommand).toBe("govc vm.power -on '/DC1/vm/web|01'");
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
test("double and single quotes read the same", () => {
|
|
1343
|
+
expect(govc('govc vm.power -on "web 01"').targets).toEqual(["web 01"]);
|
|
1344
|
+
expect(govc("govc vm.power -on 'web 01'").targets).toEqual(["web 01"]);
|
|
1345
|
+
expect(govc("govc vm.power -on web\\ 01").targets).toEqual(["web 01"]);
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
test("very long commands are refused", () => {
|
|
1349
|
+
expectDenied(`govc ls ${"a".repeat(MAX_COMMAND_LENGTH_CHARS)}`);
|
|
1350
|
+
|
|
1351
|
+
const longArgv: Array<string> = [
|
|
1352
|
+
"govc",
|
|
1353
|
+
"ls",
|
|
1354
|
+
"b".repeat(MAX_COMMAND_LENGTH_CHARS),
|
|
1355
|
+
];
|
|
1356
|
+
expect(direct(longArgv).tier).toBe(ResourceCommandTier.Denied);
|
|
1357
|
+
expect(direct(longArgv).reason).toContain("character limit");
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
test("too many words are refused, the maximum is fine", () => {
|
|
1361
|
+
const names: Array<string> = Array.from(
|
|
1362
|
+
{ length: MAX_RESOURCE_COMMAND_TOKENS - 2 },
|
|
1363
|
+
(_value: unknown, index: number): string => {
|
|
1364
|
+
return `vm-${index}`;
|
|
1365
|
+
},
|
|
1366
|
+
);
|
|
1367
|
+
|
|
1368
|
+
expect(direct(["govc", "vm.info", ...names]).tier).toBe(
|
|
1369
|
+
ResourceCommandTier.Read,
|
|
1370
|
+
);
|
|
1371
|
+
expect(direct(["govc", "vm.info", ...names, "one-more"]).tier).toBe(
|
|
1372
|
+
ResourceCommandTier.Denied,
|
|
1373
|
+
);
|
|
1374
|
+
expect(direct(["govc", "vm.info", ...names, "one-more"]).reason).toContain(
|
|
1375
|
+
`at most ${MAX_RESOURCE_COMMAND_TOKENS} words`,
|
|
1376
|
+
);
|
|
1377
|
+
});
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
describe("targets and rendering", () => {
|
|
1381
|
+
test("args are the argv exactly as evaluated, never rewritten", () => {
|
|
1382
|
+
const result: ResourceCommandPolicyResult = govc(
|
|
1383
|
+
"govc vm.power --on=true web-01",
|
|
1384
|
+
);
|
|
1385
|
+
|
|
1386
|
+
expect(result.args).toEqual(["vm.power", "--on=true", "web-01"]);
|
|
1387
|
+
expect(result.displayCommand).toBe("govc vm.power --on=true web-01");
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
test.each([
|
|
1391
|
+
[
|
|
1392
|
+
["govc", "vm.power", "-on", "Windows Server 2019"],
|
|
1393
|
+
"govc vm.power -on 'Windows Server 2019'",
|
|
1394
|
+
],
|
|
1395
|
+
[["govc", "ls", "It's"], "govc ls 'It'\\''s'"],
|
|
1396
|
+
[["govc", "find", ".", "-name", "web-*"], "govc find . -name 'web-*'"],
|
|
1397
|
+
[["govc", "vm.info", "/DC1/vm/web-01"], "govc vm.info /DC1/vm/web-01"],
|
|
1398
|
+
[
|
|
1399
|
+
[
|
|
1400
|
+
"govc",
|
|
1401
|
+
"object.collect",
|
|
1402
|
+
"-s",
|
|
1403
|
+
"VirtualMachine:vm-42",
|
|
1404
|
+
"runtime.powerState",
|
|
1405
|
+
],
|
|
1406
|
+
"govc object.collect -s VirtualMachine:vm-42 runtime.powerState",
|
|
1407
|
+
],
|
|
1408
|
+
[["govc", "vm.power", "-on", "web;01"], "govc vm.power -on 'web;01'"],
|
|
1409
|
+
])(
|
|
1410
|
+
"%p renders as %p and round-trips",
|
|
1411
|
+
(argv: Array<string>, shown: string) => {
|
|
1412
|
+
const result: ResourceCommandPolicyResult = direct(argv);
|
|
1413
|
+
|
|
1414
|
+
expect(result.tier).not.toBe(ResourceCommandTier.Denied);
|
|
1415
|
+
expect(result.displayCommand).toBe(shown);
|
|
1416
|
+
expect(tokenizeResourceCommand(shown).argv).toEqual(argv);
|
|
1417
|
+
},
|
|
1418
|
+
);
|
|
1419
|
+
|
|
1420
|
+
test("a Denied command still renders", () => {
|
|
1421
|
+
const result: ResourceCommandPolicyResult = direct([
|
|
1422
|
+
"govc",
|
|
1423
|
+
"vm.destroy",
|
|
1424
|
+
"My VM",
|
|
1425
|
+
]);
|
|
1426
|
+
|
|
1427
|
+
expect(result.displayCommand).toBe("govc vm.destroy 'My VM'");
|
|
1428
|
+
expect(result.program).toBe("govc");
|
|
1429
|
+
expect(result.args).toEqual(["vm.destroy", "My VM"]);
|
|
1430
|
+
expect(result.verb).toBe("");
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
test("writes target their VMs and hosts, never their destinations", () => {
|
|
1434
|
+
expect(govc("govc vm.migrate -host esx-02 -ds ds2 web-01").targets).toEqual(
|
|
1435
|
+
["web-01"],
|
|
1436
|
+
);
|
|
1437
|
+
expect(govc("govc vm.power -dc DC1 -off web-01 db-01").targets).toEqual([
|
|
1438
|
+
"web-01",
|
|
1439
|
+
"db-01",
|
|
1440
|
+
]);
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
test("the agent's argv and the server's string agree", () => {
|
|
1444
|
+
const command: string =
|
|
1445
|
+
"govc vm.power -off -force '/DC1/vm/Prod VMs/web-01'";
|
|
1446
|
+
const fromString: ResourceCommandPolicyResult = govc(command);
|
|
1447
|
+
const fromArgv: ResourceCommandPolicyResult =
|
|
1448
|
+
ResourceCommandPolicy.evaluateArgv({
|
|
1449
|
+
resourceType: VCENTER,
|
|
1450
|
+
argv: tokenizeResourceCommand(command).argv!,
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
expect(fromArgv).toEqual(fromString);
|
|
1454
|
+
expect(fromString.targets).toEqual(["/DC1/vm/Prod VMs/web-01"]);
|
|
1455
|
+
});
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
describe("totality", () => {
|
|
1459
|
+
test.each([
|
|
1460
|
+
[[]],
|
|
1461
|
+
[["govc"]],
|
|
1462
|
+
[null],
|
|
1463
|
+
[undefined],
|
|
1464
|
+
[[1, {}, null]],
|
|
1465
|
+
[["govc", 42]],
|
|
1466
|
+
["govc about"],
|
|
1467
|
+
[{ 0: "govc", 1: "about", length: 2 }],
|
|
1468
|
+
[["govc", "vm.power", undefined, "web"]],
|
|
1469
|
+
])("the tool denies %p without throwing", (argv: unknown) => {
|
|
1470
|
+
const result: ResourceCommandPolicyResult = direct(argv);
|
|
1471
|
+
|
|
1472
|
+
expect(result.tier).toBe(ResourceCommandTier.Denied);
|
|
1473
|
+
expect(result.targets).toEqual([]);
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
test("an argv that throws when read is Denied, not thrown", () => {
|
|
1477
|
+
const hostile: Array<string> = ["govc", "about"];
|
|
1478
|
+
|
|
1479
|
+
Object.defineProperty(hostile, 1, {
|
|
1480
|
+
get(): string {
|
|
1481
|
+
throw new Error("boom");
|
|
1482
|
+
},
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
let result: ResourceCommandPolicyResult | undefined = undefined;
|
|
1486
|
+
|
|
1487
|
+
expect(() => {
|
|
1488
|
+
result = direct(hostile);
|
|
1489
|
+
}).not.toThrow();
|
|
1490
|
+
expect(result!.tier).toBe(ResourceCommandTier.Denied);
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
test("the tool never changes the caller's argv", () => {
|
|
1494
|
+
const argv: Array<string> = ["govc", "vm.power", "-on", "web-01"];
|
|
1495
|
+
const copy: Array<string> = argv.slice();
|
|
1496
|
+
|
|
1497
|
+
direct(argv);
|
|
1498
|
+
expect(argv).toEqual(copy);
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
/*
|
|
1502
|
+
* A deterministic fuzz over the words a model (or an attacker) would mix:
|
|
1503
|
+
* whatever comes out, the invariants hold and nothing throws.
|
|
1504
|
+
*/
|
|
1505
|
+
test("random commands keep every invariant", () => {
|
|
1506
|
+
const commands: Array<string> = [
|
|
1507
|
+
"vm.power",
|
|
1508
|
+
"vm.power",
|
|
1509
|
+
"vm.power",
|
|
1510
|
+
"vm.info",
|
|
1511
|
+
"vm.migrate",
|
|
1512
|
+
"host.maintenance.enter",
|
|
1513
|
+
"host.maintenance.exit",
|
|
1514
|
+
"find",
|
|
1515
|
+
"object.collect",
|
|
1516
|
+
"ls",
|
|
1517
|
+
"events",
|
|
1518
|
+
"about",
|
|
1519
|
+
"env",
|
|
1520
|
+
"-json",
|
|
1521
|
+
];
|
|
1522
|
+
const vocabulary: Array<string> = [
|
|
1523
|
+
"-on",
|
|
1524
|
+
"-off",
|
|
1525
|
+
"-r",
|
|
1526
|
+
"-s",
|
|
1527
|
+
"-reset",
|
|
1528
|
+
"-suspend",
|
|
1529
|
+
"-force",
|
|
1530
|
+
"-json",
|
|
1531
|
+
"-dc",
|
|
1532
|
+
"-host",
|
|
1533
|
+
"-ds",
|
|
1534
|
+
"-pool",
|
|
1535
|
+
"-type",
|
|
1536
|
+
"-name",
|
|
1537
|
+
"-n",
|
|
1538
|
+
"-l",
|
|
1539
|
+
"-e",
|
|
1540
|
+
"-u",
|
|
1541
|
+
"--u=x",
|
|
1542
|
+
"-k",
|
|
1543
|
+
"-debug",
|
|
1544
|
+
"-dump",
|
|
1545
|
+
"--",
|
|
1546
|
+
"-",
|
|
1547
|
+
"",
|
|
1548
|
+
"web-01",
|
|
1549
|
+
"web-02",
|
|
1550
|
+
"web-*",
|
|
1551
|
+
"/DC1/vm/web-01",
|
|
1552
|
+
"/DC1/vm/db-01",
|
|
1553
|
+
"vm/web-02",
|
|
1554
|
+
"/DC2/vm/app",
|
|
1555
|
+
".",
|
|
1556
|
+
"runtime.powerState",
|
|
1557
|
+
"config.extraConfig",
|
|
1558
|
+
"-runtime.powerState",
|
|
1559
|
+
"poweredOn",
|
|
1560
|
+
"m",
|
|
1561
|
+
"5",
|
|
1562
|
+
"true",
|
|
1563
|
+
"-on=false",
|
|
1564
|
+
"-off=true",
|
|
1565
|
+
"\u2013on",
|
|
1566
|
+
];
|
|
1567
|
+
const deniedFlag: RegExp =
|
|
1568
|
+
/^--?(?:u|k|cert|key|debug|trace|verbose|dump|xml|h|help|persist-session|vim-namespace|vim-version|tls-[a-z-]+)(?:=|$)/;
|
|
1569
|
+
let seed: number = 20260929;
|
|
1570
|
+
const next: () => number = (): number => {
|
|
1571
|
+
seed = (seed * 1103515245 + 12345) % 2147483648;
|
|
1572
|
+
// The high bits: an LCG's low bits repeat with a short period.
|
|
1573
|
+
return Math.floor(seed / 65536);
|
|
1574
|
+
};
|
|
1575
|
+
const counts: Record<string, number> = {};
|
|
1576
|
+
const violations: Array<string> = [];
|
|
1577
|
+
|
|
1578
|
+
for (let round: number = 0; round < 10000; round++) {
|
|
1579
|
+
const argv: Array<string> = ["govc", commands[next() % commands.length]!];
|
|
1580
|
+
const length: number = next() % 5;
|
|
1581
|
+
|
|
1582
|
+
for (let i: number = 0; i < length; i++) {
|
|
1583
|
+
argv.push(vocabulary[next() % vocabulary.length]!);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const shown: string = JSON.stringify(argv);
|
|
1587
|
+
let result: ResourceCommandPolicyResult;
|
|
1588
|
+
|
|
1589
|
+
try {
|
|
1590
|
+
result = ResourceCommandPolicy.evaluateArgv({
|
|
1591
|
+
resourceType: VCENTER,
|
|
1592
|
+
argv,
|
|
1593
|
+
});
|
|
1594
|
+
} catch {
|
|
1595
|
+
violations.push(`${shown} threw`);
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
counts[result.tier] = (counts[result.tier] || 0) + 1;
|
|
1600
|
+
|
|
1601
|
+
const fail: (what: string) => void = (what: string): void => {
|
|
1602
|
+
violations.push(`${shown} -> ${result.tier}: ${what}`);
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
if (!ALL_TIERS.includes(result.tier)) {
|
|
1606
|
+
fail("unknown tier");
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
if (result.program !== "govc") {
|
|
1610
|
+
fail("program is not govc");
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
if (result.tier === ResourceCommandTier.Denied) {
|
|
1614
|
+
if (result.targets.length !== 0) {
|
|
1615
|
+
fail("a Denied result has targets");
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
if (JSON.stringify(result.args) !== JSON.stringify(argv.slice(1))) {
|
|
1622
|
+
fail("args were rewritten");
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
if (
|
|
1626
|
+
result.args.some((word: string): boolean => {
|
|
1627
|
+
return deniedFlag.test(word);
|
|
1628
|
+
})
|
|
1629
|
+
) {
|
|
1630
|
+
fail("a denied flag got through");
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
if (
|
|
1634
|
+
result.tier === ResourceCommandTier.Read &&
|
|
1635
|
+
(result.targets.length !== 0 || result.requiresHuman !== undefined)
|
|
1636
|
+
) {
|
|
1637
|
+
fail("a read has targets or requiresHuman");
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
if (
|
|
1641
|
+
result.tier === ResourceCommandTier.SafeWrite &&
|
|
1642
|
+
(result.verb !== "vm.power" ||
|
|
1643
|
+
result.targets.length !== 1 ||
|
|
1644
|
+
result.requiresHuman !== undefined ||
|
|
1645
|
+
!result.args.some((word: string): boolean => {
|
|
1646
|
+
return word === "-on" || word === "-r";
|
|
1647
|
+
}))
|
|
1648
|
+
) {
|
|
1649
|
+
fail("a SafeWrite that is not one VM powered on or rebooted");
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
if (
|
|
1653
|
+
result.tier === ResourceCommandTier.SafeWrite ||
|
|
1654
|
+
result.tier === ResourceCommandTier.RiskyWrite
|
|
1655
|
+
) {
|
|
1656
|
+
if (result.targets.length === 0) {
|
|
1657
|
+
fail("a write without targets");
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
for (const target of result.targets) {
|
|
1661
|
+
if (PATTERN_CHARACTERS.test(target) || target.startsWith("-")) {
|
|
1662
|
+
fail(`target ${target} is not one object`);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
if (
|
|
1668
|
+
result.requiresHuman === true &&
|
|
1669
|
+
result.tier !== ResourceCommandTier.RiskyWrite
|
|
1670
|
+
) {
|
|
1671
|
+
fail("requiresHuman on a tier other than RiskyWrite");
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
expect(violations).toEqual([]);
|
|
1676
|
+
|
|
1677
|
+
// The vocabulary reaches every tier.
|
|
1678
|
+
expect(counts[ResourceCommandTier.Read]).toBeGreaterThan(0);
|
|
1679
|
+
expect(counts[ResourceCommandTier.SafeWrite]).toBeGreaterThan(0);
|
|
1680
|
+
expect(counts[ResourceCommandTier.RiskyWrite]).toBeGreaterThan(0);
|
|
1681
|
+
expect(counts[ResourceCommandTier.Denied]).toBeGreaterThan(0);
|
|
1682
|
+
});
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1685
|
+
describe("the dispatcher's ladder for a VMware vCenter", () => {
|
|
1686
|
+
test("a read is AutoApproved (callers route it to the read tool)", () => {
|
|
1687
|
+
const verdict: ResourceAutoExecutionVerdict = autoExecution(
|
|
1688
|
+
"govc vm.info web-01",
|
|
1689
|
+
);
|
|
1690
|
+
|
|
1691
|
+
expect(verdict.verdict).toBe(
|
|
1692
|
+
AiRemediationCommandPolicyVerdict.AutoApproved,
|
|
1693
|
+
);
|
|
1694
|
+
expect(verdict.tier).toBe(ResourceCommandTier.Read);
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
test("a SafeWrite runs unattended", () => {
|
|
1698
|
+
for (const command of [
|
|
1699
|
+
"govc vm.power -on /DC1/vm/web-01",
|
|
1700
|
+
"govc vm.power -r /DC1/vm/web-01",
|
|
1701
|
+
]) {
|
|
1702
|
+
const verdict: ResourceAutoExecutionVerdict = autoExecution(command);
|
|
1703
|
+
|
|
1704
|
+
expect(verdict.verdict).toBe(
|
|
1705
|
+
AiRemediationCommandPolicyVerdict.AutoApproved,
|
|
1706
|
+
);
|
|
1707
|
+
expect(verdict.tier).toBe(ResourceCommandTier.SafeWrite);
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
test("a RiskyWrite asks, unless bypassed or allowlisted", () => {
|
|
1712
|
+
expect(autoExecution("govc vm.power -off web-01").verdict).toBe(
|
|
1713
|
+
AiRemediationCommandPolicyVerdict.RequiresApproval,
|
|
1714
|
+
);
|
|
1715
|
+
expect(
|
|
1716
|
+
autoExecution("govc vm.power -off web-01", { bypassApproval: true })
|
|
1717
|
+
.verdict,
|
|
1718
|
+
).toBe(AiRemediationCommandPolicyVerdict.AutoApproved);
|
|
1719
|
+
|
|
1720
|
+
const allowlisted: ResourceAutoExecutionVerdict = autoExecution(
|
|
1721
|
+
"govc vm.power -off web-01",
|
|
1722
|
+
{ allowlistPatterns: ["govc vm.power -off *"] },
|
|
1723
|
+
);
|
|
1724
|
+
|
|
1725
|
+
expect(allowlisted.verdict).toBe(
|
|
1726
|
+
AiRemediationCommandPolicyVerdict.AutoApproved,
|
|
1727
|
+
);
|
|
1728
|
+
expect(allowlisted.reason).toBe(
|
|
1729
|
+
"Matched the resource's command allowlist.",
|
|
1730
|
+
);
|
|
1731
|
+
expect(
|
|
1732
|
+
autoExecution("govc vm.power -off web-01", {
|
|
1733
|
+
allowlistPatterns: ["govc vm.power -off web-02"],
|
|
1734
|
+
}).verdict,
|
|
1735
|
+
).toBe(AiRemediationCommandPolicyVerdict.RequiresApproval);
|
|
1736
|
+
expect(
|
|
1737
|
+
autoExecution("govc host.maintenance.exit esx-01", {
|
|
1738
|
+
allowlistPatterns: ["govc host.maintenance.exit *"],
|
|
1739
|
+
}).verdict,
|
|
1740
|
+
).toBe(AiRemediationCommandPolicyVerdict.AutoApproved);
|
|
1741
|
+
expect(autoExecution("govc vm.power -on web-01 web-02").verdict).toBe(
|
|
1742
|
+
AiRemediationCommandPolicyVerdict.RequiresApproval,
|
|
1743
|
+
);
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
test.each([
|
|
1747
|
+
["govc vm.migrate -host esx-02 web-01", "govc vm.migrate -host * *"],
|
|
1748
|
+
["govc host.maintenance.enter esx-01", "govc host.maintenance.enter *"],
|
|
1749
|
+
])(
|
|
1750
|
+
"%s always asks a human, bypass and allowlist notwithstanding",
|
|
1751
|
+
(command: string, pattern: string) => {
|
|
1752
|
+
const verdict: ResourceAutoExecutionVerdict = autoExecution(command, {
|
|
1753
|
+
allowlistPatterns: [pattern],
|
|
1754
|
+
bypassApproval: true,
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
expect(verdict.verdict).toBe(
|
|
1758
|
+
AiRemediationCommandPolicyVerdict.RequiresApproval,
|
|
1759
|
+
);
|
|
1760
|
+
expect(verdict.requiresHuman).toBe(true);
|
|
1761
|
+
expect(verdict.reason).toContain("always needs a human");
|
|
1762
|
+
},
|
|
1763
|
+
);
|
|
1764
|
+
|
|
1765
|
+
test.each([
|
|
1766
|
+
["govc vm.destroy web-01"],
|
|
1767
|
+
["govc -u https://evil/sdk about"],
|
|
1768
|
+
["govc vm.power -on 'web-*'"],
|
|
1769
|
+
["govc guest.run -vm web-01 /bin/sh"],
|
|
1770
|
+
])("%s is Denied whatever the settings", (command: string) => {
|
|
1771
|
+
const verdict: ResourceAutoExecutionVerdict = autoExecution(command, {
|
|
1772
|
+
allowlistPatterns: ["govc vm.power -on *"],
|
|
1773
|
+
bypassApproval: true,
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
expect(verdict.verdict).toBe(AiRemediationCommandPolicyVerdict.Denied);
|
|
1777
|
+
expect(verdict.reason).toContain("cannot run even with human approval");
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
describe("the allowlist for a VMware vCenter", () => {
|
|
1782
|
+
test("matches word for word", () => {
|
|
1783
|
+
const patterns: Array<string> = ["govc vm.power -off *"];
|
|
1784
|
+
|
|
1785
|
+
expect(
|
|
1786
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1787
|
+
resourceType: VCENTER,
|
|
1788
|
+
command: "govc vm.power -off web-01",
|
|
1789
|
+
patterns,
|
|
1790
|
+
}),
|
|
1791
|
+
).toBe(true);
|
|
1792
|
+
expect(
|
|
1793
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1794
|
+
resourceType: VCENTER,
|
|
1795
|
+
command: "govc vm.power -off web-01 web-02",
|
|
1796
|
+
patterns,
|
|
1797
|
+
}),
|
|
1798
|
+
).toBe(false);
|
|
1799
|
+
expect(
|
|
1800
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1801
|
+
resourceType: VCENTER,
|
|
1802
|
+
command: "govc vm.power -off=true web-01",
|
|
1803
|
+
patterns,
|
|
1804
|
+
}),
|
|
1805
|
+
).toBe(false);
|
|
1806
|
+
expect(
|
|
1807
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1808
|
+
resourceType: VCENTER,
|
|
1809
|
+
command: "govc vm.power -reset web-01",
|
|
1810
|
+
patterns,
|
|
1811
|
+
}),
|
|
1812
|
+
).toBe(false);
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
test("an entry that could never pre-approve anything is skipped", () => {
|
|
1816
|
+
expect(
|
|
1817
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1818
|
+
resourceType: VCENTER,
|
|
1819
|
+
command: "govc ls /",
|
|
1820
|
+
patterns: ["govc ls *"],
|
|
1821
|
+
}),
|
|
1822
|
+
).toBe(false);
|
|
1823
|
+
expect(
|
|
1824
|
+
ResourceCommandPolicy.matchesAllowlist({
|
|
1825
|
+
resourceType: VCENTER,
|
|
1826
|
+
command: "govc vm.destroy web-01",
|
|
1827
|
+
patterns: ["govc vm.destroy *"],
|
|
1828
|
+
}),
|
|
1829
|
+
).toBe(false);
|
|
1830
|
+
});
|
|
1831
|
+
|
|
1832
|
+
test.each([
|
|
1833
|
+
["govc vm.power -off *"],
|
|
1834
|
+
["govc vm.power -on *"],
|
|
1835
|
+
["govc vm.power -r *"],
|
|
1836
|
+
["govc vm.power -reset -force *"],
|
|
1837
|
+
["govc vm.power -s web-01"],
|
|
1838
|
+
["govc vm.power -off * *"],
|
|
1839
|
+
["govc host.maintenance.exit *"],
|
|
1840
|
+
["govc vm.migrate -host * *"],
|
|
1841
|
+
])("%s is a valid entry", (pattern: string) => {
|
|
1842
|
+
expect(
|
|
1843
|
+
ResourceCommandPolicy.describeAllowlistPatternProblem({
|
|
1844
|
+
resourceType: VCENTER,
|
|
1845
|
+
pattern,
|
|
1846
|
+
}),
|
|
1847
|
+
).toBeNull();
|
|
1848
|
+
});
|
|
1849
|
+
|
|
1850
|
+
test.each([
|
|
1851
|
+
["govc about -json", "read-only command"],
|
|
1852
|
+
["govc ls *", "read-only command"],
|
|
1853
|
+
["govc vm.info *", "read-only command"],
|
|
1854
|
+
["govc vm.destroy *", "can never match a command that runs"],
|
|
1855
|
+
["govc vm.power -off 'web-*'", "can never match a command that runs"],
|
|
1856
|
+
["govc vm.power -off * -u", "can never match a command that runs"],
|
|
1857
|
+
["govc vm.power -u * -off web-01", "can never match a command that runs"],
|
|
1858
|
+
["govc vm.power -r -force *", "can never match a command that runs"],
|
|
1859
|
+
["govc * -off web-01", "where the command goes"],
|
|
1860
|
+
["govc vm.power", "fewer than two words"],
|
|
1861
|
+
["ceph osd out 1", "does not start with a program"],
|
|
1862
|
+
["", "cannot be blank"],
|
|
1863
|
+
])("%s is refused as an entry", (pattern: string, mentions: string) => {
|
|
1864
|
+
expect(
|
|
1865
|
+
ResourceCommandPolicy.describeAllowlistPatternProblem({
|
|
1866
|
+
resourceType: VCENTER,
|
|
1867
|
+
pattern,
|
|
1868
|
+
}),
|
|
1869
|
+
).toContain(mentions);
|
|
1870
|
+
});
|
|
1871
|
+
|
|
1872
|
+
test.each([
|
|
1873
|
+
["govc vm.power -off *", true],
|
|
1874
|
+
["govc vm.power -on *", true],
|
|
1875
|
+
["govc host.maintenance.exit *", true],
|
|
1876
|
+
["govc vm.power -off web-01", false],
|
|
1877
|
+
// The * is the destination, not a VM the entry lets the AI change.
|
|
1878
|
+
["govc vm.migrate -host * web", false],
|
|
1879
|
+
["govc ls *", false],
|
|
1880
|
+
])("isBroadAllowlistPattern(%p) is %p", (pattern: string, broad: boolean) => {
|
|
1881
|
+
expect(
|
|
1882
|
+
ResourceCommandPolicy.isBroadAllowlistPattern({
|
|
1883
|
+
resourceType: VCENTER,
|
|
1884
|
+
pattern,
|
|
1885
|
+
}),
|
|
1886
|
+
).toBe(broad);
|
|
1887
|
+
});
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
describe("the agent's write scope for a VMware vCenter", () => {
|
|
1891
|
+
function refusal(
|
|
1892
|
+
command: string,
|
|
1893
|
+
posture: {
|
|
1894
|
+
allowWrites?: boolean;
|
|
1895
|
+
writeTargets?: Array<string>;
|
|
1896
|
+
protectedTargets?: Array<string>;
|
|
1897
|
+
} = {},
|
|
1898
|
+
): string | null {
|
|
1899
|
+
return ResourceCommandPolicy.getWriteScopeRefusal({
|
|
1900
|
+
result: govc(command),
|
|
1901
|
+
allowWrites: posture.allowWrites !== false,
|
|
1902
|
+
writeTargets: posture.writeTargets || [],
|
|
1903
|
+
protectedTargets: posture.protectedTargets || [],
|
|
1904
|
+
resourceType: VCENTER,
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
test("a read is never refused", () => {
|
|
1909
|
+
expect(refusal("govc vm.info web-01", { allowWrites: false })).toBeNull();
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
test("a read-only agent refuses every change", () => {
|
|
1913
|
+
const message: string | null = refusal("govc vm.power -on web-01", {
|
|
1914
|
+
allowWrites: false,
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
expect(message).toContain("VMware AI agent");
|
|
1918
|
+
expect(message).toContain(`${RESOURCE_AI_ALLOW_WRITES_ENV}=true`);
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
test("any target when no write targets are set", () => {
|
|
1922
|
+
expect(refusal("govc vm.power -off web-01")).toBeNull();
|
|
1923
|
+
expect(refusal("govc host.maintenance.exit esx-01")).toBeNull();
|
|
1924
|
+
});
|
|
1925
|
+
|
|
1926
|
+
test("write targets are globs over the names as written", () => {
|
|
1927
|
+
expect(
|
|
1928
|
+
refusal("govc vm.power -on web-01", { writeTargets: ["web-*"] }),
|
|
1929
|
+
).toBeNull();
|
|
1930
|
+
expect(
|
|
1931
|
+
refusal("govc vm.power -on /DC1/vm/web-01", {
|
|
1932
|
+
writeTargets: ["/DC1/vm/*"],
|
|
1933
|
+
}),
|
|
1934
|
+
).toBeNull();
|
|
1935
|
+
|
|
1936
|
+
const outside: string | null = refusal("govc vm.power -on db-01", {
|
|
1937
|
+
writeTargets: ["web-*"],
|
|
1938
|
+
});
|
|
1939
|
+
|
|
1940
|
+
expect(outside).toContain("db-01");
|
|
1941
|
+
expect(outside).toContain(RESOURCE_AI_WRITE_TARGETS_ENV);
|
|
1942
|
+
|
|
1943
|
+
expect(
|
|
1944
|
+
refusal("govc vm.power -on web-01 db-01", { writeTargets: ["web-*"] }),
|
|
1945
|
+
).toContain("db-01");
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
test("the agent's own VM is protected", () => {
|
|
1949
|
+
const message: string | null = refusal(
|
|
1950
|
+
"govc vm.power -off oneuptime-ai-agent",
|
|
1951
|
+
{ protectedTargets: ["oneuptime-ai-agent"] },
|
|
1952
|
+
);
|
|
1953
|
+
|
|
1954
|
+
expect(message).toContain("protects");
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
test("a Denied command is refused here too", () => {
|
|
1958
|
+
expect(refusal("govc vm.destroy web-01")).toContain("denied");
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
/*
|
|
1962
|
+
* OneUptime refuses what the agent refuses: a protected name covers every
|
|
1963
|
+
* inventory path that ends in it, as the agent reads it (vCenter reaches
|
|
1964
|
+
* one VM by many paths).
|
|
1965
|
+
*/
|
|
1966
|
+
test("a protected name covers every inventory path that ends in it", () => {
|
|
1967
|
+
for (const [command, protectedTarget] of [
|
|
1968
|
+
["govc vm.power -off /DC/vm/infra/vcsa", "vcsa"],
|
|
1969
|
+
["govc vm.power -off /DC/host/cluster-1/Resources/VCSA", "vcsa"],
|
|
1970
|
+
["govc vm.power -r /DC/vm/critical-db", "critical-*"],
|
|
1971
|
+
["govc host.maintenance.exit ESX-01", "esx-*"],
|
|
1972
|
+
["govc vm.power -off vcsa", "/DC/vm/infra/vcsa"],
|
|
1973
|
+
] as Array<[string, string]>) {
|
|
1974
|
+
const message: string | null = refusal(command, {
|
|
1975
|
+
protectedTargets: [protectedTarget],
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
expect(message).toContain("protects");
|
|
1979
|
+
expect(message).toContain(protectedTarget);
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
expect(
|
|
1983
|
+
refusal("govc vm.power -off /DC/vm/infra/vcsa-old", {
|
|
1984
|
+
protectedTargets: ["vcsa"],
|
|
1985
|
+
}),
|
|
1986
|
+
).toBeNull();
|
|
1987
|
+
});
|
|
1988
|
+
|
|
1989
|
+
test("the name comparison is govc's alone: other resource types compare whole words", () => {
|
|
1990
|
+
const docker: ResourceCommandPolicyResult =
|
|
1991
|
+
ResourceCommandPolicy.evaluateCommand({
|
|
1992
|
+
resourceType: AiResourceType.DockerHost,
|
|
1993
|
+
command: "docker restart vcsa",
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
expect(
|
|
1997
|
+
ResourceCommandPolicy.getWriteScopeRefusal({
|
|
1998
|
+
result: { ...docker, targets: ["/DC/vm/infra/vcsa"] },
|
|
1999
|
+
allowWrites: true,
|
|
2000
|
+
writeTargets: [],
|
|
2001
|
+
protectedTargets: ["vcsa"],
|
|
2002
|
+
resourceType: AiResourceType.DockerHost,
|
|
2003
|
+
}),
|
|
2004
|
+
).toBeNull();
|
|
2005
|
+
});
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
describe("isProtectedGovcTarget", () => {
|
|
2009
|
+
test.each([
|
|
2010
|
+
["vcsa", "vcsa", true],
|
|
2011
|
+
["/DC/vm/infra/vcsa", "vcsa", true],
|
|
2012
|
+
["VCSA", "vcsa", true],
|
|
2013
|
+
["/DC/vm/db-01", "db-*", true],
|
|
2014
|
+
["web-01", "/DC/vm/web/web-01", true],
|
|
2015
|
+
["web-01", "/DC/vm/infra/*", true],
|
|
2016
|
+
["web-01", "vcsa", false],
|
|
2017
|
+
["/DC/vm/vcsa/web-01", "vcsa", false],
|
|
2018
|
+
["", "vcsa", false],
|
|
2019
|
+
["vcsa", "", false],
|
|
2020
|
+
["/", "*", false],
|
|
2021
|
+
])("%s against %s", (target: string, entry: string, expected: boolean) => {
|
|
2022
|
+
expect(isProtectedGovcTarget(target, entry)).toBe(expected);
|
|
2023
|
+
});
|
|
2024
|
+
|
|
2025
|
+
test("the inventory name is the last path segment", () => {
|
|
2026
|
+
expect(govcInventoryName("/DC/vm/infra/vcsa")).toBe("vcsa");
|
|
2027
|
+
expect(govcInventoryName("vcsa")).toBe("vcsa");
|
|
2028
|
+
expect(govcInventoryName(" /DC/vm/web/ ")).toBe("web");
|
|
2029
|
+
expect(govcInventoryName("/")).toBe("");
|
|
2030
|
+
});
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
describe("the guides", () => {
|
|
2034
|
+
test("the read guide names every read command", () => {
|
|
2035
|
+
for (const command of [
|
|
2036
|
+
"govc about",
|
|
2037
|
+
"govc version",
|
|
2038
|
+
"govc datacenter.info",
|
|
2039
|
+
"govc ls",
|
|
2040
|
+
"govc find",
|
|
2041
|
+
"govc vm.info",
|
|
2042
|
+
"govc host.info",
|
|
2043
|
+
"govc host.service.ls",
|
|
2044
|
+
"govc host.date.info",
|
|
2045
|
+
"govc datastore.info",
|
|
2046
|
+
"govc pool.info",
|
|
2047
|
+
"govc events",
|
|
2048
|
+
"govc tasks",
|
|
2049
|
+
"govc metric.ls",
|
|
2050
|
+
"govc metric.sample",
|
|
2051
|
+
"govc object.collect",
|
|
2052
|
+
"govc tags.ls",
|
|
2053
|
+
]) {
|
|
2054
|
+
expect(GovcCommandPolicy.readCommandGuide).toContain(command);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
expect(GovcCommandPolicy.readCommandGuide).toContain(
|
|
2058
|
+
"-json and -e are refused",
|
|
2059
|
+
);
|
|
2060
|
+
expect(GovcCommandPolicy.readCommandGuide).not.toContain("Unavailable");
|
|
2061
|
+
expect(GovcCommandPolicy.readCommandGuide).not.toContain("not implemented");
|
|
2062
|
+
expect(ResourceCommandPolicy.getReadCommandGuide(VCENTER)).toBe(
|
|
2063
|
+
GovcCommandPolicy.readCommandGuide,
|
|
2064
|
+
);
|
|
2065
|
+
});
|
|
2066
|
+
|
|
2067
|
+
test("the write guide names every tier", () => {
|
|
2068
|
+
const guide: string = GovcCommandPolicy.writeCommandGuide;
|
|
2069
|
+
|
|
2070
|
+
expect(guide).toContain("govc vm.power -on VM");
|
|
2071
|
+
expect(guide).toContain("govc vm.power -r VM");
|
|
2072
|
+
expect(guide).toContain("SafeWrite");
|
|
2073
|
+
expect(guide).toContain("RiskyWrite");
|
|
2074
|
+
expect(guide).toContain("always needs a human");
|
|
2075
|
+
expect(guide).toContain("govc host.maintenance.exit HOST");
|
|
2076
|
+
expect(guide).toContain("govc vm.migrate");
|
|
2077
|
+
expect(guide).not.toContain("not implemented");
|
|
2078
|
+
expect(ResourceCommandPolicy.getWriteCommandGuide(VCENTER)).toBe(guide);
|
|
2079
|
+
});
|
|
2080
|
+
|
|
2081
|
+
test("every guide line is a markdown bullet", () => {
|
|
2082
|
+
for (const guide of [
|
|
2083
|
+
GovcCommandPolicy.readCommandGuide,
|
|
2084
|
+
GovcCommandPolicy.writeCommandGuide,
|
|
2085
|
+
]) {
|
|
2086
|
+
for (const line of guide.split("\n")) {
|
|
2087
|
+
expect(line.startsWith("- ")).toBe(true);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
2091
|
+
|
|
2092
|
+
test.each([
|
|
2093
|
+
[
|
|
2094
|
+
"govc find . -type m -runtime.powerState poweredOff",
|
|
2095
|
+
ResourceCommandTier.Read,
|
|
2096
|
+
],
|
|
2097
|
+
[
|
|
2098
|
+
"govc object.collect -s /DC/vm/web-01 runtime.powerState",
|
|
2099
|
+
ResourceCommandTier.Read,
|
|
2100
|
+
],
|
|
2101
|
+
[
|
|
2102
|
+
"govc metric.sample -n 12 /DC/vm/web-01 cpu.usage.average mem.usage.average",
|
|
2103
|
+
ResourceCommandTier.Read,
|
|
2104
|
+
],
|
|
2105
|
+
])(
|
|
2106
|
+
"the guide's example %s does what it says",
|
|
2107
|
+
(command: string, tier: ResourceCommandTier) => {
|
|
2108
|
+
expect(GovcCommandPolicy.readCommandGuide).toContain(command);
|
|
2109
|
+
expect(govc(command).tier).toBe(tier);
|
|
2110
|
+
},
|
|
2111
|
+
);
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
describe("govc output redaction", () => {
|
|
2115
|
+
function redact(text: string): string {
|
|
2116
|
+
return redactResourceCommandOutput({
|
|
2117
|
+
resourceType: VCENTER,
|
|
2118
|
+
program: "govc",
|
|
2119
|
+
text,
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
test("govc has a hook", () => {
|
|
2124
|
+
expect(getResourceOutputRedactionHooks("govc").length).toBeGreaterThan(0);
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
test.each([
|
|
2128
|
+
[
|
|
2129
|
+
'{"key":"guestinfo.userdata","value":"I2Nsb3VkLWNvbmZpZwpwYXNzd29yZDogaHVudGVyMg"}',
|
|
2130
|
+
"I2Nsb3VkLWNvbmZpZwpwYXNzd29yZDogaHVudGVyMg",
|
|
2131
|
+
],
|
|
2132
|
+
[
|
|
2133
|
+
'{"key":"guestinfo.metadata","value":{"_typeName":"string","_value":"instance-id: x\\nsecret-thing: hunter2"}}',
|
|
2134
|
+
"hunter2",
|
|
2135
|
+
],
|
|
2136
|
+
[
|
|
2137
|
+
'{\n "key": "guestinfo.vendordata",\n "value": {\n "_typeName": "string",\n "_value": "c3VwZXJzZWNyZXQ"\n }\n}',
|
|
2138
|
+
"c3VwZXJzZWNyZXQ",
|
|
2139
|
+
],
|
|
2140
|
+
['{"value":"s3cr3t-value","key":"guestinfo.ovfEnv"}', "s3cr3t-value"],
|
|
2141
|
+
[
|
|
2142
|
+
'{"value":{"_typeName":"string","_value":"abc123xyz"},"key":"guestinfo.foo"}',
|
|
2143
|
+
"abc123xyz",
|
|
2144
|
+
],
|
|
2145
|
+
['{"Key":"guestinfo.x","Value":"abcd1234"}', "abcd1234"],
|
|
2146
|
+
['{"KEY": "GuestInfo.Y", "VALUE": "zyxw9876"}', "zyxw9876"],
|
|
2147
|
+
['{"key":"RemoteDisplay.vnc.password","value":"vncpass1"}', "vncpass1"],
|
|
2148
|
+
['{"key":"guestinfo.pin","value":987654}', "987654"],
|
|
2149
|
+
[
|
|
2150
|
+
'{"key":"guestinfo.pin","value":{"_typeName":"int","_value":424242}}',
|
|
2151
|
+
"424242",
|
|
2152
|
+
],
|
|
2153
|
+
[" guestinfo.userdata:\tI2Nsb3Vk\n", "I2Nsb3Vk"],
|
|
2154
|
+
["guestinfo.hostname = web-01-secretish\n", "web-01-secretish"],
|
|
2155
|
+
['Key: "guestinfo.password",\n Value: "hunter2",', "hunter2"],
|
|
2156
|
+
['types.OptionValue{Key:"guestinfo.token", Value:"tok-9999"}', "tok-9999"],
|
|
2157
|
+
[
|
|
2158
|
+
'{"type":"UserLoginSessionEvent","sessionId":"52a1b2c3-d4e5-f6a7-b8c9-d0e1f2a3b4c5","userName":"VSPHERE.LOCAL\\\\admin"}',
|
|
2159
|
+
"52a1b2c3-d4e5-f6a7-b8c9-d0e1f2a3b4c5",
|
|
2160
|
+
],
|
|
2161
|
+
['{"ticket":"cst-VCT-52ab-xyz","host":"esx-01"}', "cst-VCT-52ab-xyz"],
|
|
2162
|
+
['{"cloneTicket":"cst-clone-1234"}', "cst-clone-1234"],
|
|
2163
|
+
[
|
|
2164
|
+
'{"guestinfo.userdata": "I2Nsb3VkZGF0YQ", "other": "x"}',
|
|
2165
|
+
"I2Nsb3VkZGF0YQ",
|
|
2166
|
+
],
|
|
2167
|
+
['{"extraConfig":{"guestinfo.metadata":"bWV0YWRhdGE"}}', "bWV0YWRhdGE"],
|
|
2168
|
+
])("masks %p", (text: string, secret: string) => {
|
|
2169
|
+
const redacted: string = redact(text);
|
|
2170
|
+
|
|
2171
|
+
expect(redacted).not.toContain(secret);
|
|
2172
|
+
expect(redacted).toContain(RESOURCE_REDACTED_MARKER);
|
|
2173
|
+
});
|
|
2174
|
+
|
|
2175
|
+
test("keeps the keys and the names that are not secret", () => {
|
|
2176
|
+
const text: string =
|
|
2177
|
+
'[{"key":"guestinfo.userdata","value":"U0VDUkVU"},{"key":"ethernet0.present","value":"TRUE"},{"key":"svga.present","value":"TRUE"}]';
|
|
2178
|
+
const redacted: string = redact(text);
|
|
2179
|
+
|
|
2180
|
+
expect(redacted).toContain('"key":"guestinfo.userdata"');
|
|
2181
|
+
expect(redacted).toContain('{"key":"ethernet0.present","value":"TRUE"}');
|
|
2182
|
+
expect(redacted).toContain('{"key":"svga.present","value":"TRUE"}');
|
|
2183
|
+
expect(redacted).not.toContain("U0VDUkVU");
|
|
2184
|
+
expect(JSON.parse(redacted)).toHaveLength(3);
|
|
2185
|
+
});
|
|
2186
|
+
|
|
2187
|
+
test("keeps the rest of a login event", () => {
|
|
2188
|
+
const redacted: string = redact(
|
|
2189
|
+
'{"type":"UserLoginSessionEvent","sessionId":"52a1b2c3-d4e5","userName":"admin","ipAddress":"10.0.0.9"}',
|
|
2190
|
+
);
|
|
2191
|
+
|
|
2192
|
+
expect(redacted).toContain('"userName":"admin"');
|
|
2193
|
+
expect(redacted).toContain('"ipAddress":"10.0.0.9"');
|
|
2194
|
+
expect(redacted).not.toContain("52a1b2c3-d4e5");
|
|
2195
|
+
});
|
|
2196
|
+
|
|
2197
|
+
test("counts what it masks and is idempotent", () => {
|
|
2198
|
+
const first: { text: string; redactionCount: number } =
|
|
2199
|
+
redactResourceCommandOutputWithCount({
|
|
2200
|
+
resourceType: VCENTER,
|
|
2201
|
+
program: "govc",
|
|
2202
|
+
text: '{"key":"guestinfo.a","value":"one1"}\n{"key":"guestinfo.b","value":"two2"}',
|
|
2203
|
+
});
|
|
2204
|
+
|
|
2205
|
+
expect(first.redactionCount).toBeGreaterThanOrEqual(2);
|
|
2206
|
+
|
|
2207
|
+
const second: { text: string; redactionCount: number } =
|
|
2208
|
+
redactResourceCommandOutputWithCount({
|
|
2209
|
+
resourceType: VCENTER,
|
|
2210
|
+
program: "govc",
|
|
2211
|
+
text: first.text,
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
expect(second.text).toBe(first.text);
|
|
2215
|
+
expect(second.redactionCount).toBe(0);
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
test("empty option values stay as they are", () => {
|
|
2219
|
+
const text: string = '{"key":"guestinfo.userdata","value":""}';
|
|
2220
|
+
|
|
2221
|
+
expect(redact(text)).toBe(text);
|
|
2222
|
+
});
|
|
2223
|
+
|
|
2224
|
+
test("the generic rules still run on govc output", () => {
|
|
2225
|
+
expect(redact("password=hunter2")).toBe("password=[redacted]");
|
|
2226
|
+
expect(redact('{"password":"hunter2"}')).not.toContain("hunter2");
|
|
2227
|
+
});
|
|
2228
|
+
|
|
2229
|
+
test.each([
|
|
2230
|
+
[
|
|
2231
|
+
[
|
|
2232
|
+
"Name: web-01",
|
|
2233
|
+
" Path: /DC1/vm/web-01",
|
|
2234
|
+
" Guest name: Ubuntu Linux (64-bit)",
|
|
2235
|
+
" Memory: 4096MB",
|
|
2236
|
+
" CPU: 2 vCPU(s)",
|
|
2237
|
+
" Power state: poweredOn",
|
|
2238
|
+
" IP address: 10.0.0.5",
|
|
2239
|
+
" Host: esx-01",
|
|
2240
|
+
].join("\n"),
|
|
2241
|
+
],
|
|
2242
|
+
[
|
|
2243
|
+
[
|
|
2244
|
+
"FullName: VMware vCenter Server 8.0.2 build-22385739",
|
|
2245
|
+
"Name: VMware VirtualCenter Server",
|
|
2246
|
+
"Vendor: VMware, Inc.",
|
|
2247
|
+
"Version: 8.0.2",
|
|
2248
|
+
"API type: VirtualCenter",
|
|
2249
|
+
"API version: 8.0.2.0",
|
|
2250
|
+
].join("\n"),
|
|
2251
|
+
],
|
|
2252
|
+
[
|
|
2253
|
+
'[{"createdTime":"2026-09-29T10:00:00Z","category":"warning","message":"web-01 on esx-01 in DC1 is powered off","type":"VmPoweredOffEvent","key":4242}]',
|
|
2254
|
+
],
|
|
2255
|
+
["/DC1/vm/web-01\n/DC1/vm/web-02\n/DC1/host/Cluster1/esx-01"],
|
|
2256
|
+
])("ordinary govc output passes through unchanged", (text: string) => {
|
|
2257
|
+
expect(redact(text)).toBe(text);
|
|
2258
|
+
});
|
|
2259
|
+
});
|