@oneuptime/common 14.0.9 → 14.0.11

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.
Files changed (1146) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +127 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  3. package/Models/DatabaseModels/CephCluster.ts +225 -0
  4. package/Models/DatabaseModels/CloudResource.ts +12 -0
  5. package/Models/DatabaseModels/DatabaseServer.ts +225 -0
  6. package/Models/DatabaseModels/DockerHost.ts +225 -0
  7. package/Models/DatabaseModels/DockerSwarmCluster.ts +225 -0
  8. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  9. package/Models/DatabaseModels/GlobalOidc.ts +0 -4
  10. package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
  11. package/Models/DatabaseModels/GlobalSso.ts +0 -4
  12. package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
  13. package/Models/DatabaseModels/Host.ts +225 -0
  14. package/Models/DatabaseModels/IncidentForm.ts +594 -0
  15. package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
  16. package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
  17. package/Models/DatabaseModels/Index.ts +16 -0
  18. package/Models/DatabaseModels/IoTFleet.ts +12 -0
  19. package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
  20. package/Models/DatabaseModels/MessageQueue.ts +1102 -0
  21. package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
  22. package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
  23. package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
  24. package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
  25. package/Models/DatabaseModels/PodmanHost.ts +225 -0
  26. package/Models/DatabaseModels/Project.ts +59 -0
  27. package/Models/DatabaseModels/ProjectOidc.ts +0 -4
  28. package/Models/DatabaseModels/ProjectSso.ts +0 -4
  29. package/Models/DatabaseModels/ProxmoxCluster.ts +225 -0
  30. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  31. package/Models/DatabaseModels/RumApplication.ts +12 -0
  32. package/Models/DatabaseModels/RunnerJob.ts +144 -0
  33. package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
  34. package/Models/DatabaseModels/Service.ts +12 -0
  35. package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
  36. package/Models/DatabaseModels/StatusPageSso.ts +0 -4
  37. package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
  38. package/Models/DatabaseModels/VMwareVCenter.ts +225 -0
  39. package/Server/API/AIChatAPI.ts +13 -114
  40. package/Server/API/AIInvestigationAPI.ts +3 -3
  41. package/Server/API/AIInvestigationConversationAPI.ts +266 -0
  42. package/Server/API/AutoRemediationAPI.ts +392 -1
  43. package/Server/API/IncidentFormAPI.ts +376 -0
  44. package/Server/API/ProjectOIDC.ts +16 -25
  45. package/Server/API/ProjectSSO.ts +16 -24
  46. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  47. package/Server/API/StatusPageAPI.ts +42 -77
  48. package/Server/Enterprise/EnterpriseEdition.ts +93 -28
  49. package/Server/Enterprise/EnterpriseFeature.ts +25 -3
  50. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
  51. package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
  52. package/Server/EnvironmentConfig.ts +1 -1
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  54. package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
  55. package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
  56. package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
  57. package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
  58. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  59. package/Server/Infrastructure/Semaphore.ts +22 -0
  60. package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
  61. package/Server/Middleware/TelemetryIngest.ts +15 -5
  62. package/Server/Middleware/UserAuthorization.ts +16 -38
  63. package/Server/Services/AIService.ts +10 -13
  64. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  65. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  66. package/Server/Services/CephClusterService.ts +109 -1
  67. package/Server/Services/DatabaseServerService.ts +116 -1
  68. package/Server/Services/DockerHostService.ts +109 -1
  69. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  70. package/Server/Services/HostService.ts +126 -4
  71. package/Server/Services/IncidentCustomFieldService.ts +112 -2
  72. package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
  73. package/Server/Services/IncidentEpisodeService.ts +10 -1
  74. package/Server/Services/IncidentFormService.ts +1566 -0
  75. package/Server/Services/IncidentFormSubmissionService.ts +97 -0
  76. package/Server/Services/IncidentGroupingEngineService.ts +45 -8
  77. package/Server/Services/IncidentService.ts +123 -5
  78. package/Server/Services/IncidentTemplateService.ts +22 -0
  79. package/Server/Services/Index.ts +14 -0
  80. package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
  81. package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
  82. package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
  83. package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
  84. package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
  85. package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
  86. package/Server/Services/MessageQueueService.ts +1463 -0
  87. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  88. package/Server/Services/OpenTelemetryIngestService.ts +37 -4
  89. package/Server/Services/PodmanHostService.ts +109 -1
  90. package/Server/Services/ProjectService.ts +0 -59
  91. package/Server/Services/ProxmoxClusterService.ts +110 -1
  92. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  93. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  94. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  95. package/Server/Services/RunnerJobService.ts +611 -4
  96. package/Server/Services/StatusPagePrivateUserService.ts +1 -4
  97. package/Server/Services/StatusPageService.ts +1 -51
  98. package/Server/Services/TeamComplianceSettingService.ts +163 -42
  99. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  100. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  101. package/Server/Services/UserNotificationRuleService.ts +8 -1
  102. package/Server/Services/VMwareVCenterService.ts +110 -1
  103. package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
  104. package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
  105. package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
  106. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
  107. package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
  108. package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
  109. package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
  110. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
  111. package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
  112. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
  113. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  114. package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
  115. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
  116. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
  117. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
  118. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
  119. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1442 -34
  120. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +968 -34
  121. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  122. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +622 -0
  123. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  124. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  125. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  126. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  127. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +882 -0
  128. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +125 -12
  129. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +99 -15
  130. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +99 -15
  131. package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
  132. package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
  133. package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
  134. package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
  135. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
  136. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  137. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  138. package/Server/Utils/EditionEnforcement.ts +19 -202
  139. package/Server/Utils/LLM/LLMService.ts +259 -63
  140. package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
  141. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  142. package/Server/Utils/SameOriginRequest.ts +203 -0
  143. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  144. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  145. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  146. package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
  147. package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
  148. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
  149. package/Server/Utils/VendorAssets.ts +3 -3
  150. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  151. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  152. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  153. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +301 -225
  154. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +38 -16
  155. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  156. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +376 -144
  157. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  158. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  159. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  160. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  161. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  162. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  163. package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
  164. package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
  165. package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
  166. package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
  167. package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
  168. package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
  169. package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
  170. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
  171. package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
  172. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
  173. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
  174. package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
  175. package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
  176. package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
  177. package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
  178. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +267 -12
  179. package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
  180. package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
  181. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
  182. package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
  183. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +23 -6
  184. package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
  185. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
  186. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
  187. package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
  188. package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
  189. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
  190. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
  191. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
  192. package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
  193. package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
  194. package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
  195. package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
  196. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
  197. package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
  198. package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
  199. package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
  200. package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
  201. package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
  202. package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
  203. package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
  204. package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
  205. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
  206. package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
  207. package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
  208. package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
  209. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
  210. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  211. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
  212. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
  213. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
  214. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
  215. package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
  216. package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
  217. package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
  218. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
  219. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
  220. package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
  221. package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
  222. package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
  223. package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
  224. package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
  225. package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
  226. package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
  227. package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
  228. package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
  229. package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
  230. package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
  231. package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
  232. package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
  233. package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
  234. package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
  235. package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
  236. package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
  237. package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
  238. package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
  239. package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
  240. package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
  241. package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
  242. package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
  243. package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
  244. package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
  245. package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
  246. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
  247. package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
  248. package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
  249. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
  250. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
  251. package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
  252. package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
  253. package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
  254. package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
  255. package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
  256. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  257. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  258. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  259. package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
  260. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  261. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +2010 -0
  262. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1455 -0
  263. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  264. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  265. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
  266. package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
  267. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
  268. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
  269. package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
  270. package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
  271. package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
  272. package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
  273. package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
  274. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  275. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
  276. package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
  277. package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
  278. package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
  279. package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
  280. package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
  281. package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
  282. package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
  283. package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
  284. package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
  285. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
  286. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +61 -10
  287. package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
  288. package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
  289. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  290. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  291. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  292. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
  293. package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
  294. package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
  295. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  296. package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
  297. package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
  298. package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
  299. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
  300. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  301. package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
  302. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
  303. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
  304. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
  305. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
  306. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
  307. package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
  308. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  309. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  310. package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
  311. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  312. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
  313. package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
  314. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  315. package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
  316. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
  317. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
  318. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
  319. package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
  320. package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
  321. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
  322. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  323. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  324. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  325. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  326. package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
  327. package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
  328. package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
  329. package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
  330. package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
  331. package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
  332. package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
  333. package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
  334. package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
  335. package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
  336. package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
  337. package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
  338. package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
  339. package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
  340. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
  341. package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
  342. package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
  343. package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
  344. package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
  345. package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
  346. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  347. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  348. package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
  349. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
  350. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
  351. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
  352. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  353. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  354. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  355. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  356. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  357. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  358. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  359. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  360. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  361. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  362. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
  363. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
  364. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
  365. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  366. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  367. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  368. package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
  369. package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
  370. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  371. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
  372. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
  373. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
  374. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  375. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  376. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
  377. package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
  378. package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
  379. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
  380. package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
  381. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
  382. package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
  383. package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
  384. package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
  385. package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
  386. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
  387. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
  388. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
  389. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +694 -0
  390. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  391. package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
  392. package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
  393. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
  394. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
  395. package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
  396. package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
  397. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  398. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  399. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
  400. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1251 -0
  401. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  402. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  403. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +1004 -0
  404. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  405. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  406. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  407. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +834 -0
  408. package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
  409. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
  410. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
  411. package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
  412. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  413. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  414. package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
  415. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  416. package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
  417. package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
  418. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  419. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  420. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
  421. package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
  422. package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
  423. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
  424. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
  425. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  426. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  427. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  428. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  429. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  430. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  431. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  432. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  433. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  434. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  435. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  436. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  437. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  438. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  439. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  440. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  441. package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
  442. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  443. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  444. package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
  445. package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
  446. package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
  447. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
  448. package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
  449. package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
  450. package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
  451. package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
  452. package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
  453. package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
  454. package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
  455. package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
  456. package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
  457. package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
  458. package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
  459. package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
  460. package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
  461. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  462. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  463. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  464. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  465. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +519 -0
  466. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  467. package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
  468. package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
  469. package/Tests/Types/XML.test.ts +51 -0
  470. package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
  471. package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
  472. package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
  473. package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
  474. package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
  475. package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
  476. package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
  477. package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
  478. package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
  479. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
  480. package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
  481. package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
  482. package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
  483. package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
  484. package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
  485. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
  486. package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
  487. package/Tests/UI/Components/Icon.test.tsx +32 -0
  488. package/Tests/UI/Components/Input.test.tsx +31 -0
  489. package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
  490. package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
  491. package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
  492. package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
  493. package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
  494. package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
  495. package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
  496. package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
  497. package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
  498. package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
  499. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  500. package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
  501. package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
  502. package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
  503. package/Tests/UI/Components/TextArea.test.tsx +19 -0
  504. package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
  505. package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
  506. package/Tests/UI/EsbuildConfig.test.ts +115 -39
  507. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
  508. package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
  509. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  510. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
  511. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
  512. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  513. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  514. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  515. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  516. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  517. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  518. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  519. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  520. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  521. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  522. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  523. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  524. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  525. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  526. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  527. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  528. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  529. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  530. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  531. package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
  532. package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
  533. package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
  534. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  535. package/Types/AI/AIAgentRunLimits.ts +146 -0
  536. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  537. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  538. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
  539. package/Types/Code/CodeType.ts +2 -0
  540. package/Types/Code/JsonSyntax.ts +923 -0
  541. package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
  542. package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
  543. package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
  544. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
  545. package/Types/Icon/IconProp.ts +2 -0
  546. package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
  547. package/Types/Incident/IncidentFormPublic.ts +1145 -0
  548. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
  549. package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
  550. package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
  551. package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
  552. package/Types/MessageQueue/MessagingSystem.ts +518 -0
  553. package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
  554. package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
  555. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  556. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  557. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  558. package/Types/Permission.ts +310 -0
  559. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  560. package/Types/ResourceAiAgent/ResourceAiAccess.ts +580 -0
  561. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  562. package/Types/Rules/RuleRun.ts +4 -0
  563. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  564. package/Types/Runbook/RunbookStepType.ts +29 -0
  565. package/Types/Team/ComplianceNotificationChannel.ts +4 -3
  566. package/Types/Team/ComplianceRule.ts +71 -18
  567. package/Types/Team/ComplianceRuleType.ts +2 -2
  568. package/Types/Team/TeamComplianceStatus.ts +6 -2
  569. package/Types/Telemetry/EntityType.ts +16 -0
  570. package/Types/Telemetry/LockedFilterDetail.ts +22 -0
  571. package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
  572. package/Types/XML.ts +26 -0
  573. package/UI/Components/CodeEditor/CodeEditor.css +379 -0
  574. package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
  575. package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
  576. package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
  577. package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
  578. package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
  579. package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
  580. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
  581. package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
  582. package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
  583. package/UI/Components/Forms/Fields/FormField.tsx +8 -0
  584. package/UI/Components/Forms/Types/Field.ts +8 -0
  585. package/UI/Components/Icon/Icon.tsx +18 -0
  586. package/UI/Components/Input/Input.tsx +11 -4
  587. package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
  588. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
  589. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  590. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
  591. package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
  592. package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
  593. package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
  594. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
  595. package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
  596. package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
  597. package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
  598. package/UI/Components/Table/EmptyTableMessage.ts +81 -0
  599. package/UI/Components/Table/Table.tsx +50 -21
  600. package/UI/Components/Table/TableRow.tsx +9 -1
  601. package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
  602. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
  603. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
  604. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
  605. package/UI/Components/TextArea/TextArea.tsx +9 -2
  606. package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
  607. package/UI/Styles/Theme.css +8 -0
  608. package/UI/esbuild-config.js +2 -32
  609. package/Utils/AI/InvestigationReport.ts +121 -15
  610. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  611. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  612. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  613. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  614. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  615. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  616. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  617. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  618. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  619. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  620. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  621. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  622. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  623. package/Utils/LabelRuleImportExport.ts +2 -0
  624. package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
  625. package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
  626. package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
  627. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  628. package/Utils/Telemetry/EntityKey.ts +48 -0
  629. package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
  630. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  631. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  632. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  633. package/build/dist/Models/DatabaseModels/CephCluster.js +231 -0
  634. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  635. package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
  636. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  637. package/build/dist/Models/DatabaseModels/DatabaseServer.js +231 -0
  638. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  639. package/build/dist/Models/DatabaseModels/DockerHost.js +231 -0
  640. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  641. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +231 -0
  642. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  643. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  644. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  645. package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
  646. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  647. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
  648. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  649. package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
  650. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  651. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
  652. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  653. package/build/dist/Models/DatabaseModels/Host.js +231 -0
  654. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  655. package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
  656. package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
  657. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
  658. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
  659. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
  660. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  661. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  662. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  663. package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
  664. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  665. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
  666. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  667. package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
  668. package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
  669. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
  670. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
  671. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
  672. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
  673. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
  674. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
  675. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
  676. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
  677. package/build/dist/Models/DatabaseModels/PodmanHost.js +231 -0
  678. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  679. package/build/dist/Models/DatabaseModels/Project.js +61 -0
  680. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  681. package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
  682. package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
  683. package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
  684. package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
  685. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +231 -0
  686. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  687. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  688. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  689. package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
  690. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  691. package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
  692. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  693. package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
  694. package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
  695. package/build/dist/Models/DatabaseModels/Service.js +12 -0
  696. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  697. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
  698. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  699. package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
  700. package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
  701. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
  702. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  703. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +231 -0
  704. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  705. package/build/dist/Server/API/AIChatAPI.js +9 -103
  706. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  707. package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
  708. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  709. package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
  710. package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
  711. package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
  712. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  713. package/build/dist/Server/API/IncidentFormAPI.js +245 -0
  714. package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
  715. package/build/dist/Server/API/ProjectOIDC.js +16 -24
  716. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  717. package/build/dist/Server/API/ProjectSSO.js +16 -24
  718. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  719. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  720. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  721. package/build/dist/Server/API/StatusPageAPI.js +42 -75
  722. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  723. package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
  724. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
  725. package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
  726. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
  727. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
  728. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
  729. package/build/dist/Server/EnvironmentConfig.js +1 -1
  730. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  731. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  732. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
  733. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
  734. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
  735. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
  736. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
  737. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
  738. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
  739. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
  740. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  741. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  742. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  743. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  744. package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
  745. package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
  746. package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
  747. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  748. package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
  749. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  750. package/build/dist/Server/Services/AIService.js +10 -13
  751. package/build/dist/Server/Services/AIService.js.map +1 -1
  752. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  753. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  754. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  755. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  756. package/build/dist/Server/Services/CephClusterService.js +104 -0
  757. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  758. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  759. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  760. package/build/dist/Server/Services/DockerHostService.js +104 -0
  761. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  762. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  763. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  764. package/build/dist/Server/Services/HostService.js +114 -2
  765. package/build/dist/Server/Services/HostService.js.map +1 -1
  766. package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
  767. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  768. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
  769. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  770. package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
  771. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  772. package/build/dist/Server/Services/IncidentFormService.js +1202 -0
  773. package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
  774. package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
  775. package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
  776. package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
  777. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  778. package/build/dist/Server/Services/IncidentService.js +106 -4
  779. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  780. package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
  781. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  782. package/build/dist/Server/Services/Index.js +14 -0
  783. package/build/dist/Server/Services/Index.js.map +1 -1
  784. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
  785. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
  786. package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
  787. package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
  788. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
  789. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
  790. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
  791. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
  792. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
  793. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
  794. package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
  795. package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
  796. package/build/dist/Server/Services/MessageQueueService.js +1140 -0
  797. package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
  798. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  799. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  800. package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
  801. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  802. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  803. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  804. package/build/dist/Server/Services/ProjectService.js +0 -54
  805. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  806. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  807. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  808. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  809. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  810. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  811. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  812. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  813. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  814. package/build/dist/Server/Services/RunnerJobService.js +344 -5
  815. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  816. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
  817. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  818. package/build/dist/Server/Services/StatusPageService.js +0 -46
  819. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  820. package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
  821. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  822. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  823. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  824. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  825. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  826. package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
  827. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  828. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  829. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  830. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
  831. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  832. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
  833. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  834. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
  835. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  836. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
  837. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  838. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
  839. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
  840. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  841. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
  842. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
  843. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
  844. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  845. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
  846. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
  847. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
  848. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  849. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  850. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
  851. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
  852. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
  853. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  854. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
  855. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  856. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
  857. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  858. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
  859. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  860. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1036 -32
  861. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  862. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +661 -41
  863. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  864. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  865. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  866. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +354 -0
  867. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  868. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  869. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  870. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  871. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  872. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  873. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  874. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  875. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  876. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +602 -0
  877. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  878. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +90 -14
  879. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  880. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +73 -10
  881. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  882. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +73 -10
  883. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  884. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
  885. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  886. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
  887. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
  888. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
  889. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
  890. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
  891. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
  892. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
  893. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  894. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  895. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  896. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  897. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  898. package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
  899. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
  900. package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
  901. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  902. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
  903. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
  904. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  905. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  906. package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
  907. package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
  908. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  909. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  910. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  911. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  912. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  913. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  914. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
  915. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  916. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
  917. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
  918. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
  919. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  920. package/build/dist/Server/Utils/VendorAssets.js +3 -3
  921. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  922. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  923. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  924. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  925. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  926. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  927. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +186 -210
  928. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  929. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +22 -16
  930. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  931. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  932. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  933. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +273 -111
  934. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  935. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  936. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  937. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  938. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  939. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  940. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  941. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  942. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  943. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  944. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  945. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  946. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  947. package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
  948. package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
  949. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  950. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  951. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  952. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  953. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
  954. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  955. package/build/dist/Types/Code/CodeType.js +2 -0
  956. package/build/dist/Types/Code/CodeType.js.map +1 -1
  957. package/build/dist/Types/Code/JsonSyntax.js +604 -0
  958. package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
  959. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
  960. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
  961. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
  962. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
  963. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
  964. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
  965. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
  966. package/build/dist/Types/Icon/IconProp.js +2 -0
  967. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  968. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
  969. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
  970. package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
  971. package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
  972. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
  973. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  974. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
  975. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
  976. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
  977. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
  978. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
  979. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
  980. package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
  981. package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
  982. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
  983. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
  984. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
  985. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
  986. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  987. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  988. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  989. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  990. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  991. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  992. package/build/dist/Types/Permission.js +274 -0
  993. package/build/dist/Types/Permission.js.map +1 -1
  994. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  995. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  996. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +281 -0
  997. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  998. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
  999. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  1000. package/build/dist/Types/Rules/RuleRun.js +4 -0
  1001. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  1002. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  1003. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  1004. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  1005. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1006. package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
  1007. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
  1008. package/build/dist/Types/Team/ComplianceRule.js +48 -14
  1009. package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
  1010. package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
  1011. package/build/dist/Types/Telemetry/EntityType.js +16 -0
  1012. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  1013. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
  1014. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1015. package/build/dist/Types/XML.js +23 -0
  1016. package/build/dist/Types/XML.js.map +1 -1
  1017. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
  1018. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  1019. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
  1020. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
  1021. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
  1022. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
  1023. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
  1024. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
  1025. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
  1026. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
  1027. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  1028. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  1029. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
  1030. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
  1031. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
  1032. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  1033. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
  1034. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  1035. package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
  1036. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1037. package/build/dist/UI/Components/Icon/Icon.js +8 -0
  1038. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  1039. package/build/dist/UI/Components/Input/Input.js +11 -3
  1040. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  1041. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
  1042. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1043. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
  1044. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1045. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  1046. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  1047. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
  1048. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  1049. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
  1050. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
  1051. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
  1052. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
  1053. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
  1054. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
  1055. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
  1056. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  1057. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
  1058. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
  1059. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
  1060. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1061. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
  1062. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
  1063. package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
  1064. package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
  1065. package/build/dist/UI/Components/Table/Table.js +30 -11
  1066. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  1067. package/build/dist/UI/Components/Table/TableRow.js +9 -2
  1068. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  1069. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
  1070. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
  1071. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
  1072. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1073. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
  1074. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
  1075. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
  1076. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1077. package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
  1078. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  1079. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
  1080. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
  1081. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  1082. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1083. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  1084. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  1085. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  1086. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  1087. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  1088. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  1089. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  1090. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  1091. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  1092. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  1093. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  1094. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  1095. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  1096. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  1097. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  1098. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  1099. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  1100. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  1101. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  1102. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  1103. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  1104. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  1105. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  1106. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  1107. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  1108. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  1109. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  1110. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  1111. package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
  1112. package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
  1113. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
  1114. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
  1115. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
  1116. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
  1117. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  1118. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  1119. package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
  1120. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  1121. package/package.json +1 -3
  1122. package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
  1123. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
  1124. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
  1125. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
  1126. package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
  1127. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
  1128. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
  1129. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
  1130. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
  1131. package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
  1132. package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
  1133. package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
  1134. package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
  1135. package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
  1136. package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
  1137. package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
  1138. package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
  1139. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
  1140. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
  1141. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
  1142. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
  1143. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
  1144. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
  1145. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
  1146. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
@@ -0,0 +1,2497 @@
1
+ import ProxmoxCommandPolicy, {
2
+ MAX_PROXMOX_API_PATH_CHARS,
3
+ PROXMOX_CLUSTER_CRITICAL_SERVICES,
4
+ PROXMOX_CREATE_PATHS_SUMMARY,
5
+ PROXMOX_OUTPUT_FORMATS,
6
+ PROXMOX_READABLE_PATHS_SUMMARY,
7
+ PROXMOX_RESTARTABLE_SERVICES,
8
+ PVESH_PROGRAM,
9
+ ProxmoxApiRequest,
10
+ ProxmoxCommandParseResult,
11
+ parseProxmoxCommand,
12
+ } from "../../../../Utils/AiRemediation/Resource/ProxmoxCommandPolicy";
13
+ import ResourceCommandPolicy from "../../../../Utils/AiRemediation/Resource/ResourceCommandPolicy";
14
+ import {
15
+ ResourceAutoExecutionVerdict,
16
+ ResourceCommandPolicyResult,
17
+ ResourceTokenizeResult,
18
+ renderResourceDisplayCommand,
19
+ tokenizeResourceCommand,
20
+ } from "../../../../Utils/AiRemediation/Resource/ResourceCommandPolicyCore";
21
+ import {
22
+ RESOURCE_REDACTED_MARKER,
23
+ redactResourceCommandOutput,
24
+ redactResourceCommandOutputWithCount,
25
+ } from "../../../../Utils/AiRemediation/Resource/ResourceOutputRedactor";
26
+ import AiResourceType, {
27
+ AI_RESOURCE_TYPE_INFO,
28
+ } from "../../../../Types/ResourceAiAgent/AiResourceType";
29
+ import { ResourceCommandTier } from "../../../../Types/ResourceAiAgent/ResourceAiAccess";
30
+ import { AiRemediationCommandPolicyVerdict } from "../../../../Types/AutoRemediation/AiRemediationCommandPolicyVerdict";
31
+ import { describe, expect, test } from "@jest/globals";
32
+
33
+ /*
34
+ * Contract under test — the pvesh command policy (ProxmoxCommandPolicy)
35
+ * for Proxmox clusters, and the request parser the agent sends to the
36
+ * Proxmox VE API (parseProxmoxCommand).
37
+ *
38
+ * - Read (get, ls) only on an allowlist of API paths with validated
39
+ * segments and per-path options; /access, consoles, the QEMU monitor and
40
+ * guest-agent exec/file calls are Denied.
41
+ * - create: guest start/resume/reboot are SafeWrite; shutdown, stop,
42
+ * suspend, qemu reset and restarting the allowed node services are
43
+ * RiskyWrite; migrate and corosync / pve-cluster are RiskyWrite that
44
+ * always need a human; everything else (set, delete, usage, other paths,
45
+ * service stop, ...) is Denied.
46
+ * - The grammar leaves no second reading: every option takes a value, no
47
+ * single-dash, abbreviated, duplicated or "--" forms, no query strings,
48
+ * no encoded or relative paths — so a denied option or path can never
49
+ * hide in another word.
50
+ * - parseProxmoxCommand only ever builds a request for a command the
51
+ * policy allows, and the request is exactly the command that was tiered.
52
+ * - Targets are the VMID for a guest and "<node>/<service>" for a service.
53
+ * - The pvesh redaction hook masks cipassword, password, keyring and
54
+ * encryption-key in config, pending and storage output.
55
+ */
56
+
57
+ const UPID: string =
58
+ "UPID:pve1:0001A2B3:0C4D5E6F:65A1B2C3:qmstart:101:root@pam:";
59
+ const TOKEN_UPID: string =
60
+ "UPID:pve-2.lab:00ABCDEF:0012345AB:65A1B2C3:vzdump::svc@pve!oneuptime:";
61
+
62
+ // A VMID (or any other all-digit path segment).
63
+ const DIGITS_ONLY: RegExp = /^[0-9]+$/;
64
+
65
+ function evaluate(command: string): ResourceCommandPolicyResult {
66
+ return ResourceCommandPolicy.evaluateCommand({
67
+ resourceType: AiResourceType.ProxmoxCluster,
68
+ command,
69
+ });
70
+ }
71
+
72
+ function evaluateArgv(argv: Array<string>): ResourceCommandPolicyResult {
73
+ return ProxmoxCommandPolicy.evaluateArgv(argv);
74
+ }
75
+
76
+ function tokens(command: string): Array<string> {
77
+ const tokenized: ResourceTokenizeResult = tokenizeResourceCommand(command);
78
+
79
+ if (!tokenized.argv) {
80
+ throw new Error(`does not tokenize: ${command}`);
81
+ }
82
+
83
+ return tokenized.argv;
84
+ }
85
+
86
+ function parse(command: string): ProxmoxCommandParseResult {
87
+ return parseProxmoxCommand(tokens(command));
88
+ }
89
+
90
+ function request(command: string): ProxmoxApiRequest {
91
+ const parsed: ProxmoxCommandParseResult = parse(command);
92
+
93
+ if (!parsed.request) {
94
+ throw new Error(`no request for ${command}: ${parsed.errorMessage}`);
95
+ }
96
+
97
+ return parsed.request;
98
+ }
99
+
100
+ function expectDenied(command: string, mentions?: string | RegExp): void {
101
+ const result: ResourceCommandPolicyResult = evaluate(command);
102
+
103
+ expect(result.tier).toBe(ResourceCommandTier.Denied);
104
+ expect(result.targets).toEqual([]);
105
+ expect(result.requiresHuman).toBeUndefined();
106
+
107
+ if (mentions !== undefined) {
108
+ expect(result.reason).toMatch(mentions);
109
+ }
110
+ }
111
+
112
+ describe("module shape", () => {
113
+ test("is the pvesh policy for the pvesh program", () => {
114
+ expect(ProxmoxCommandPolicy.name).toBe("pvesh");
115
+ expect([...ProxmoxCommandPolicy.programs]).toEqual(["pvesh"]);
116
+ expect(PVESH_PROGRAM).toBe("pvesh");
117
+ expect([...PROXMOX_OUTPUT_FORMATS]).toEqual([
118
+ "json",
119
+ "json-pretty",
120
+ "text",
121
+ ]);
122
+ });
123
+
124
+ test("is what the dispatcher routes a Proxmox cluster to", () => {
125
+ expect(
126
+ ResourceCommandPolicy.getToolPolicy(AiResourceType.ProxmoxCluster),
127
+ ).toBe(ProxmoxCommandPolicy);
128
+ expect(
129
+ ResourceCommandPolicy.getReadCommandGuide(AiResourceType.ProxmoxCluster),
130
+ ).toBe(ProxmoxCommandPolicy.readCommandGuide);
131
+ expect(
132
+ ResourceCommandPolicy.getWriteCommandGuide(AiResourceType.ProxmoxCluster),
133
+ ).toBe(ProxmoxCommandPolicy.writeCommandGuide);
134
+ });
135
+
136
+ test("is no longer the fail-closed stub", () => {
137
+ expect(evaluate("pvesh get /version").reason).not.toContain(
138
+ "not implemented yet",
139
+ );
140
+ expect(ProxmoxCommandPolicy.readCommandGuide).not.toContain("Unavailable");
141
+ expect(ProxmoxCommandPolicy.writeCommandGuide).not.toContain("Unavailable");
142
+ });
143
+
144
+ test.each(
145
+ AI_RESOURCE_TYPE_INFO[AiResourceType.ProxmoxCluster].testCommands.map(
146
+ (command: string): [string] => {
147
+ return [command];
148
+ },
149
+ ),
150
+ )("the Test connection command %p is a Read", (command: string) => {
151
+ expect(evaluate(command).tier).toBe(ResourceCommandTier.Read);
152
+ expect(
153
+ ResourceCommandPolicy.isReadOnly({
154
+ resourceType: AiResourceType.ProxmoxCluster,
155
+ command,
156
+ }),
157
+ ).toBe(true);
158
+ });
159
+ });
160
+
161
+ /*
162
+ * One command per read route, with the canonical API path it reads. Every
163
+ * route in READ_ROUTES appears here at least once.
164
+ */
165
+ const READ_COMMANDS: Array<[string, string]> = [
166
+ ["pvesh get /version", "/version"],
167
+ ["pvesh get /cluster/status", "/cluster/status"],
168
+ ["pvesh get /cluster/resources", "/cluster/resources"],
169
+ ["pvesh get /cluster/ha/status/current", "/cluster/ha/status/current"],
170
+ ["pvesh get /cluster/ha/resources", "/cluster/ha/resources"],
171
+ ["pvesh get /cluster/log", "/cluster/log"],
172
+ ["pvesh get /cluster/tasks", "/cluster/tasks"],
173
+ ["pvesh get /cluster/replication", "/cluster/replication"],
174
+ ["pvesh get /cluster/backup", "/cluster/backup"],
175
+ ["pvesh get /nodes", "/nodes"],
176
+ ["pvesh get /nodes/pve1/status", "/nodes/pve1/status"],
177
+ ["pvesh get /nodes/pve1/version", "/nodes/pve1/version"],
178
+ ["pvesh get /nodes/pve1/tasks", "/nodes/pve1/tasks"],
179
+ [
180
+ `pvesh get /nodes/pve1/tasks/${UPID}/status`,
181
+ `/nodes/pve1/tasks/${UPID}/status`,
182
+ ],
183
+ [`pvesh get /nodes/pve1/tasks/${UPID}/log`, `/nodes/pve1/tasks/${UPID}/log`],
184
+ [
185
+ `pvesh get '/nodes/pve-2.lab/tasks/${TOKEN_UPID}/log'`,
186
+ `/nodes/pve-2.lab/tasks/${TOKEN_UPID}/log`,
187
+ ],
188
+ ["pvesh get /nodes/pve1/syslog", "/nodes/pve1/syslog"],
189
+ ["pvesh get /nodes/pve1/journal", "/nodes/pve1/journal"],
190
+ ["pvesh get /nodes/pve1/services", "/nodes/pve1/services"],
191
+ [
192
+ "pvesh get /nodes/pve1/services/pveproxy/state",
193
+ "/nodes/pve1/services/pveproxy/state",
194
+ ],
195
+ [
196
+ "pvesh get /nodes/pve1/services/pve-ha-lrm/state",
197
+ "/nodes/pve1/services/pve-ha-lrm/state",
198
+ ],
199
+ ["pvesh get /nodes/pve1/storage", "/nodes/pve1/storage"],
200
+ [
201
+ "pvesh get /nodes/pve1/storage/local-lvm/status",
202
+ "/nodes/pve1/storage/local-lvm/status",
203
+ ],
204
+ ["pvesh get /nodes/pve1/disks/list", "/nodes/pve1/disks/list"],
205
+ [
206
+ "pvesh get /nodes/pve1/disks/smart --disk /dev/sda",
207
+ "/nodes/pve1/disks/smart",
208
+ ],
209
+ ["pvesh get /nodes/pve1/network", "/nodes/pve1/network"],
210
+ ["pvesh get /nodes/pve1/netstat", "/nodes/pve1/netstat"],
211
+ ["pvesh get /nodes/pve1/rrddata --timeframe hour", "/nodes/pve1/rrddata"],
212
+ ["pvesh get /nodes/pve1/qemu", "/nodes/pve1/qemu"],
213
+ ["pvesh get /nodes/pve1/lxc", "/nodes/pve1/lxc"],
214
+ [
215
+ "pvesh get /nodes/pve1/qemu/101/status/current",
216
+ "/nodes/pve1/qemu/101/status/current",
217
+ ],
218
+ [
219
+ "pvesh get /nodes/pve1/lxc/200/status/current",
220
+ "/nodes/pve1/lxc/200/status/current",
221
+ ],
222
+ ["pvesh get /nodes/pve1/qemu/101/config", "/nodes/pve1/qemu/101/config"],
223
+ ["pvesh get /nodes/pve1/lxc/200/config", "/nodes/pve1/lxc/200/config"],
224
+ ["pvesh get /nodes/pve1/qemu/101/pending", "/nodes/pve1/qemu/101/pending"],
225
+ ["pvesh get /nodes/pve1/lxc/200/pending", "/nodes/pve1/lxc/200/pending"],
226
+ [
227
+ "pvesh get /nodes/pve1/qemu/101/rrddata --timeframe day",
228
+ "/nodes/pve1/qemu/101/rrddata",
229
+ ],
230
+ [
231
+ "pvesh get /nodes/pve1/lxc/200/rrddata --timeframe week --cf MAX",
232
+ "/nodes/pve1/lxc/200/rrddata",
233
+ ],
234
+ ["pvesh get /nodes/pve1/qemu/101/snapshot", "/nodes/pve1/qemu/101/snapshot"],
235
+ ["pvesh get /nodes/pve1/lxc/200/snapshot", "/nodes/pve1/lxc/200/snapshot"],
236
+ [
237
+ "pvesh get /nodes/pve1/qemu/101/agent/info",
238
+ "/nodes/pve1/qemu/101/agent/info",
239
+ ],
240
+ [
241
+ "pvesh get /nodes/pve1/qemu/101/agent/get-osinfo",
242
+ "/nodes/pve1/qemu/101/agent/get-osinfo",
243
+ ],
244
+ [
245
+ "pvesh get /nodes/pve1/qemu/101/agent/get-fsinfo",
246
+ "/nodes/pve1/qemu/101/agent/get-fsinfo",
247
+ ],
248
+ [
249
+ "pvesh get /nodes/pve1/qemu/101/agent/network-get-interfaces",
250
+ "/nodes/pve1/qemu/101/agent/network-get-interfaces",
251
+ ],
252
+ [
253
+ "pvesh get /nodes/pve1/qemu/101/agent/get-host-name",
254
+ "/nodes/pve1/qemu/101/agent/get-host-name",
255
+ ],
256
+ [
257
+ "pvesh get /nodes/pve1/qemu/101/agent/get-time",
258
+ "/nodes/pve1/qemu/101/agent/get-time",
259
+ ],
260
+ ["pvesh get /nodes/pve1/replication", "/nodes/pve1/replication"],
261
+ ["pvesh get /nodes/pve1/apt/update", "/nodes/pve1/apt/update"],
262
+ ["pvesh get /nodes/pve1/ceph/status", "/nodes/pve1/ceph/status"],
263
+ ["pvesh get /pools", "/pools"],
264
+ ["pvesh get /storage", "/storage"],
265
+ // ls reads the same paths.
266
+ ["pvesh ls /nodes", "/nodes"],
267
+ ["pvesh ls /nodes/pve1/qemu", "/nodes/pve1/qemu"],
268
+ // Node names: FQDN-style, digits first, and the API's localhost alias.
269
+ [
270
+ "pvesh get /nodes/pve1.example.com/status",
271
+ "/nodes/pve1.example.com/status",
272
+ ],
273
+ ["pvesh get /nodes/1node/status", "/nodes/1node/status"],
274
+ ["pvesh get /nodes/localhost/status", "/nodes/localhost/status"],
275
+ // VMIDs from two to nine digits.
276
+ [
277
+ "pvesh get /nodes/pve1/qemu/10/status/current",
278
+ "/nodes/pve1/qemu/10/status/current",
279
+ ],
280
+ [
281
+ "pvesh get /nodes/pve1/qemu/999999999/status/current",
282
+ "/nodes/pve1/qemu/999999999/status/current",
283
+ ],
284
+ // A trailing slash is dropped from the request path.
285
+ ["pvesh get /nodes/pve1/status/", "/nodes/pve1/status"],
286
+ ];
287
+
288
+ describe("Read", () => {
289
+ test.each(READ_COMMANDS)(
290
+ "%p is a Read of %p",
291
+ (command: string, path: string) => {
292
+ const result: ResourceCommandPolicyResult = evaluate(command);
293
+
294
+ expect(result.tier).toBe(ResourceCommandTier.Read);
295
+ expect(result.targets).toEqual([]);
296
+ expect(result.requiresHuman).toBeUndefined();
297
+ expect(result.program).toBe("pvesh");
298
+ expect(result.verb).toBe(tokens(command)[1]);
299
+ expect(result.reason).toContain(`reads ${path} from the Proxmox VE API`);
300
+ expect(result.reason).toContain("changes nothing");
301
+
302
+ const built: ProxmoxApiRequest = request(command);
303
+
304
+ expect(built.method).toBe("GET");
305
+ expect(built.path).toBe(path);
306
+ },
307
+ );
308
+
309
+ test.each([
310
+ ["pvesh get /nodes/pve1/qemu/101/config"],
311
+ ["pvesh get /nodes/pve1/lxc/200/pending"],
312
+ ["pvesh get /storage"],
313
+ ])("%p says its secrets are redacted", (command: string) => {
314
+ expect(evaluate(command).reason).toContain("redacted");
315
+ });
316
+
317
+ test("a plain read does not claim redaction", () => {
318
+ expect(evaluate("pvesh get /cluster/status").reason).not.toContain(
319
+ "redacted",
320
+ );
321
+ });
322
+ });
323
+
324
+ describe("Read options", () => {
325
+ test.each([
326
+ ["pvesh get /cluster/resources --type vm", { type: "vm" }],
327
+ ["pvesh get /cluster/resources --type=storage", { type: "storage" }],
328
+ ["pvesh get /cluster/resources --type node", { type: "node" }],
329
+ ["pvesh get /cluster/resources --type sdn", { type: "sdn" }],
330
+ ["pvesh get /cluster/ha/resources --type ct", { type: "ct" }],
331
+ ["pvesh get /cluster/log --max 50", { max: "50" }],
332
+ [
333
+ "pvesh get /nodes/pve1/tasks --errors 1 --limit 20",
334
+ { errors: "1", limit: "20" },
335
+ ],
336
+ [
337
+ "pvesh get /nodes/pve1/tasks --vmid 101 --source all --start 0",
338
+ { vmid: "101", source: "all", start: "0" },
339
+ ],
340
+ [
341
+ "pvesh get /nodes/pve1/tasks --typefilter qmstart --userfilter root@pam",
342
+ { typefilter: "qmstart", userfilter: "root@pam" },
343
+ ],
344
+ [
345
+ "pvesh get /nodes/pve1/tasks --since 1735689600 --until 1735776000",
346
+ { since: "1735689600", until: "1735776000" },
347
+ ],
348
+ [
349
+ `pvesh get /nodes/pve1/tasks/${UPID}/log --limit 100 --start 50`,
350
+ { limit: "100", start: "50" },
351
+ ],
352
+ [
353
+ "pvesh get /nodes/pve1/syslog --limit 200 --since '2025-01-31 14:05' --until 2025-02-01 --service pveproxy",
354
+ {
355
+ limit: "200",
356
+ since: "2025-01-31 14:05",
357
+ until: "2025-02-01",
358
+ service: "pveproxy",
359
+ },
360
+ ],
361
+ [
362
+ "pvesh get /nodes/pve1/syslog --since '2025-01-31 14:05:59'",
363
+ { since: "2025-01-31 14:05:59" },
364
+ ],
365
+ [
366
+ "pvesh get /nodes/pve1/journal --lastentries 100 --since 1735689600",
367
+ { lastentries: "100", since: "1735689600" },
368
+ ],
369
+ [
370
+ "pvesh get /nodes/pve1/storage --content images,rootdir --enabled 1",
371
+ { content: "images,rootdir", enabled: "1" },
372
+ ],
373
+ [
374
+ "pvesh get /nodes/pve1/disks/list --skipsmart 1 --include-partitions 0",
375
+ { skipsmart: "1", "include-partitions": "0" },
376
+ ],
377
+ [
378
+ "pvesh get /nodes/pve1/disks/smart --disk /dev/nvme0n1 --healthonly 1",
379
+ { disk: "/dev/nvme0n1", healthonly: "1" },
380
+ ],
381
+ [
382
+ "pvesh get /nodes/pve1/disks/smart --disk=/dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0XXXXXX",
383
+ { disk: "/dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0XXXXXX" },
384
+ ],
385
+ ["pvesh get /nodes/pve1/network --type bridge", { type: "bridge" }],
386
+ ["pvesh get /nodes/pve1/network --type OVSBridge", { type: "OVSBridge" }],
387
+ [
388
+ "pvesh get /nodes/pve1/rrddata --timeframe week --cf AVERAGE",
389
+ { timeframe: "week", cf: "AVERAGE" },
390
+ ],
391
+ ["pvesh get /nodes/pve1/qemu --full 1", { full: "1" }],
392
+ ["pvesh get /nodes/pve1/qemu/101/config --current 1", { current: "1" }],
393
+ ["pvesh get /nodes/pve1/replication --guest 101", { guest: "101" }],
394
+ // Options before the path, and mixed around it, read the same.
395
+ ["pvesh get --type vm /cluster/resources", { type: "vm" }],
396
+ [
397
+ "pvesh get --errors=1 /nodes/pve1/tasks --limit 5",
398
+ { errors: "1", limit: "5" },
399
+ ],
400
+ ])("%p", (command: string, params: Record<string, string>) => {
401
+ expect(evaluate(command).tier).toBe(ResourceCommandTier.Read);
402
+ expect(request(command).params).toEqual(params);
403
+ });
404
+
405
+ test.each([
406
+ ["pvesh get /version --output-format json", "json"],
407
+ ["pvesh get /version --output-format=json-pretty", "json-pretty"],
408
+ ["pvesh get --output-format text /version", "text"],
409
+ [
410
+ "pvesh create /nodes/pve1/qemu/101/status/start --output-format json",
411
+ "json",
412
+ ],
413
+ ])("%p prints as %p", (command: string, format: string) => {
414
+ expect(evaluate(command).tier).not.toBe(ResourceCommandTier.Denied);
415
+ expect(request(command).outputFormat).toBe(format);
416
+ expect(request(command).params).toEqual({});
417
+ });
418
+
419
+ test("no --output-format leaves the choice to the agent", () => {
420
+ expect(request("pvesh get /version").outputFormat).toBeNull();
421
+ });
422
+
423
+ test.each([
424
+ [
425
+ "pvesh get /version --output-format yaml",
426
+ "--output-format must be one of json, json-pretty, text",
427
+ ],
428
+ [
429
+ "pvesh get /version --output-format JSON",
430
+ "--output-format must be one of",
431
+ ],
432
+ ["pvesh get /version --output-format=", "--output-format must be one of"],
433
+ [
434
+ "pvesh get /cluster/resources --type vms",
435
+ "must be one of vm, storage, node, sdn",
436
+ ],
437
+ [
438
+ "pvesh get /cluster/resources --type VM",
439
+ "must be one of vm, storage, node, sdn",
440
+ ],
441
+ ["pvesh get /cluster/ha/resources --type vm1", "one of vm, ct"],
442
+ ["pvesh get /cluster/log --max 0", "a whole number from 1 to 5000"],
443
+ ["pvesh get /cluster/log --max 5001", "a whole number from 1 to 5000"],
444
+ ["pvesh get /cluster/log --max 050", "a whole number"],
445
+ ["pvesh get /cluster/log --max -1", "--max needs a value"],
446
+ ["pvesh get /cluster/log --max=-1", "a whole number"],
447
+ ["pvesh get /cluster/log --max 1e3", "a whole number"],
448
+ ["pvesh get /cluster/log --max ' 10'", "a whole number"],
449
+ [
450
+ "pvesh get /nodes/pve1/tasks --errors true",
451
+ "--errors on pvesh get /nodes/{node}/tasks must be 0 or 1",
452
+ ],
453
+ ["pvesh get /nodes/pve1/tasks --errors yes", "must be 0 or 1"],
454
+ ["pvesh get /nodes/pve1/tasks --vmid 0101", "a guest id (VMID)"],
455
+ [
456
+ "pvesh get /nodes/pve1/tasks --source running",
457
+ "one of archive, active, all",
458
+ ],
459
+ ["pvesh get /nodes/pve1/tasks --typefilter 'qm start'", "a task type"],
460
+ [
461
+ "pvesh get /nodes/pve1/tasks --userfilter 'root@pam x'",
462
+ "a user such as root@pam",
463
+ ],
464
+ [
465
+ "pvesh get /nodes/pve1/syslog --since yesterday",
466
+ "a time such as 2025-01-31",
467
+ ],
468
+ [
469
+ "pvesh get /nodes/pve1/syslog --since 1735689600",
470
+ "a time such as 2025-01-31",
471
+ ],
472
+ ["pvesh get /nodes/pve1/journal --since '2025-01-31'", "a whole number"],
473
+ [
474
+ "pvesh get /nodes/pve1/storage --content images,secrets",
475
+ "a comma-separated list of",
476
+ ],
477
+ ["pvesh get /nodes/pve1/storage --content ''", "a comma-separated list of"],
478
+ [
479
+ "pvesh get /nodes/pve1/disks/smart --disk sda",
480
+ "a block device such as /dev/sda",
481
+ ],
482
+ [
483
+ "pvesh get /nodes/pve1/disks/smart --disk /dev/../etc/shadow",
484
+ "a block device",
485
+ ],
486
+ [
487
+ "pvesh get /nodes/pve1/disks/smart --disk /etc/pve/priv/authkey.key",
488
+ "a block device",
489
+ ],
490
+ [
491
+ "pvesh get /nodes/pve1/rrddata --timeframe month",
492
+ "one of hour, day, week",
493
+ ],
494
+ [
495
+ "pvesh get /nodes/pve1/rrddata --timeframe hour --cf MIN",
496
+ "one of AVERAGE, MAX",
497
+ ],
498
+ [
499
+ "pvesh get /nodes/pve1/network --type wireguard",
500
+ "--type on pvesh get /nodes/{node}/network",
501
+ ],
502
+ ])("%p is Denied: a bad value", (command: string, mentions: string) => {
503
+ expectDenied(command, mentions);
504
+ });
505
+
506
+ test.each([
507
+ [
508
+ "pvesh get /nodes/pve1/rrddata",
509
+ "needs --timeframe (one of hour, day, week)",
510
+ ],
511
+ ["pvesh get /nodes/pve1/qemu/101/rrddata --cf MAX", "needs --timeframe"],
512
+ [
513
+ "pvesh get /nodes/pve1/disks/smart",
514
+ "needs --disk (a block device such as /dev/sda)",
515
+ ],
516
+ ])(
517
+ "%p is Denied: a required option is missing",
518
+ (command: string, mentions: string) => {
519
+ expectDenied(command, mentions);
520
+ },
521
+ );
522
+
523
+ test.each([
524
+ [
525
+ "pvesh get /version --type vm",
526
+ "it takes no options besides --output-format",
527
+ ],
528
+ [
529
+ "pvesh get /cluster/status --full 1",
530
+ "it takes no options besides --output-format",
531
+ ],
532
+ [
533
+ "pvesh get /nodes/pve1/lxc --full 1",
534
+ "is not an option OneUptime AI may pass to pvesh get /nodes/{node}/lxc",
535
+ ],
536
+ [
537
+ "pvesh get /nodes/pve1/tasks --statusfilter ok",
538
+ "its options are --errors, --limit, --vmid",
539
+ ],
540
+ ["pvesh get /nodes/pve1/tasks --Limit 5", "--Limit is not an option"],
541
+ ["pvesh get /nodes/pve1/tasks --lim 5", "--lim is not an option"],
542
+ [
543
+ "pvesh get /nodes/pve1/qemu/101/config --snapshot before-upgrade",
544
+ "--snapshot is not an option",
545
+ ],
546
+ [
547
+ "pvesh get /nodes/pve1/journal --startcursor x",
548
+ "--startcursor is not an option",
549
+ ],
550
+ [
551
+ "pvesh get /nodes/pve1/tasks/" + UPID + "/log --download 1",
552
+ "--download is not an option",
553
+ ],
554
+ [
555
+ "pvesh get /version --human-readable 1",
556
+ "--human-readable is not an option",
557
+ ],
558
+ ["pvesh get /version --noborder 1", "--noborder is not an option"],
559
+ ["pvesh get /version --noproxy 1", "--noproxy is not an option"],
560
+ ["pvesh get /version --constructor 1", "--constructor is not an option"],
561
+ ["pvesh get /version --toString 1", "--toString is not an option"],
562
+ ])(
563
+ "%p is Denied: an option the path does not take",
564
+ (command: string, mentions: string) => {
565
+ expectDenied(command, mentions);
566
+ },
567
+ );
568
+ });
569
+
570
+ describe("Read refusals", () => {
571
+ test.each([
572
+ ["pvesh get /access"],
573
+ ["pvesh get /access/users"],
574
+ ["pvesh get /access/users/root@pam/token"],
575
+ ["pvesh get /access/acl"],
576
+ ["pvesh get /access/tfa"],
577
+ ["pvesh get /access/ticket"],
578
+ ["pvesh ls /access/roles"],
579
+ ["pvesh create /access/ticket --username root@pam"],
580
+ ["pvesh create /access/users/root@pam/token/ai"],
581
+ ["pvesh get /access/users --output-format yaml"],
582
+ ["pvesh get /access/users -x"],
583
+ ])("%p is Denied: /access", (command: string) => {
584
+ expectDenied(command, "everything under /access");
585
+ });
586
+
587
+ test.each([
588
+ ["pvesh create /nodes/pve1/qemu/101/vncproxy"],
589
+ ["pvesh create /nodes/pve1/lxc/200/termproxy"],
590
+ ["pvesh create /nodes/pve1/qemu/101/spiceproxy"],
591
+ ["pvesh get /nodes/pve1/qemu/101/vncwebsocket --port 5900"],
592
+ ["pvesh create /nodes/pve1/qemu/101/mtunnel"],
593
+ ["pvesh create /nodes/pve1/termproxy"],
594
+ ["pvesh create /nodes/pve1/vncshell"],
595
+ ["pvesh create /nodes/pve1/spiceshell"],
596
+ ["pvesh get /nodes/pve1/vncwebsocket"],
597
+ ])("%p is Denied: a console", (command: string) => {
598
+ expectDenied(command, "the Proxmox equivalent of exec");
599
+ });
600
+
601
+ test.each([
602
+ ["pvesh create /nodes/pve1/qemu/101/agent/exec --command whoami"],
603
+ ["pvesh get /nodes/pve1/qemu/101/agent/exec-status --pid 1"],
604
+ ["pvesh get /nodes/pve1/qemu/101/agent/file-read --file /etc/shadow"],
605
+ ["pvesh create /nodes/pve1/qemu/101/agent/file-write"],
606
+ ["pvesh create /nodes/pve1/qemu/101/agent/set-user-password"],
607
+ ["pvesh create /nodes/pve1/qemu/101/monitor --command 'info status'"],
608
+ ])("%p is Denied: exec in a guest", (command: string) => {
609
+ expectDenied(
610
+ command,
611
+ "run commands in, read files from or change passwords",
612
+ );
613
+ });
614
+
615
+ test("the node's batch execute is Denied", () => {
616
+ expectDenied(
617
+ "pvesh create /nodes/pve1/execute",
618
+ "a batch of arbitrary API calls",
619
+ );
620
+ });
621
+
622
+ test.each([
623
+ ["pvesh get /", '"/" is the root of the API'],
624
+ ["pvesh get /cluster", "/cluster is not a path OneUptime AI may read"],
625
+ ["pvesh get /cluster/config", "is not a path OneUptime AI may read"],
626
+ ["pvesh get /cluster/config/nodes", "is not a path OneUptime AI may read"],
627
+ ["pvesh get /cluster/acme/plugins", "is not a path OneUptime AI may read"],
628
+ ["pvesh get /cluster/notifications/endpoints/smtp", "is not a path"],
629
+ ["pvesh get /cluster/firewall/rules", "is not a path"],
630
+ ["pvesh get /cluster/sdn/vnets", "is not a path"],
631
+ ["pvesh get /cluster/mapping/pci", "is not a path"],
632
+ ["pvesh get /nodes/pve1", "is not a path"],
633
+ ["pvesh get /nodes/pve1/qemu/101", "is not a path"],
634
+ [
635
+ "pvesh get /nodes/pve1/qemu/101/cloudinit/dump --type user",
636
+ "is not a path",
637
+ ],
638
+ ["pvesh get /nodes/pve1/storage/local/content", "is not a path"],
639
+ ["pvesh get /nodes/pve1/lxc/200/agent/info", "is not a path"],
640
+ ["pvesh get /nodes/pve1/qemu/101/snapshot/before/config", "is not a path"],
641
+ ["pvesh get /nodes/pve1/firewall/rules", "is not a path"],
642
+ ["pvesh get /nodes/pve1/certificates/info", "is not a path"],
643
+ ["pvesh get /nodes/pve1/hosts", "is not a path"],
644
+ ["pvesh get /Nodes", "is not a path"],
645
+ ["pvesh get /VERSION", "is not a path"],
646
+ ])(
647
+ "%p is Denied: not an allowed read path",
648
+ (command: string, mentions: string) => {
649
+ expectDenied(command, mentions);
650
+ expect(evaluate(command).reason).toContain("Readable paths:");
651
+ },
652
+ );
653
+
654
+ test.each([
655
+ [
656
+ "pvesh get /nodes/pve_1/status",
657
+ '"pve_1" in /nodes/{node}/status must be a node name',
658
+ ],
659
+ ["pvesh get /nodes/-pve/status", "must be a node name"],
660
+ [`pvesh get /nodes/${"n".repeat(64)}/status`, "must be a node name"],
661
+ ["pvesh get /nodes/pve1/qemu/0101/status/current", "without leading zeros"],
662
+ ["pvesh get /nodes/pve1/qemu/7/status/current", "a guest id (VMID)"],
663
+ [
664
+ "pvesh get /nodes/pve1/qemu/1234567890/status/current",
665
+ "a guest id (VMID)",
666
+ ],
667
+ ["pvesh get /nodes/pve1/qemu/abc/config", "a guest id (VMID)"],
668
+ ["pvesh get /nodes/pve1/vm/101/config", "qemu (a VM) or lxc (a container)"],
669
+ [
670
+ "pvesh get /nodes/pve1/QEMU/101/config",
671
+ "qemu (a VM) or lxc (a container)",
672
+ ],
673
+ ["pvesh get /nodes/pve1/tasks/UPID:pve1:bad/status", "a task id (UPID)"],
674
+ ["pvesh get /nodes/pve1/tasks/12345/log", "a task id (UPID)"],
675
+ ["pvesh get /nodes/pve1/storage/-local/status", "a storage id"],
676
+ ["pvesh get /nodes/pve1/services/PVEPROXY/state", "a service name"],
677
+ ["pvesh get /nodes/pve1/qemu/101/agent/ping", "one of info, get-osinfo"],
678
+ [
679
+ "pvesh get /nodes/pve1/qemu/101/agent/get-users",
680
+ "one of info, get-osinfo",
681
+ ],
682
+ ])(
683
+ "%p is Denied: a bad path segment",
684
+ (command: string, mentions: string) => {
685
+ expectDenied(command, mentions);
686
+ },
687
+ );
688
+
689
+ test.each([
690
+ ["pvesh get /nodes/pve1/qemu/101/status/start"],
691
+ ["pvesh get /nodes/pve1/lxc/200/status/stop"],
692
+ ["pvesh ls /nodes/pve1/qemu/101/migrate"],
693
+ ["pvesh get /nodes/pve1/services/pveproxy/restart"],
694
+ ])("%p is Denied: an action, not a read", (command: string) => {
695
+ expectDenied(
696
+ command,
697
+ "is an action for pvesh create, not something to read",
698
+ );
699
+ });
700
+ });
701
+
702
+ describe("SafeWrite", () => {
703
+ test.each([
704
+ [
705
+ "pvesh create /nodes/pve1/qemu/101/status/start",
706
+ "qemu start",
707
+ "101",
708
+ "starts VM 101 on node pve1",
709
+ ],
710
+ [
711
+ "pvesh create /nodes/pve1/qemu/101/status/start --timeout 60",
712
+ "qemu start",
713
+ "101",
714
+ "starts VM 101",
715
+ ],
716
+ [
717
+ "pvesh create /nodes/pve1/lxc/200/status/start",
718
+ "lxc start",
719
+ "200",
720
+ "starts container 200 on node pve1",
721
+ ],
722
+ [
723
+ "pvesh create /nodes/pve1/qemu/101/status/resume",
724
+ "qemu resume",
725
+ "101",
726
+ "resumes the paused VM 101",
727
+ ],
728
+ [
729
+ "pvesh create /nodes/pve1/lxc/200/status/resume",
730
+ "lxc resume",
731
+ "200",
732
+ "resumes the paused container 200",
733
+ ],
734
+ [
735
+ "pvesh create /nodes/pve1/qemu/101/status/reboot",
736
+ "qemu reboot",
737
+ "101",
738
+ "cleanly reboots VM 101",
739
+ ],
740
+ [
741
+ "pvesh create /nodes/pve1/qemu/101/status/reboot --timeout=120",
742
+ "qemu reboot",
743
+ "101",
744
+ "reboots",
745
+ ],
746
+ [
747
+ "pvesh create /nodes/pve2/lxc/200/status/reboot --timeout 30",
748
+ "lxc reboot",
749
+ "200",
750
+ "reboots container 200 on node pve2",
751
+ ],
752
+ [
753
+ "pvesh create /nodes/pve1/qemu/999999999/status/start",
754
+ "qemu start",
755
+ "999999999",
756
+ "starts",
757
+ ],
758
+ [
759
+ "pvesh create --timeout 60 /nodes/pve1/qemu/101/status/start",
760
+ "qemu start",
761
+ "101",
762
+ "starts",
763
+ ],
764
+ ])(
765
+ "%p is SafeWrite",
766
+ (command: string, verb: string, target: string, reason: string) => {
767
+ const result: ResourceCommandPolicyResult = evaluate(command);
768
+
769
+ expect(result.tier).toBe(ResourceCommandTier.SafeWrite);
770
+ expect(result.verb).toBe(verb);
771
+ expect(result.targets).toEqual([target]);
772
+ expect(result.requiresHuman).toBeUndefined();
773
+ expect(result.reason).toContain(reason);
774
+ expect(request(command).method).toBe("POST");
775
+ },
776
+ );
777
+
778
+ test("the timeout is sent as an API parameter", () => {
779
+ expect(
780
+ request("pvesh create /nodes/pve1/qemu/101/status/start --timeout 60"),
781
+ ).toEqual({
782
+ method: "POST",
783
+ path: "/nodes/pve1/qemu/101/status/start",
784
+ params: { timeout: "60" },
785
+ outputFormat: null,
786
+ });
787
+ });
788
+
789
+ test.each([
790
+ [
791
+ "pvesh create /nodes/pve1/lxc/200/status/start --timeout 60",
792
+ "it takes no options besides --output-format",
793
+ ],
794
+ [
795
+ "pvesh create /nodes/pve1/qemu/101/status/resume --timeout 5",
796
+ "it takes no options",
797
+ ],
798
+ [
799
+ "pvesh create /nodes/pve1/qemu/101/status/start --skiplock 1",
800
+ "--skiplock is not an option",
801
+ ],
802
+ [
803
+ "pvesh create /nodes/pve1/qemu/101/status/start --force-cpu host",
804
+ "--force-cpu is not an option",
805
+ ],
806
+ [
807
+ "pvesh create /nodes/pve1/qemu/101/status/start --stateuri tcp",
808
+ "--stateuri is not an option",
809
+ ],
810
+ [
811
+ "pvesh create /nodes/pve1/qemu/101/status/start --targetstorage local",
812
+ "--targetstorage is not an option",
813
+ ],
814
+ [
815
+ "pvesh create /nodes/pve1/qemu/101/status/resume --nocheck 1",
816
+ "--nocheck is not an option",
817
+ ],
818
+ [
819
+ "pvesh create /nodes/pve1/lxc/200/status/start --debug 1",
820
+ "--debug is not an option",
821
+ ],
822
+ [
823
+ "pvesh create /nodes/pve1/qemu/101/status/start --timeout 3601",
824
+ "a whole number from 0 to 3600",
825
+ ],
826
+ [
827
+ "pvesh create /nodes/pve1/qemu/101/status/start --timeout abc",
828
+ "a whole number from 0 to 3600",
829
+ ],
830
+ ])(
831
+ "%p is Denied: an option a safe write does not take",
832
+ (command: string, mentions: string) => {
833
+ expectDenied(command, mentions);
834
+ },
835
+ );
836
+ });
837
+
838
+ describe("RiskyWrite", () => {
839
+ test.each([
840
+ [
841
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown",
842
+ "qemu shutdown",
843
+ "101",
844
+ "shuts down VM 101",
845
+ ],
846
+ [
847
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout 180 --forceStop 0",
848
+ "qemu shutdown",
849
+ "101",
850
+ "stays down",
851
+ ],
852
+ [
853
+ "pvesh create /nodes/pve1/lxc/200/status/shutdown --forceStop=0",
854
+ "lxc shutdown",
855
+ "200",
856
+ "shuts down container 200",
857
+ ],
858
+ [
859
+ "pvesh create /nodes/pve1/qemu/101/status/stop",
860
+ "qemu stop",
861
+ "101",
862
+ "hard-stops VM 101",
863
+ ],
864
+ [
865
+ "pvesh create /nodes/pve1/lxc/200/status/stop",
866
+ "lxc stop",
867
+ "200",
868
+ "pulling the plug",
869
+ ],
870
+ [
871
+ "pvesh create /nodes/pve1/qemu/101/status/suspend",
872
+ "qemu suspend",
873
+ "101",
874
+ "pauses VM 101",
875
+ ],
876
+ [
877
+ "pvesh create /nodes/pve1/lxc/200/status/suspend",
878
+ "lxc suspend",
879
+ "200",
880
+ "until someone resumes it",
881
+ ],
882
+ [
883
+ "pvesh create /nodes/pve1/qemu/101/status/reset",
884
+ "qemu reset",
885
+ "101",
886
+ "hard-resets VM 101",
887
+ ],
888
+ ])(
889
+ "%p is RiskyWrite",
890
+ (command: string, verb: string, target: string, reason: string) => {
891
+ const result: ResourceCommandPolicyResult = evaluate(command);
892
+
893
+ expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
894
+ expect(result.verb).toBe(verb);
895
+ expect(result.targets).toEqual([target]);
896
+ expect(result.requiresHuman).toBeUndefined();
897
+ expect(result.reason).toContain(reason);
898
+ },
899
+ );
900
+
901
+ test.each(
902
+ PROXMOX_RESTARTABLE_SERVICES.flatMap(
903
+ (service: string): Array<[string, string]> => {
904
+ return ["start", "restart", "reload"].map(
905
+ (action: string): [string, string] => {
906
+ return [service, action];
907
+ },
908
+ );
909
+ },
910
+ ),
911
+ )(
912
+ "the %s service may be %sed (RiskyWrite, no human required)",
913
+ (service: string, action: string) => {
914
+ const command: string = `pvesh create /nodes/pve1/services/${service}/${action}`;
915
+ const result: ResourceCommandPolicyResult = evaluate(command);
916
+
917
+ expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
918
+ expect(result.verb).toBe(`service ${action}`);
919
+ expect(result.targets).toEqual([`pve1/${service}`]);
920
+ expect(result.requiresHuman).toBeUndefined();
921
+ expect(request(command)).toEqual({
922
+ method: "POST",
923
+ path: `/nodes/pve1/services/${service}/${action}`,
924
+ params: {},
925
+ outputFormat: null,
926
+ });
927
+ },
928
+ );
929
+
930
+ test("the service list is the one the spec names", () => {
931
+ expect([...PROXMOX_RESTARTABLE_SERVICES].sort()).toEqual(
932
+ [
933
+ "pveproxy",
934
+ "pvedaemon",
935
+ "pvestatd",
936
+ "pve-ha-lrm",
937
+ "pve-ha-crm",
938
+ "spiceproxy",
939
+ "pvescheduler",
940
+ "pve-firewall",
941
+ "chrony",
942
+ "cron",
943
+ "postfix",
944
+ ].sort(),
945
+ );
946
+ expect([...PROXMOX_CLUSTER_CRITICAL_SERVICES]).toEqual([
947
+ "corosync",
948
+ "pve-cluster",
949
+ ]);
950
+ });
951
+
952
+ test("a restart says what pauses, a reload that it reloads", () => {
953
+ expect(
954
+ evaluate("pvesh create /nodes/pve1/services/pveproxy/restart").reason,
955
+ ).toContain("restarts the pveproxy service on node pve1");
956
+ expect(
957
+ evaluate("pvesh create /nodes/pve1/services/pveproxy/reload").reason,
958
+ ).toContain("while it reloads");
959
+ });
960
+
961
+ test.each([
962
+ [
963
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --forceStop 1",
964
+ "--forceStop on pvesh create /nodes/{node}/qemu/{vmid}/status/shutdown must be exactly 0",
965
+ ],
966
+ [
967
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --keepActive 1",
968
+ "--keepActive is not an option",
969
+ ],
970
+ [
971
+ "pvesh create /nodes/pve1/qemu/101/status/stop --timeout 10",
972
+ "it takes no options besides --output-format",
973
+ ],
974
+ [
975
+ "pvesh create /nodes/pve1/qemu/101/status/stop --overrule-shutdown 1",
976
+ "--overrule-shutdown is not an option",
977
+ ],
978
+ [
979
+ "pvesh create /nodes/pve1/qemu/101/status/suspend --todisk 1",
980
+ "--todisk is not an option",
981
+ ],
982
+ [
983
+ "pvesh create /nodes/pve1/qemu/101/status/reset --skiplock 1",
984
+ "--skiplock is not an option",
985
+ ],
986
+ [
987
+ "pvesh create /nodes/pve1/services/pveproxy/restart --timeout 5",
988
+ "it takes no options besides --output-format",
989
+ ],
990
+ ])(
991
+ "%p is Denied: an option a risky write does not take",
992
+ (command: string, mentions: string) => {
993
+ expectDenied(command, mentions);
994
+ },
995
+ );
996
+ });
997
+
998
+ describe("writes that always need a human", () => {
999
+ test.each([
1000
+ [
1001
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2",
1002
+ "qemu migrate",
1003
+ "101",
1004
+ { target: "pve2" },
1005
+ ],
1006
+ [
1007
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --online 1",
1008
+ "qemu migrate",
1009
+ "101",
1010
+ { target: "pve2", online: "1" },
1011
+ ],
1012
+ [
1013
+ "pvesh create /nodes/pve1/qemu/101/migrate --online=0 --target=pve3.lab",
1014
+ "qemu migrate",
1015
+ "101",
1016
+ { online: "0", target: "pve3.lab" },
1017
+ ],
1018
+ [
1019
+ "pvesh create /nodes/pve1/lxc/200/migrate --target pve2 --restart 1",
1020
+ "lxc migrate",
1021
+ "200",
1022
+ { target: "pve2", restart: "1" },
1023
+ ],
1024
+ [
1025
+ "pvesh create /nodes/pve1/lxc/200/migrate --target pve2 --online 0 --restart 0",
1026
+ "lxc migrate",
1027
+ "200",
1028
+ { target: "pve2", online: "0", restart: "0" },
1029
+ ],
1030
+ ])(
1031
+ "%p is RiskyWrite + requiresHuman",
1032
+ (
1033
+ command: string,
1034
+ verb: string,
1035
+ target: string,
1036
+ params: Record<string, string>,
1037
+ ) => {
1038
+ const result: ResourceCommandPolicyResult = evaluate(command);
1039
+
1040
+ expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
1041
+ expect(result.requiresHuman).toBe(true);
1042
+ expect(result.verb).toBe(verb);
1043
+ expect(result.targets).toEqual([target]);
1044
+ expect(result.reason).toContain("a human always decides");
1045
+ expect(request(command).params).toEqual(params);
1046
+ },
1047
+ );
1048
+
1049
+ test("the migrate reason names both nodes", () => {
1050
+ expect(
1051
+ evaluate("pvesh create /nodes/pve1/qemu/101/migrate --target pve2")
1052
+ .reason,
1053
+ ).toContain("moves VM 101 from node pve1 to node pve2");
1054
+ });
1055
+
1056
+ test.each(
1057
+ PROXMOX_CLUSTER_CRITICAL_SERVICES.flatMap(
1058
+ (service: string): Array<[string, string]> => {
1059
+ return ["start", "restart", "reload"].map(
1060
+ (action: string): [string, string] => {
1061
+ return [service, action];
1062
+ },
1063
+ );
1064
+ },
1065
+ ),
1066
+ )(
1067
+ "%s %s is RiskyWrite + requiresHuman",
1068
+ (service: string, action: string) => {
1069
+ const result: ResourceCommandPolicyResult = evaluate(
1070
+ `pvesh create /nodes/pve1/services/${service}/${action}`,
1071
+ );
1072
+
1073
+ expect(result.tier).toBe(ResourceCommandTier.RiskyWrite);
1074
+ expect(result.requiresHuman).toBe(true);
1075
+ expect(result.targets).toEqual([`pve1/${service}`]);
1076
+ expect(result.reason).toContain("a human always decides");
1077
+ },
1078
+ );
1079
+
1080
+ test.each([
1081
+ [
1082
+ "pvesh create /nodes/pve1/qemu/101/migrate",
1083
+ "needs --target (a node name such as pve2)",
1084
+ ],
1085
+ [
1086
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve1",
1087
+ "is already on node pve1",
1088
+ ],
1089
+ [
1090
+ "pvesh create /nodes/pve1/qemu/101/migrate --target PVE1",
1091
+ "is already on node pve1",
1092
+ ],
1093
+ [
1094
+ "pvesh create /nodes/pve1/qemu/101/migrate --target localhost",
1095
+ "--target must name the node to move to",
1096
+ ],
1097
+ [
1098
+ "pvesh create /nodes/pve1/qemu/101/migrate --target 'pve 2'",
1099
+ "a node name such as pve2",
1100
+ ],
1101
+ [
1102
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --restart 1",
1103
+ "--restart is not an option",
1104
+ ],
1105
+ [
1106
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --with-local-disks 1",
1107
+ "--with-local-disks is not an option",
1108
+ ],
1109
+ [
1110
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --force 1",
1111
+ "--force is not an option",
1112
+ ],
1113
+ [
1114
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --targetstorage local",
1115
+ "--targetstorage is not an option",
1116
+ ],
1117
+ [
1118
+ "pvesh create /nodes/pve1/lxc/200/migrate --target pve2 --target-storage local",
1119
+ "--target-storage is not an option",
1120
+ ],
1121
+ [
1122
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --online yes",
1123
+ "must be 0 or 1",
1124
+ ],
1125
+ [
1126
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --target pve3",
1127
+ "--target is given more than once",
1128
+ ],
1129
+ ])("%p is Denied", (command: string, mentions: string) => {
1130
+ expectDenied(command, mentions);
1131
+ });
1132
+ });
1133
+
1134
+ describe("Denied writes", () => {
1135
+ test.each([
1136
+ [
1137
+ "pvesh set /nodes/pve1/qemu/101/config --memory 8192",
1138
+ "pvesh set changes configuration",
1139
+ ],
1140
+ [
1141
+ "pvesh set /cluster/options --keyboard en-us",
1142
+ "pvesh set changes configuration",
1143
+ ],
1144
+ ["pvesh delete /nodes/pve1/qemu/101", "pvesh delete removes objects"],
1145
+ [
1146
+ "pvesh delete /nodes/pve1/qemu/101/snapshot/before-upgrade",
1147
+ "pvesh delete removes objects",
1148
+ ],
1149
+ ["pvesh usage /nodes", "pvesh usage is not available"],
1150
+ ["pvesh help", "pvesh help is not available"],
1151
+ [
1152
+ "pvesh post /nodes/pve1/qemu/101/status/start",
1153
+ '"post" is not a pvesh command',
1154
+ ],
1155
+ ["pvesh put /nodes/pve1/qemu/101/config", '"put" is not a pvesh command'],
1156
+ ["pvesh GET /version", '"GET" is not a pvesh command'],
1157
+ [
1158
+ "pvesh Create /nodes/pve1/qemu/101/status/start",
1159
+ '"Create" is not a pvesh command',
1160
+ ],
1161
+ ["pvesh constructor /version", '"constructor" is not a pvesh command'],
1162
+ ["pvesh __proto__ /version", '"__proto__" is not a pvesh command'],
1163
+ ["pvesh", "name what pvesh should do"],
1164
+ ])("%p is Denied: the command word", (command: string, mentions: string) => {
1165
+ expectDenied(command, mentions);
1166
+ });
1167
+
1168
+ test("set and delete say what pvesh create may do instead", () => {
1169
+ expect(evaluate("pvesh set /nodes/pve1/qemu/101/config").reason).toContain(
1170
+ PROXMOX_CREATE_PATHS_SUMMARY,
1171
+ );
1172
+ expect(evaluate("pvesh delete /pools/prod").reason).toContain(
1173
+ PROXMOX_CREATE_PATHS_SUMMARY,
1174
+ );
1175
+ expect(evaluate("pvesh usage /nodes").reason).toContain(
1176
+ PROXMOX_READABLE_PATHS_SUMMARY,
1177
+ );
1178
+ });
1179
+
1180
+ test.each([
1181
+ [
1182
+ "pvesh create /nodes/pve1/services/pveproxy/stop",
1183
+ "stopping a node service is never allowed",
1184
+ ],
1185
+ [
1186
+ "pvesh create /nodes/pve1/services/corosync/stop",
1187
+ "stopping a node service is never allowed",
1188
+ ],
1189
+ [
1190
+ "pvesh create /nodes/pve1/services/sshd/restart",
1191
+ '"sshd" is not a node service OneUptime AI may restart',
1192
+ ],
1193
+ [
1194
+ "pvesh create /nodes/pve1/services/systemd-journald/restart",
1195
+ "is not a node service",
1196
+ ],
1197
+ [
1198
+ "pvesh create /nodes/pve1/services/pvefw-logger/restart",
1199
+ "is not a node service",
1200
+ ],
1201
+ [
1202
+ "pvesh create /nodes/pve1/services/ksmtuned/start",
1203
+ "is not a node service",
1204
+ ],
1205
+ [
1206
+ "pvesh create /nodes/pve1/services/PVEPROXY/restart",
1207
+ "is not a node service",
1208
+ ],
1209
+ [
1210
+ "pvesh create /nodes/pve1/services/pveproxy/kill",
1211
+ '"kill" is not a service action',
1212
+ ],
1213
+ [
1214
+ "pvesh create /nodes/pve1/services/pveproxy/state",
1215
+ '"state" is not a service action',
1216
+ ],
1217
+ ])("%p is Denied: node services", (command: string, mentions: string) => {
1218
+ expectDenied(command, mentions);
1219
+ });
1220
+
1221
+ test.each([
1222
+ [
1223
+ "pvesh create /nodes/pve1/lxc/200/status/reset",
1224
+ "containers have no reset, reboot one instead",
1225
+ ],
1226
+ [
1227
+ "pvesh create /nodes/pve1/qemu/101/status/hibernate",
1228
+ '"hibernate" is not a VM status action',
1229
+ ],
1230
+ [
1231
+ "pvesh create /nodes/pve1/qemu/101/status/current",
1232
+ '"current" is not a VM status action',
1233
+ ],
1234
+ [
1235
+ "pvesh create /nodes/pve1/qemu/101/status/Start",
1236
+ '"Start" is not a VM status action',
1237
+ ],
1238
+ [
1239
+ "pvesh create /nodes/pve1/qemu/101/status/constructor",
1240
+ '"constructor" is not a VM status action',
1241
+ ],
1242
+ [
1243
+ "pvesh create /nodes/pve1/qemu/0101/status/start",
1244
+ "without leading zeros",
1245
+ ],
1246
+ ["pvesh create /nodes/pve1/qemu/abc/status/start", "a guest id (VMID)"],
1247
+ [
1248
+ "pvesh create /nodes/pve1/qemu/abc/migrate --target pve2",
1249
+ "a guest id (VMID)",
1250
+ ],
1251
+ ["pvesh create /nodes/pve_1/qemu/101/status/start", "must be a node name"],
1252
+ ])("%p is Denied: guest actions", (command: string, mentions: string) => {
1253
+ expectDenied(command, mentions);
1254
+ });
1255
+
1256
+ test.each([
1257
+ ["pvesh create /nodes/localhost/qemu/101/status/start"],
1258
+ ["pvesh create /nodes/LocalHost/qemu/101/status/stop"],
1259
+ ["pvesh create /nodes/localhost/lxc/200/migrate --target pve2"],
1260
+ ["pvesh create /nodes/localhost/services/pveproxy/restart"],
1261
+ ])("%p is Denied: a write names its node", (command: string) => {
1262
+ expectDenied(command, "a change must name its node");
1263
+ });
1264
+
1265
+ test.each([
1266
+ [
1267
+ "pvesh create /nodes/pve1/status --command reboot",
1268
+ "rebooting or shutting down a node",
1269
+ ],
1270
+ ["pvesh create /nodes/pve1/stopall", "acts on every guest on the node"],
1271
+ ["pvesh create /nodes/pve1/startall", "acts on every guest on the node"],
1272
+ [
1273
+ "pvesh create /nodes/pve1/migrateall --target pve2",
1274
+ "acts on every guest on the node",
1275
+ ],
1276
+ ["pvesh create /nodes/pve1/suspendall", "acts on every guest on the node"],
1277
+ [
1278
+ "pvesh create /nodes/pve1/qemu/101/snapshot --snapname before",
1279
+ "snapshots is never allowed",
1280
+ ],
1281
+ [
1282
+ "pvesh create /nodes/pve1/qemu/101/snapshot/before/rollback",
1283
+ "a rollback discards everything",
1284
+ ],
1285
+ [
1286
+ "pvesh create /nodes/pve1/lxc/200/snapshot/before/rollback",
1287
+ "a rollback discards everything",
1288
+ ],
1289
+ ])(
1290
+ "%p is Denied: node-wide and destructive actions",
1291
+ (command: string, mentions: string) => {
1292
+ expectDenied(command, mentions);
1293
+ },
1294
+ );
1295
+
1296
+ test.each([
1297
+ ["pvesh create /nodes/pve1/qemu --vmid 999"],
1298
+ ["pvesh create /nodes/pve1/qemu/101/clone --newid 102"],
1299
+ ["pvesh create /nodes/pve1/qemu/101/template"],
1300
+ ["pvesh create /nodes/pve1/qemu/101/move_disk --disk scsi0"],
1301
+ ["pvesh create /nodes/pve1/lxc/200/remote_migrate"],
1302
+ ["pvesh create /nodes/pve1/apt/update"],
1303
+ ["pvesh create /nodes/pve1/storage/local/upload"],
1304
+ ["pvesh create /nodes/pve1/storage/local/download-url"],
1305
+ ["pvesh create /cluster/ha/resources/vm:101/migrate --node pve2"],
1306
+ ["pvesh create /cluster/backup --schedule daily"],
1307
+ ["pvesh create /pools --poolid prod"],
1308
+ ["pvesh create /cluster/firewall/rules"],
1309
+ ["pvesh create /nodes/pve1/qemu/101/status"],
1310
+ ])("%p is Denied: not an allowed create path", (command: string) => {
1311
+ expectDenied(command, PROXMOX_CREATE_PATHS_SUMMARY);
1312
+ });
1313
+
1314
+ test.each([
1315
+ ["pvesh create /version"],
1316
+ ["pvesh create /nodes/pve1/tasks"],
1317
+ ["pvesh create /nodes/pve1/qemu/101/status/current"],
1318
+ ])("%p is Denied: a read path is not an action", (command: string) => {
1319
+ const reason: string = evaluate(command).reason;
1320
+
1321
+ expect(evaluate(command).tier).toBe(ResourceCommandTier.Denied);
1322
+ expect(reason).toMatch(/is a path to read|is not a VM status action/);
1323
+ });
1324
+ });
1325
+
1326
+ describe("the grammar leaves no second reading", () => {
1327
+ test.each([
1328
+ // Single-dash and combined short options.
1329
+ ["pvesh get -timeout 30 /version", '"-timeout" is not supported'],
1330
+ ["pvesh get /nodes/pve1/tasks -errors 1", '"-errors" is not supported'],
1331
+ [
1332
+ "pvesh create /nodes/pve1/qemu/101/status/start -t 5",
1333
+ '"-t" is not supported',
1334
+ ],
1335
+ [
1336
+ "pvesh get /version -abc",
1337
+ "never as single-dash or combined short options",
1338
+ ],
1339
+ ["pvesh get /version -", '"-" is not supported'],
1340
+ // The terminator.
1341
+ ["pvesh get -- /version", 'pvesh commands never need "--"'],
1342
+ ["pvesh get /version --", 'pvesh commands never need "--"'],
1343
+ [
1344
+ "pvesh create /nodes/pve1/qemu/101/status/start -- --forceStop 1",
1345
+ 'never need "--"',
1346
+ ],
1347
+ // A value option with no value, or with an option where the value goes.
1348
+ ["pvesh get /cluster/log --max", "--max needs a value right after it"],
1349
+ [
1350
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout --forceStop 1",
1351
+ 'got the option "--forceStop"',
1352
+ ],
1353
+ [
1354
+ "pvesh create /nodes/pve1/qemu/101/migrate --target --online 1",
1355
+ "--target needs a value",
1356
+ ],
1357
+ // Booleans are never bare.
1358
+ ["pvesh get /nodes/pve1/tasks --errors", "--errors needs a value"],
1359
+ [
1360
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --online",
1361
+ "booleans are written 0 or 1",
1362
+ ],
1363
+ // Duplicates, in every spelling.
1364
+ [
1365
+ "pvesh get /cluster/log --max 5 --max 10",
1366
+ "--max is given more than once",
1367
+ ],
1368
+ [
1369
+ "pvesh get /cluster/log --max=5 --max 10",
1370
+ "--max is given more than once",
1371
+ ],
1372
+ [
1373
+ "pvesh get /version --output-format json --output-format=text",
1374
+ "--output-format is given more than once",
1375
+ ],
1376
+ [
1377
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --forceStop 0 --forceStop=1",
1378
+ "--forceStop is given more than once",
1379
+ ],
1380
+ // Malformed option names.
1381
+ [
1382
+ "pvesh get /version --=json",
1383
+ '"--=json" is not an option pvesh understands',
1384
+ ],
1385
+ ["pvesh get /version --output_format json", "--output_format"],
1386
+ [
1387
+ "pvesh get /version '--output format' json",
1388
+ "is not an option pvesh understands",
1389
+ ],
1390
+ ["pvesh get /version --__proto__ x", "is not an option pvesh understands"],
1391
+ // The command word first.
1392
+ ["pvesh --output-format json get /version", "comes right after pvesh"],
1393
+ ["pvesh -x get /version", "comes right after pvesh"],
1394
+ ])("%p is Denied", (command: string, mentions: string) => {
1395
+ expectDenied(command, mentions);
1396
+ });
1397
+
1398
+ test("an option swallowing the path leaves no path to run", () => {
1399
+ // --timeout takes "/nodes/..." as its value: there is no path left.
1400
+ expectDenied(
1401
+ "pvesh create --timeout /nodes/pve1/qemu/101/status/start",
1402
+ "name the API path after pvesh create",
1403
+ );
1404
+ });
1405
+
1406
+ test("a denied path cannot hide as an option value", () => {
1407
+ expectDenied(
1408
+ "pvesh get /version --output-format /access/users",
1409
+ "--output-format must be one of",
1410
+ );
1411
+ expectDenied(
1412
+ "pvesh get /cluster/log --max=/access/users",
1413
+ "a whole number",
1414
+ );
1415
+ });
1416
+
1417
+ test.each([
1418
+ ["pvesh get /version /cluster/status", 'but "/cluster/status" follows it'],
1419
+ ["pvesh get /nodes/pve1/tasks errors=1", 'but "errors=1" follows it'],
1420
+ [
1421
+ "pvesh create /nodes/pve1/qemu/101/status/start /nodes/pve1/qemu/102/status/start",
1422
+ "exactly one API path",
1423
+ ],
1424
+ [
1425
+ "pvesh create /nodes/pve1/qemu/101/status/start 60",
1426
+ 'but "60" follows it',
1427
+ ],
1428
+ ["pvesh get /version ''", 'but "" follows it'],
1429
+ ])("%p is Denied: one path only", (command: string, mentions: string) => {
1430
+ expectDenied(command, mentions);
1431
+ });
1432
+
1433
+ test.each([
1434
+ [
1435
+ "pvesh get '/nodes/pve1/tasks?errors=1&limit=5'",
1436
+ "carries a query string",
1437
+ ],
1438
+ ["pvesh get /nodes/pve1/tasks?errors=1", "carries a query string"],
1439
+ ["pvesh get '/version#x'", "carries a query string"],
1440
+ [
1441
+ "pvesh get /nodes/pve1/tasks/UPID%3Apve1%3A0001A2B3/status",
1442
+ "is percent-encoded",
1443
+ ],
1444
+ ["pvesh get nodes", "paths start with"],
1445
+ ["pvesh get nodes/pve1/status", "paths start with"],
1446
+ ["pvesh get ''", "the API path is empty"],
1447
+ ["pvesh get //nodes", 'has an empty, "." or ".." segment'],
1448
+ ["pvesh get /nodes//status", "has an empty"],
1449
+ ["pvesh get /nodes/pve1/status//", "has an empty"],
1450
+ ["pvesh get /nodes/pve1/../../access/users", '".." segment'],
1451
+ ["pvesh get /nodes/./pve1/status", '"." or ".."'],
1452
+ ["pvesh get '/nodes/pve 1/status'", "holds a character no API path"],
1453
+ ["pvesh get '/nodes/pve1/status\\'", "holds a character"],
1454
+ ["pvesh get /nodes/pve1/status*", "holds a character"],
1455
+ ["pvesh get /nodes/pve1/qemu/101/config,/access", "holds a character"],
1456
+ ["pvesh get /nodes/pve1/status;id", "a command separator"],
1457
+ ])(
1458
+ "%p is Denied: the path is not written plainly",
1459
+ (command: string, mentions: string) => {
1460
+ expectDenied(command, mentions);
1461
+ },
1462
+ );
1463
+
1464
+ test("a path longer than the limit is Denied", () => {
1465
+ const longPath: string = `/nodes/${"a".repeat(MAX_PROXMOX_API_PATH_CHARS)}`;
1466
+
1467
+ expectDenied(`pvesh get ${longPath}`, "longer than 512 characters");
1468
+ });
1469
+
1470
+ test("a command longer than 2000 characters is Denied", () => {
1471
+ expectDenied(
1472
+ `pvesh get /cluster/log --max ${"1".repeat(2100)}`,
1473
+ "2000-character limit",
1474
+ );
1475
+ expect(
1476
+ evaluateArgv([
1477
+ "pvesh",
1478
+ "get",
1479
+ "/version",
1480
+ "--output-format",
1481
+ "x".repeat(2100),
1482
+ ]).reason,
1483
+ ).toContain("2000-character limit");
1484
+ });
1485
+
1486
+ test("more than 64 words is Denied", () => {
1487
+ const argv: Array<string> = ["pvesh", "get", "/version"];
1488
+
1489
+ while (argv.length <= 64) {
1490
+ argv.push("--output-format", "json");
1491
+ }
1492
+
1493
+ expect(evaluateArgv(argv).reason).toContain("at most 64 words");
1494
+ });
1495
+
1496
+ test.each([
1497
+ // Cyrillic "ѕ" in pvesh, "е" in get, and in path segments.
1498
+ ["pveѕh get /version", "is not a program the Proxmox AI agent runs"],
1499
+ ["pvesh gеt /version", "is not a pvesh command"],
1500
+ ["pvesh get /vеrsion", "holds a character no API path"],
1501
+ ["pvesh get /nodes/pve1/qemu/101/config", "holds a character"],
1502
+ ["pvesh get /version", "paths start with"],
1503
+ ["pvesh create /nodes/pve1/qemu/101/status/stаrt", "holds a character"],
1504
+ // An en dash or em dash is not an option: it is an extra word.
1505
+ ["pvesh get /cluster/log –max 5", 'but "–max" follows it'],
1506
+ ["pvesh get /cluster/log —max 5", 'but "—max" follows it'],
1507
+ // Invisible and non-ASCII space and dash characters.
1508
+ ["pvesh get /version\u200b", "holds a character"],
1509
+ [
1510
+ "pvesh get /cluster/log --ma\u200bx 5",
1511
+ "is not an option pvesh understands",
1512
+ ],
1513
+ ["pvesh get /ver\u00a0sion", "holds a character"],
1514
+ ["pvesh get\u00a0/version", "is not a pvesh command"],
1515
+ [
1516
+ "pvesh create /nodes/pve1/services/pve\u2010proxy/restart",
1517
+ "holds a character",
1518
+ ],
1519
+ [
1520
+ "pvesh create /nodes/pve1/qemu/101/status/start \u2014timeout 5",
1521
+ "follows it",
1522
+ ],
1523
+ ])(
1524
+ "%p is Denied: look-alike characters",
1525
+ (command: string, mentions: string) => {
1526
+ expectDenied(command, mentions);
1527
+ },
1528
+ );
1529
+
1530
+ test.each([
1531
+ ['pvesh get "/nodes/pve1/status"', "/nodes/pve1/status"],
1532
+ ["pvesh get '/nodes/pve1/status'", "/nodes/pve1/status"],
1533
+ ["pvesh get /nodes/'pve1'/status", "/nodes/pve1/status"],
1534
+ [
1535
+ 'pvesh get /nodes/pve1/syslog --since "2025-01-31 14:05"',
1536
+ "/nodes/pve1/syslog",
1537
+ ],
1538
+ ])("quoting %p reads %p", (command: string, path: string) => {
1539
+ expect(evaluate(command).tier).toBe(ResourceCommandTier.Read);
1540
+ expect(request(command).path).toBe(path);
1541
+ });
1542
+
1543
+ test.each([
1544
+ ["pvesh get /version | head", "pipes and redirects are not supported"],
1545
+ ["pvesh get /version > out.json", "pipes and redirects are not supported"],
1546
+ [
1547
+ "pvesh get /version; pvesh delete /pools/prod",
1548
+ "pipes and redirects are not supported",
1549
+ ],
1550
+ [
1551
+ "pvesh get $(cat /etc/pve/priv/token)",
1552
+ "pipes and redirects are not supported",
1553
+ ],
1554
+ ["sudo pvesh get /version", "own permissions"],
1555
+ ["/usr/bin/pvesh get /version", "write the program name without a path"],
1556
+ ["qm start 101", 'starts with "pvesh"'],
1557
+ ["pct stop 200", 'starts with "pvesh"'],
1558
+ ["PVESH get /version", "is not a program"],
1559
+ ])(
1560
+ "%p is Denied by the dispatcher before the tool",
1561
+ (command: string, mentions: string) => {
1562
+ expectDenied(command, mentions);
1563
+ },
1564
+ );
1565
+ });
1566
+
1567
+ describe("totality", () => {
1568
+ test.each([
1569
+ [null],
1570
+ [undefined],
1571
+ [42],
1572
+ ["pvesh get /version"],
1573
+ [{}],
1574
+ [[]],
1575
+ [[null]],
1576
+ [["pvesh", 5]],
1577
+ [["pvesh", "get", {}]],
1578
+ [["pvesh", "get", "/version\n"]],
1579
+ [["pvesh", "get", "/version\0"]],
1580
+ [["pvesh", "get", "/version\r"]],
1581
+ [["kubectl", "get", "pods"]],
1582
+ [["", "get", "/version"]],
1583
+ [["pvesh", ""]],
1584
+ [["pvesh", "get", ""]],
1585
+ ])("the tool never throws on %p and denies it", (argv: unknown) => {
1586
+ const result: ResourceCommandPolicyResult = evaluateArgv(
1587
+ argv as Array<string>,
1588
+ );
1589
+
1590
+ expect(result.tier).toBe(ResourceCommandTier.Denied);
1591
+ expect(result.targets).toEqual([]);
1592
+ expect(typeof result.reason).toBe("string");
1593
+ expect(result.reason.length).toBeGreaterThan(0);
1594
+
1595
+ const parsed: ProxmoxCommandParseResult = parseProxmoxCommand(
1596
+ argv as Array<string>,
1597
+ );
1598
+
1599
+ expect(parsed.request).toBeUndefined();
1600
+ expect(typeof parsed.errorMessage).toBe("string");
1601
+ });
1602
+
1603
+ test("the tool never changes the argv it is handed", () => {
1604
+ const argv: Array<string> = [
1605
+ "pvesh",
1606
+ "create",
1607
+ "/nodes/pve1/qemu/101/status/start/",
1608
+ "--timeout=5",
1609
+ ];
1610
+ const copy: Array<string> = argv.slice();
1611
+
1612
+ evaluateArgv(argv);
1613
+ parseProxmoxCommand(argv);
1614
+
1615
+ expect(argv).toEqual(copy);
1616
+ });
1617
+ });
1618
+
1619
+ describe("the result fields", () => {
1620
+ test("args are the words after pvesh, exactly as written", () => {
1621
+ const result: ResourceCommandPolicyResult = evaluate(
1622
+ "pvesh create /nodes/pve1/qemu/101/status/start/ --timeout=5",
1623
+ );
1624
+
1625
+ expect(result.args).toEqual([
1626
+ "create",
1627
+ "/nodes/pve1/qemu/101/status/start/",
1628
+ "--timeout=5",
1629
+ ]);
1630
+ expect(result.program).toBe("pvesh");
1631
+ });
1632
+
1633
+ test.each([
1634
+ ["pvesh get /version", "pvesh get /version"],
1635
+ ["pvesh get /version ", "pvesh get /version"],
1636
+ [
1637
+ "pvesh get /nodes/pve1/syslog --since '2025-01-31 14:05'",
1638
+ "pvesh get /nodes/pve1/syslog --since '2025-01-31 14:05'",
1639
+ ],
1640
+ [
1641
+ `pvesh get /nodes/pve-2.lab/tasks/${TOKEN_UPID}/status`,
1642
+ `pvesh get '/nodes/pve-2.lab/tasks/${TOKEN_UPID}/status'`,
1643
+ ],
1644
+ [
1645
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout=60 --forceStop 0",
1646
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout=60 --forceStop 0",
1647
+ ],
1648
+ [
1649
+ "pvesh set /nodes/pve1/qemu/101/config",
1650
+ "pvesh set /nodes/pve1/qemu/101/config",
1651
+ ],
1652
+ [
1653
+ "pvesh get '/nodes/pve1/tasks?errors=1&limit=5'",
1654
+ "pvesh get '/nodes/pve1/tasks?errors=1&limit=5'",
1655
+ ],
1656
+ ])("%p is displayed as %p", (command: string, display: string) => {
1657
+ expect(evaluate(command).displayCommand).toBe(display);
1658
+ });
1659
+
1660
+ test("a display command round-trips through the tokenizer", () => {
1661
+ for (const [command] of READ_COMMANDS) {
1662
+ const result: ResourceCommandPolicyResult = evaluate(command);
1663
+
1664
+ expect(tokens(result.displayCommand)).toEqual(tokens(command));
1665
+ expect(evaluate(result.displayCommand).tier).toBe(result.tier);
1666
+ }
1667
+ });
1668
+
1669
+ test("the display command is the tokenizer's rendering", () => {
1670
+ const argv: Array<string> = tokens(
1671
+ `pvesh get /nodes/pve1/tasks/${TOKEN_UPID}/log --limit 5`,
1672
+ );
1673
+
1674
+ expect(evaluateArgv(argv).displayCommand).toBe(
1675
+ renderResourceDisplayCommand(argv),
1676
+ );
1677
+ });
1678
+ });
1679
+
1680
+ describe("parseProxmoxCommand", () => {
1681
+ test.each([
1682
+ [
1683
+ "pvesh get /nodes/pve1/tasks --errors 1 --limit 20 --output-format json",
1684
+ {
1685
+ method: "GET",
1686
+ path: "/nodes/pve1/tasks",
1687
+ params: { errors: "1", limit: "20" },
1688
+ outputFormat: "json",
1689
+ },
1690
+ ],
1691
+ [
1692
+ "pvesh ls /nodes/",
1693
+ { method: "GET", path: "/nodes", params: {}, outputFormat: null },
1694
+ ],
1695
+ [
1696
+ `pvesh get /nodes/pve1/tasks/${UPID}/status`,
1697
+ {
1698
+ method: "GET",
1699
+ path: `/nodes/pve1/tasks/${UPID}/status`,
1700
+ params: {},
1701
+ outputFormat: null,
1702
+ },
1703
+ ],
1704
+ [
1705
+ "pvesh create /nodes/pve1/lxc/200/status/shutdown --timeout=30 --forceStop 0",
1706
+ {
1707
+ method: "POST",
1708
+ path: "/nodes/pve1/lxc/200/status/shutdown",
1709
+ params: { timeout: "30", forceStop: "0" },
1710
+ outputFormat: null,
1711
+ },
1712
+ ],
1713
+ [
1714
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2 --online 1",
1715
+ {
1716
+ method: "POST",
1717
+ path: "/nodes/pve1/qemu/101/migrate",
1718
+ params: { target: "pve2", online: "1" },
1719
+ outputFormat: null,
1720
+ },
1721
+ ],
1722
+ [
1723
+ "pvesh create /nodes/pve1/services/corosync/restart --output-format json-pretty",
1724
+ {
1725
+ method: "POST",
1726
+ path: "/nodes/pve1/services/corosync/restart",
1727
+ params: {},
1728
+ outputFormat: "json-pretty",
1729
+ },
1730
+ ],
1731
+ ])("%p", (command: string, expected: unknown) => {
1732
+ expect(parse(command)).toEqual({ request: expected });
1733
+ });
1734
+
1735
+ test("the argv with its program is what it reads", () => {
1736
+ expect(
1737
+ parseProxmoxCommand(["pvesh", "get", "/version"]).request,
1738
+ ).toBeDefined();
1739
+ expect(parseProxmoxCommand(["get", "/version"]).errorMessage).toContain(
1740
+ 'starts with "pvesh"',
1741
+ );
1742
+ });
1743
+
1744
+ test("a denied command gets the policy's reason and no request", () => {
1745
+ for (const command of [
1746
+ "pvesh get /access/users",
1747
+ "pvesh set /nodes/pve1/qemu/101/config --memory 1",
1748
+ "pvesh create /nodes/pve1/services/pveproxy/stop",
1749
+ "pvesh get /cluster/log --max 5 --max 6",
1750
+ ]) {
1751
+ const parsed: ProxmoxCommandParseResult = parse(command);
1752
+
1753
+ expect(parsed.request).toBeUndefined();
1754
+ expect(parsed.errorMessage).toBe(evaluate(command).reason);
1755
+ }
1756
+ });
1757
+
1758
+ test("builds a request exactly when the policy does not deny", () => {
1759
+ const commands: Array<string> = [
1760
+ ...READ_COMMANDS.map(([command]: [string, string]): string => {
1761
+ return command;
1762
+ }),
1763
+ "pvesh create /nodes/pve1/qemu/101/status/start",
1764
+ "pvesh create /nodes/pve1/qemu/101/status/reset",
1765
+ "pvesh create /nodes/pve1/lxc/200/status/reset",
1766
+ "pvesh create /nodes/pve1/services/pveproxy/restart",
1767
+ "pvesh create /nodes/pve1/services/pveproxy/stop",
1768
+ "pvesh create /nodes/pve1/qemu/101/migrate --target pve2",
1769
+ "pvesh get /access/users",
1770
+ "pvesh delete /pools/prod",
1771
+ "pvesh get /version --bogus 1",
1772
+ ];
1773
+
1774
+ for (const command of commands) {
1775
+ const denied: boolean =
1776
+ evaluate(command).tier === ResourceCommandTier.Denied;
1777
+ const parsed: ProxmoxCommandParseResult = parse(command);
1778
+
1779
+ expect(parsed.request === undefined).toBe(denied);
1780
+ expect(parsed.errorMessage === undefined).toBe(!denied);
1781
+ }
1782
+ });
1783
+
1784
+ test("the method follows the command word, never the path", () => {
1785
+ expect(
1786
+ request("pvesh get /nodes/pve1/qemu/101/status/current").method,
1787
+ ).toBe("GET");
1788
+ expect(request("pvesh ls /nodes").method).toBe("GET");
1789
+ expect(
1790
+ request("pvesh create /nodes/pve1/qemu/101/status/stop").method,
1791
+ ).toBe("POST");
1792
+ });
1793
+
1794
+ test("each call returns its own params object", () => {
1795
+ const first: ProxmoxApiRequest = request("pvesh get /cluster/log --max 5");
1796
+
1797
+ first.params["max"] = "999999";
1798
+
1799
+ expect(request("pvesh get /cluster/log --max 5").params).toEqual({
1800
+ max: "5",
1801
+ });
1802
+ });
1803
+
1804
+ test("request segments never hold characters that break a URL", () => {
1805
+ for (const [command] of READ_COMMANDS) {
1806
+ for (const segment of request(command).path.slice(1).split("/")) {
1807
+ expect(segment).toMatch(/^[A-Za-z0-9._:@!-]+$/);
1808
+ }
1809
+ }
1810
+ });
1811
+ });
1812
+
1813
+ describe("through the dispatcher", () => {
1814
+ function autoExecute(
1815
+ command: string,
1816
+ options: {
1817
+ allowlistPatterns?: Array<string>;
1818
+ bypassApproval?: boolean;
1819
+ } = {},
1820
+ ): ResourceAutoExecutionVerdict {
1821
+ return ResourceCommandPolicy.evaluateForAutoExecution({
1822
+ resourceType: AiResourceType.ProxmoxCluster,
1823
+ command,
1824
+ allowlistPatterns: options.allowlistPatterns || [],
1825
+ bypassApproval: options.bypassApproval === true,
1826
+ });
1827
+ }
1828
+
1829
+ test("a read is AutoApproved (it changes nothing)", () => {
1830
+ const verdict: ResourceAutoExecutionVerdict = autoExecute(
1831
+ "pvesh get /cluster/status",
1832
+ );
1833
+
1834
+ expect(verdict.verdict).toBe(
1835
+ AiRemediationCommandPolicyVerdict.AutoApproved,
1836
+ );
1837
+ expect(verdict.tier).toBe(ResourceCommandTier.Read);
1838
+ });
1839
+
1840
+ test("a SafeWrite is AutoApproved", () => {
1841
+ const verdict: ResourceAutoExecutionVerdict = autoExecute(
1842
+ "pvesh create /nodes/pve1/qemu/101/status/start",
1843
+ );
1844
+
1845
+ expect(verdict.verdict).toBe(
1846
+ AiRemediationCommandPolicyVerdict.AutoApproved,
1847
+ );
1848
+ expect(verdict.tier).toBe(ResourceCommandTier.SafeWrite);
1849
+ });
1850
+
1851
+ test("a RiskyWrite needs approval", () => {
1852
+ const verdict: ResourceAutoExecutionVerdict = autoExecute(
1853
+ "pvesh create /nodes/pve1/qemu/101/status/stop",
1854
+ );
1855
+
1856
+ expect(verdict.verdict).toBe(
1857
+ AiRemediationCommandPolicyVerdict.RequiresApproval,
1858
+ );
1859
+ expect(verdict.requiresHuman).toBeUndefined();
1860
+ });
1861
+
1862
+ test("a RiskyWrite runs when approvals are bypassed", () => {
1863
+ expect(
1864
+ autoExecute("pvesh create /nodes/pve1/qemu/101/status/stop", {
1865
+ bypassApproval: true,
1866
+ }).verdict,
1867
+ ).toBe(AiRemediationCommandPolicyVerdict.AutoApproved);
1868
+ });
1869
+
1870
+ test("a RiskyWrite runs when the allowlist names it", () => {
1871
+ const verdict: ResourceAutoExecutionVerdict = autoExecute(
1872
+ "pvesh create /nodes/pve1/services/pveproxy/restart",
1873
+ {
1874
+ allowlistPatterns: [
1875
+ "pvesh create /nodes/pve1/services/pveproxy/restart",
1876
+ ],
1877
+ },
1878
+ );
1879
+
1880
+ expect(verdict.verdict).toBe(
1881
+ AiRemediationCommandPolicyVerdict.AutoApproved,
1882
+ );
1883
+ expect(verdict.reason).toBe("Matched the resource's command allowlist.");
1884
+ });
1885
+
1886
+ test("the allowlist matches word by word, options included", () => {
1887
+ const patterns: Array<string> = [
1888
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout *",
1889
+ ];
1890
+
1891
+ expect(
1892
+ autoExecute(
1893
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout 60",
1894
+ { allowlistPatterns: patterns },
1895
+ ).verdict,
1896
+ ).toBe(AiRemediationCommandPolicyVerdict.AutoApproved);
1897
+ // Another spelling is another command to the allowlist.
1898
+ expect(
1899
+ autoExecute(
1900
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout=60",
1901
+ { allowlistPatterns: patterns },
1902
+ ).verdict,
1903
+ ).toBe(AiRemediationCommandPolicyVerdict.RequiresApproval);
1904
+ // Another guest is another path.
1905
+ expect(
1906
+ autoExecute(
1907
+ "pvesh create /nodes/pve1/qemu/102/status/shutdown --timeout 60",
1908
+ { allowlistPatterns: patterns },
1909
+ ).verdict,
1910
+ ).toBe(AiRemediationCommandPolicyVerdict.RequiresApproval);
1911
+ });
1912
+
1913
+ test.each([
1914
+ ["pvesh create /nodes/pve1/qemu/101/migrate --target pve2"],
1915
+ ["pvesh create /nodes/pve1/services/corosync/restart"],
1916
+ ["pvesh create /nodes/pve1/services/pve-cluster/reload"],
1917
+ ])(
1918
+ "%p always asks a human: not bypass, not the allowlist",
1919
+ (command: string) => {
1920
+ for (const verdict of [
1921
+ autoExecute(command),
1922
+ autoExecute(command, { bypassApproval: true }),
1923
+ autoExecute(command, { allowlistPatterns: [command] }),
1924
+ autoExecute(command, {
1925
+ allowlistPatterns: [command],
1926
+ bypassApproval: true,
1927
+ }),
1928
+ ]) {
1929
+ expect(verdict.verdict).toBe(
1930
+ AiRemediationCommandPolicyVerdict.RequiresApproval,
1931
+ );
1932
+ expect(verdict.requiresHuman).toBe(true);
1933
+ }
1934
+ },
1935
+ );
1936
+
1937
+ test.each([
1938
+ ["pvesh get /access/users"],
1939
+ ["pvesh set /nodes/pve1/qemu/101/config --memory 1"],
1940
+ ["pvesh create /nodes/pve1/services/pveproxy/stop"],
1941
+ ])("%p is Denied whatever the settings", (command: string) => {
1942
+ const verdict: ResourceAutoExecutionVerdict = autoExecute(command, {
1943
+ allowlistPatterns: [command],
1944
+ bypassApproval: true,
1945
+ });
1946
+
1947
+ expect(verdict.verdict).toBe(AiRemediationCommandPolicyVerdict.Denied);
1948
+ expect(verdict.reason).toContain("cannot run even with human approval");
1949
+ });
1950
+
1951
+ describe("matchesAllowlist", () => {
1952
+ function matches(command: string, patterns: Array<string>): boolean {
1953
+ return ResourceCommandPolicy.matchesAllowlist({
1954
+ resourceType: AiResourceType.ProxmoxCluster,
1955
+ command,
1956
+ patterns,
1957
+ });
1958
+ }
1959
+
1960
+ test("an exact entry matches", () => {
1961
+ expect(
1962
+ matches("pvesh create /nodes/pve1/qemu/101/status/stop", [
1963
+ "pvesh create /nodes/pve1/qemu/101/status/stop",
1964
+ ]),
1965
+ ).toBe(true);
1966
+ });
1967
+
1968
+ test("a * is one whole word, never part of a path", () => {
1969
+ expect(
1970
+ matches("pvesh create /nodes/pve1/qemu/101/status/stop", [
1971
+ "pvesh create /nodes/*/qemu/*/status/stop",
1972
+ ]),
1973
+ ).toBe(false);
1974
+ expect(
1975
+ matches(
1976
+ "pvesh create /nodes/pve1/qemu/101/status/stop --output-format json",
1977
+ [
1978
+ "pvesh create /nodes/pve1/qemu/101/status/stop --output-format json",
1979
+ ],
1980
+ ),
1981
+ ).toBe(true);
1982
+ expect(
1983
+ matches(
1984
+ "pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout 30",
1985
+ ["pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout *"],
1986
+ ),
1987
+ ).toBe(true);
1988
+ });
1989
+
1990
+ test("an entry for a read or a denied command never matches", () => {
1991
+ expect(matches("pvesh get /version", ["pvesh get /version"])).toBe(false);
1992
+ expect(
1993
+ matches("pvesh create /nodes/pve1/services/pveproxy/stop", [
1994
+ "pvesh create /nodes/pve1/services/pveproxy/stop",
1995
+ ]),
1996
+ ).toBe(false);
1997
+ });
1998
+ });
1999
+
2000
+ describe("describeAllowlistPatternProblem", () => {
2001
+ function problem(pattern: string): string | null {
2002
+ return ResourceCommandPolicy.describeAllowlistPatternProblem({
2003
+ resourceType: AiResourceType.ProxmoxCluster,
2004
+ pattern,
2005
+ });
2006
+ }
2007
+
2008
+ test.each([
2009
+ ["pvesh create /nodes/pve1/qemu/101/status/stop"],
2010
+ ["pvesh create /nodes/pve1/qemu/101/status/shutdown --timeout *"],
2011
+ ["pvesh create /nodes/pve1/services/pveproxy/restart"],
2012
+ ["pvesh create /nodes/pve1/qemu/101/status/start"],
2013
+ ["pvesh create /nodes/pve1/qemu/101/migrate --target *"],
2014
+ ])("%p is valid", (pattern: string) => {
2015
+ expect(problem(pattern)).toBeNull();
2016
+ });
2017
+
2018
+ test("a read pre-approves nothing", () => {
2019
+ expect(problem("pvesh get /cluster/status")).toContain(
2020
+ "is a read-only command",
2021
+ );
2022
+ expect(problem("pvesh get /cluster/log --max *")).toContain(
2023
+ "is a read-only command",
2024
+ );
2025
+ });
2026
+
2027
+ test("a * standing for an option with a closed set of values never runs", () => {
2028
+ // No stand-in for the * is one of the option's values.
2029
+ expect(problem("pvesh get /cluster/resources --type *")).toContain(
2030
+ "can never match a command that runs",
2031
+ );
2032
+ expect(
2033
+ problem(
2034
+ "pvesh create /nodes/pve1/qemu/101/status/stop --output-format *",
2035
+ ),
2036
+ ).toContain("can never match a command that runs");
2037
+ });
2038
+
2039
+ test.each([
2040
+ ["pvesh create *", "can never match a command that runs"],
2041
+ [
2042
+ "pvesh set /nodes/pve1/qemu/101/config",
2043
+ "pvesh set changes configuration",
2044
+ ],
2045
+ [
2046
+ "pvesh create /nodes/pve1/services/pveproxy/stop",
2047
+ "stopping a node service",
2048
+ ],
2049
+ ["pvesh get /access/users", "everything under /access"],
2050
+ [
2051
+ "pvesh * /nodes/pve1/qemu/101/status/stop",
2052
+ "has a * where the command goes",
2053
+ ],
2054
+ ["pvesh create", "fewer than two words"],
2055
+ [
2056
+ "qm stop 101",
2057
+ "does not start with a program the Proxmox AI agent runs",
2058
+ ],
2059
+ ])("%p is refused", (pattern: string, mentions: string) => {
2060
+ expect(problem(pattern)).toContain(mentions);
2061
+ });
2062
+ });
2063
+
2064
+ describe("isBroadAllowlistPattern", () => {
2065
+ function broad(pattern: string): boolean {
2066
+ return ResourceCommandPolicy.isBroadAllowlistPattern({
2067
+ resourceType: AiResourceType.ProxmoxCluster,
2068
+ pattern,
2069
+ });
2070
+ }
2071
+
2072
+ test("an entry with no * is never broad", () => {
2073
+ expect(broad("pvesh create /nodes/pve1/qemu/101/status/stop")).toBe(
2074
+ false,
2075
+ );
2076
+ expect(broad("pvesh create /nodes/pve1/services/pveproxy/restart")).toBe(
2077
+ false,
2078
+ );
2079
+ });
2080
+
2081
+ test("an invalid entry is never broad", () => {
2082
+ expect(broad("pvesh get /cluster/resources --type *")).toBe(false);
2083
+ expect(broad("pvesh create *")).toBe(false);
2084
+ });
2085
+ });
2086
+
2087
+ describe("getWriteScopeRefusal", () => {
2088
+ function refusal(
2089
+ command: string,
2090
+ posture: {
2091
+ allowWrites?: boolean;
2092
+ writeTargets?: Array<string>;
2093
+ protectedTargets?: Array<string>;
2094
+ } = {},
2095
+ ): string | null {
2096
+ return ResourceCommandPolicy.getWriteScopeRefusal({
2097
+ result: evaluate(command),
2098
+ allowWrites: posture.allowWrites ?? true,
2099
+ writeTargets: posture.writeTargets ?? [],
2100
+ protectedTargets: posture.protectedTargets ?? [],
2101
+ resourceType: AiResourceType.ProxmoxCluster,
2102
+ });
2103
+ }
2104
+
2105
+ test("a read is never refused, even by a read-only agent", () => {
2106
+ expect(
2107
+ refusal("pvesh get /cluster/status", {
2108
+ allowWrites: false,
2109
+ writeTargets: ["999"],
2110
+ }),
2111
+ ).toBeNull();
2112
+ });
2113
+
2114
+ test("a read-only agent refuses every write, naming the setting", () => {
2115
+ const text: string | null = refusal(
2116
+ "pvesh create /nodes/pve1/qemu/101/status/start",
2117
+ { allowWrites: false },
2118
+ );
2119
+
2120
+ expect(text).toContain("Proxmox AI agent");
2121
+ expect(text).toContain("ONEUPTIME_AI_ALLOW_WRITES=true");
2122
+ });
2123
+
2124
+ test("a denied command is refused", () => {
2125
+ expect(refusal("pvesh get /access/users")).toContain(
2126
+ "is denied by the command policy",
2127
+ );
2128
+ });
2129
+
2130
+ test("a write inside the scope runs", () => {
2131
+ expect(
2132
+ refusal("pvesh create /nodes/pve1/qemu/101/status/start", {
2133
+ writeTargets: ["1*"],
2134
+ }),
2135
+ ).toBeNull();
2136
+ expect(
2137
+ refusal("pvesh create /nodes/pve1/services/pveproxy/restart", {
2138
+ writeTargets: ["pve1/*"],
2139
+ }),
2140
+ ).toBeNull();
2141
+ expect(
2142
+ refusal("pvesh create /nodes/pve2/qemu/101/migrate --target pve3", {
2143
+ writeTargets: ["101"],
2144
+ }),
2145
+ ).toBeNull();
2146
+ });
2147
+
2148
+ test("a write outside the scope is refused", () => {
2149
+ expect(
2150
+ refusal("pvesh create /nodes/pve1/qemu/205/status/stop", {
2151
+ writeTargets: ["1*"],
2152
+ }),
2153
+ ).toContain("would change 205, which is outside the targets");
2154
+ expect(
2155
+ refusal("pvesh create /nodes/pve2/services/pveproxy/restart", {
2156
+ writeTargets: ["pve1/*"],
2157
+ }),
2158
+ ).toContain("would change pve2/pveproxy");
2159
+ });
2160
+
2161
+ test("the target is the VMID on whichever node the guest runs", () => {
2162
+ for (const node of ["pve1", "pve2", "pve3.lab"]) {
2163
+ expect(
2164
+ refusal(`pvesh create /nodes/${node}/qemu/105/status/stop`, {
2165
+ protectedTargets: ["105"],
2166
+ }),
2167
+ ).toContain("which the Proxmox AI agent protects (105)");
2168
+ }
2169
+ });
2170
+
2171
+ test("a protected node service is never changed", () => {
2172
+ expect(
2173
+ refusal("pvesh create /nodes/pve1/services/pveproxy/restart", {
2174
+ protectedTargets: ["pve1/pveproxy"],
2175
+ }),
2176
+ ).toContain("which the Proxmox AI agent protects");
2177
+ expect(
2178
+ refusal("pvesh create /nodes/PVE1/services/pveproxy/restart", {
2179
+ protectedTargets: ["pve1/*"],
2180
+ }),
2181
+ ).toContain("which the Proxmox AI agent protects");
2182
+ });
2183
+
2184
+ test("migrating a protected guest is refused", () => {
2185
+ expect(
2186
+ refusal("pvesh create /nodes/pve1/lxc/200/migrate --target pve2", {
2187
+ protectedTargets: ["200"],
2188
+ }),
2189
+ ).toContain("protects (200)");
2190
+ });
2191
+ });
2192
+ });
2193
+
2194
+ describe("the guides", () => {
2195
+ test("the read guide names every readable path family", () => {
2196
+ const guide: string = ProxmoxCommandPolicy.readCommandGuide;
2197
+
2198
+ for (const fragment of [
2199
+ "pvesh get",
2200
+ "pvesh ls",
2201
+ "--output-format json|json-pretty|text",
2202
+ "/version",
2203
+ "/cluster/status",
2204
+ "/cluster/resources [--type vm|storage|node|sdn]",
2205
+ "/cluster/ha/status/current",
2206
+ "/cluster/ha/resources",
2207
+ "/cluster/log [--max N]",
2208
+ "/cluster/tasks",
2209
+ "/cluster/replication",
2210
+ "/cluster/backup",
2211
+ "/pools",
2212
+ "/storage",
2213
+ "/nodes/{node}/status",
2214
+ "/services/{service}/state",
2215
+ "/storage/{storage}/status",
2216
+ "/disks/smart --disk /dev/sda",
2217
+ "/rrddata --timeframe hour|day|week",
2218
+ "/nodes/{node}/tasks [--errors 1]",
2219
+ "/nodes/{node}/tasks/{upid}/log",
2220
+ "/nodes/{node}/syslog",
2221
+ "/nodes/{node}/journal [--lastentries N]",
2222
+ "/nodes/{node}/{type}/{vmid}/status/current",
2223
+ "/config [--current 1]",
2224
+ "/pending",
2225
+ "/snapshot",
2226
+ "agent/info|get-osinfo|get-fsinfo|network-get-interfaces|get-host-name|get-time",
2227
+ "/access",
2228
+ ]) {
2229
+ expect(guide).toContain(fragment);
2230
+ }
2231
+ });
2232
+
2233
+ test("the write guide names every write and its tier", () => {
2234
+ const guide: string = ProxmoxCommandPolicy.writeCommandGuide;
2235
+
2236
+ for (const fragment of [
2237
+ "pvesh create",
2238
+ "Safe (runs unattended in Automatic mode)",
2239
+ "/status/start",
2240
+ "/status/resume",
2241
+ "/status/reboot",
2242
+ "Riskier",
2243
+ "/status/shutdown [--timeout N] [--forceStop 0]",
2244
+ "/status/stop",
2245
+ "/status/suspend",
2246
+ "/nodes/{node}/qemu/{vmid}/status/reset",
2247
+ "/nodes/{node}/services/{service}/start|restart|reload",
2248
+ "Always asks a human",
2249
+ "/migrate --target {othernode}",
2250
+ "corosync or pve-cluster",
2251
+ "never localhost",
2252
+ "Never",
2253
+ ]) {
2254
+ expect(guide).toContain(fragment);
2255
+ }
2256
+
2257
+ for (const service of PROXMOX_RESTARTABLE_SERVICES) {
2258
+ expect(guide).toContain(service);
2259
+ }
2260
+ });
2261
+
2262
+ test("the guides are compact markdown bullets", () => {
2263
+ for (const guide of [
2264
+ ProxmoxCommandPolicy.readCommandGuide,
2265
+ ProxmoxCommandPolicy.writeCommandGuide,
2266
+ ]) {
2267
+ for (const line of guide.split("\n")) {
2268
+ expect(line.startsWith("- ")).toBe(true);
2269
+ }
2270
+
2271
+ expect(guide.length).toBeLessThan(2500);
2272
+ }
2273
+ });
2274
+
2275
+ test("the readable-path summary names every read route's last segment", () => {
2276
+ for (const [command] of READ_COMMANDS) {
2277
+ const path: string = request(command).path;
2278
+ const last: string = path.slice(path.lastIndexOf("/") + 1);
2279
+
2280
+ if (
2281
+ DIGITS_ONLY.test(last) ||
2282
+ last.startsWith("pve") ||
2283
+ last === "localhost"
2284
+ ) {
2285
+ continue;
2286
+ }
2287
+
2288
+ expect(PROXMOX_READABLE_PATHS_SUMMARY).toContain(last);
2289
+ }
2290
+ });
2291
+ });
2292
+
2293
+ describe("redaction of pvesh output", () => {
2294
+ function redact(text: string): string {
2295
+ return redactResourceCommandOutput({
2296
+ resourceType: AiResourceType.ProxmoxCluster,
2297
+ program: "pvesh",
2298
+ text,
2299
+ });
2300
+ }
2301
+
2302
+ test("a VM config's cloud-init password and other secrets", () => {
2303
+ const config: string = JSON.stringify(
2304
+ {
2305
+ cores: 2,
2306
+ memory: "4096",
2307
+ ciuser: "ubuntu",
2308
+ cipassword: "$5$abcdefgh$Zq0r7Qm8vYl3nWb4kE1uXo9pTzAa5sDdFfGgHhJjKk2",
2309
+ net0: "virtio=BC:24:11:AA:BB:CC,bridge=vmbr0",
2310
+ description: "db host; admin password=Hunter2Hunter2",
2311
+ name: "db-1",
2312
+ },
2313
+ null,
2314
+ 2,
2315
+ );
2316
+ const redacted: string = redact(config);
2317
+
2318
+ expect(redacted).not.toContain(
2319
+ "Zq0r7Qm8vYl3nWb4kE1uXo9pTzAa5sDdFfGgHhJjKk2",
2320
+ );
2321
+ expect(redacted).not.toContain("Hunter2Hunter2");
2322
+ expect(redacted).toContain(`"cipassword": "${RESOURCE_REDACTED_MARKER}"`);
2323
+ expect(redacted).toContain('"ciuser": "ubuntu"');
2324
+ expect(redacted).toContain('"name": "db-1"');
2325
+ expect(redacted).toContain("virtio=BC:24:11:AA:BB:CC,bridge=vmbr0");
2326
+ });
2327
+
2328
+ test("compact JSON too", () => {
2329
+ expect(redact('{"cipassword":"s3cr3t-value","cores":4}')).toBe(
2330
+ `{"cipassword":"${RESOURCE_REDACTED_MARKER}","cores":4}`,
2331
+ );
2332
+ });
2333
+
2334
+ /*
2335
+ * Pinned for the agent's executor: the API wraps every answer as
2336
+ * {"data": ...}, and the generic rules read a "data" object as a
2337
+ * Kubernetes Secret's data and mask all of it. The agent prints the data
2338
+ * member, never the envelope.
2339
+ */
2340
+ test("a {data: ...} envelope would be masked whole", () => {
2341
+ expect(redact('{"data":{"cores":4,"name":"db-1"}}')).not.toContain("db-1");
2342
+ expect(redact('{"cores":4,"name":"db-1"}')).toBe(
2343
+ '{"cores":4,"name":"db-1"}',
2344
+ );
2345
+ });
2346
+
2347
+ test.each([
2348
+ [
2349
+ '[{"key":"cipassword","value":"old-hash-value","pending":"new-hash-value"},{"key":"cores","value":2,"pending":4}]',
2350
+ ],
2351
+ [
2352
+ '[{"pending":"new-hash-value","value":"old-hash-value","key":"cipassword"}]',
2353
+ ],
2354
+ [
2355
+ '[\n {\n "value": "old-hash-value",\n "key": "cipassword",\n "pending": "new-hash-value"\n }\n]',
2356
+ ],
2357
+ [
2358
+ '[{"key":"password","value":"old-hash-value","pending":"new-hash-value"}]',
2359
+ ],
2360
+ ])("a /pending entry %p", (text: string) => {
2361
+ const redacted: string = redact(text);
2362
+
2363
+ expect(redacted).not.toContain("old-hash-value");
2364
+ expect(redacted).not.toContain("new-hash-value");
2365
+ });
2366
+
2367
+ test("a /pending entry for an ordinary key is untouched", () => {
2368
+ const text: string =
2369
+ '[{"key":"cores","value":2,"pending":4},{"key":"name","value":"db-1"}]';
2370
+
2371
+ expect(redact(text)).toBe(text);
2372
+ });
2373
+
2374
+ test("the cluster storage list's keyring, password and encryption key", () => {
2375
+ const storage: string = JSON.stringify([
2376
+ {
2377
+ storage: "ceph-rbd",
2378
+ type: "rbd",
2379
+ keyring: "/etc/pve/priv/ceph/ceph-rbd.keyring-secret-path",
2380
+ monhost: "10.0.0.1",
2381
+ },
2382
+ {
2383
+ storage: "pbs",
2384
+ type: "pbs",
2385
+ password: "pbs-password-value",
2386
+ "encryption-key": "pbs-encryption-key-value",
2387
+ fingerprint: "aa:bb:cc",
2388
+ },
2389
+ ]);
2390
+ const redacted: string = redact(storage);
2391
+
2392
+ expect(redacted).not.toContain("keyring-secret-path");
2393
+ expect(redacted).not.toContain("pbs-password-value");
2394
+ expect(redacted).not.toContain("pbs-encryption-key-value");
2395
+ expect(redacted).toContain('"monhost":"10.0.0.1"');
2396
+ expect(redacted).toContain('"fingerprint":"aa:bb:cc"');
2397
+ });
2398
+
2399
+ test("JSON escaped inside another JSON string", () => {
2400
+ const redacted: string = redact(
2401
+ '{"log":"{\\"keyring\\":\\"escaped-keyring-value\\",\\"cores\\":2}"}',
2402
+ );
2403
+
2404
+ expect(redacted).not.toContain("escaped-keyring-value");
2405
+ expect(redacted).toContain('\\"cores\\":2');
2406
+ });
2407
+
2408
+ test.each([
2409
+ ["cipassword: hashed-password-value", "cipassword: "],
2410
+ ["keyring: /etc/pve/priv/ceph/secret-keyring-path", "keyring: "],
2411
+ ["encryption-key = encryption-key-value", "encryption-key = "],
2412
+ [" password plain-password-value", " password "],
2413
+ ["- keyring: list-keyring-value", "- keyring: "],
2414
+ ])("the text line %p", (line: string, kept: string) => {
2415
+ const redacted: string = redact(`cores: 2\n${line}\nname: db-1`);
2416
+
2417
+ expect(redacted).toContain(`${kept}${RESOURCE_REDACTED_MARKER}`);
2418
+ expect(redacted).toContain("cores: 2");
2419
+ expect(redacted).toContain("name: db-1");
2420
+ });
2421
+
2422
+ test("pvesh's text table", () => {
2423
+ const table: string = [
2424
+ "┌────────────┬──────────────────────┐",
2425
+ "│ key │ value │",
2426
+ "╞════════════╪══════════════════════╡",
2427
+ "│ cipassword │ table-password-value │",
2428
+ "├────────────┼──────────────────────┤",
2429
+ "│ cores │ 2 │",
2430
+ "└────────────┴──────────────────────┘",
2431
+ ].join("\n");
2432
+ const redacted: string = redact(table);
2433
+
2434
+ expect(redacted).not.toContain("table-password-value");
2435
+ expect(redacted).toContain(`│ cipassword │ ${RESOURCE_REDACTED_MARKER} │`);
2436
+ expect(redacted).toContain("│ key │ value │");
2437
+ expect(redacted).toContain("│ cores │ 2 │");
2438
+ });
2439
+
2440
+ test("a pending table masks every column of the secret's row", () => {
2441
+ const redacted: string = redact(
2442
+ "| key | value | pending |\n| cipassword | old-cell | new-cell |\n| cores | 2 | 4 |",
2443
+ );
2444
+
2445
+ expect(redacted).not.toContain("old-cell");
2446
+ expect(redacted).not.toContain("new-cell");
2447
+ expect(redacted).toContain("| cores | 2 | 4 |");
2448
+ expect(redacted).toContain("| key | value | pending |");
2449
+ });
2450
+
2451
+ test("counts what it masks", () => {
2452
+ expect(
2453
+ redactResourceCommandOutputWithCount({
2454
+ resourceType: AiResourceType.ProxmoxCluster,
2455
+ program: "pvesh",
2456
+ text: '{"cipassword":"a-secret","keyring":"b-secret","cores":2}',
2457
+ }).redactionCount,
2458
+ ).toBeGreaterThanOrEqual(2);
2459
+ });
2460
+
2461
+ test("an already-masked or empty value is left as it is", () => {
2462
+ expect(redact('{"cipassword":"[redacted]"}')).toBe(
2463
+ '{"cipassword":"[redacted]"}',
2464
+ );
2465
+ expect(redact('{"keyring":""}')).toBe('{"keyring":""}');
2466
+ });
2467
+
2468
+ test("ordinary Proxmox output passes through unchanged", () => {
2469
+ const outputs: Array<string> = [
2470
+ JSON.stringify([
2471
+ { node: "pve1", status: "online", cpu: 0.03, maxmem: 67430035456 },
2472
+ ]),
2473
+ JSON.stringify({
2474
+ version: "8.2.4",
2475
+ release: "8.2",
2476
+ repoid: "faa83925c9641325",
2477
+ }),
2478
+ "Jan 31 14:05:01 pve1 pveproxy[1234]: worker 5678 started",
2479
+ `${UPID}\nTASK OK`,
2480
+ "HEALTH_OK",
2481
+ ];
2482
+
2483
+ for (const text of outputs) {
2484
+ expect(redact(text)).toBe(text);
2485
+ }
2486
+ });
2487
+
2488
+ test("the hook runs only for pvesh", () => {
2489
+ expect(
2490
+ redactResourceCommandOutput({
2491
+ resourceType: AiResourceType.DockerHost,
2492
+ program: "docker",
2493
+ text: "keyring: docker-keyring-value",
2494
+ }),
2495
+ ).toContain("docker-keyring-value");
2496
+ });
2497
+ });