@oneuptime/common 14.0.9 → 14.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  2. package/Models/DatabaseModels/CephCluster.ts +213 -0
  3. package/Models/DatabaseModels/DatabaseServer.ts +213 -0
  4. package/Models/DatabaseModels/DockerHost.ts +213 -0
  5. package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
  6. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  7. package/Models/DatabaseModels/Host.ts +213 -0
  8. package/Models/DatabaseModels/Index.ts +2 -0
  9. package/Models/DatabaseModels/PodmanHost.ts +213 -0
  10. package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
  11. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  12. package/Models/DatabaseModels/RunnerJob.ts +144 -0
  13. package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
  14. package/Server/API/AutoRemediationAPI.ts +392 -1
  15. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  18. package/Server/Infrastructure/Semaphore.ts +22 -0
  19. package/Server/Middleware/TelemetryIngest.ts +15 -5
  20. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  21. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  22. package/Server/Services/CephClusterService.ts +109 -1
  23. package/Server/Services/DatabaseServerService.ts +116 -1
  24. package/Server/Services/DockerHostService.ts +109 -1
  25. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  26. package/Server/Services/HostService.ts +126 -4
  27. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  28. package/Server/Services/PodmanHostService.ts +109 -1
  29. package/Server/Services/ProxmoxClusterService.ts +110 -1
  30. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  31. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  32. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  33. package/Server/Services/RunnerJobService.ts +611 -4
  34. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  35. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  36. package/Server/Services/VMwareVCenterService.ts +110 -1
  37. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1434 -32
  38. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
  39. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  40. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
  41. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  42. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  43. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  44. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  45. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
  46. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +72 -2
  47. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
  48. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
  49. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
  50. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  51. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  52. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  53. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  54. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  55. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  56. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  57. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
  58. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
  59. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  60. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +366 -143
  61. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  62. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  63. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  64. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  65. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  66. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  67. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +93 -0
  68. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
  69. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  70. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  71. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  72. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  73. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  74. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
  75. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
  76. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  77. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  78. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  79. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
  80. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  81. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  82. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  83. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  84. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  85. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  86. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  87. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
  88. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
  89. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
  90. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  91. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  92. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  93. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  95. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  96. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  97. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  98. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  99. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  100. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  101. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  102. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  103. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  104. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  105. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
  106. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  107. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  108. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  109. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  110. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  111. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
  112. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  113. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  114. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  115. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
  116. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  117. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  118. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
  119. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  120. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  121. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  122. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
  123. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  124. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  125. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  126. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  127. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  128. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
  129. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  130. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  131. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  132. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  133. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  134. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  139. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  140. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  141. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  142. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  143. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  144. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  145. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  146. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  147. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  148. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  149. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  150. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  151. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
  152. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  153. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  154. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  155. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  156. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  157. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  158. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  159. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  160. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  161. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  162. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  163. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  164. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  165. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  166. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  167. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  168. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  169. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  170. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  171. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  172. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  173. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  174. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  175. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
  176. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  177. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  178. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  179. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  180. package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
  181. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  182. package/Types/Runbook/RunbookStepType.ts +29 -0
  183. package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
  184. package/Utils/AI/InvestigationReport.ts +121 -15
  185. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  186. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  187. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  188. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  189. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  190. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  191. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  192. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  193. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  194. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  195. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  196. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  197. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  198. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  199. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  200. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
  202. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
  204. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
  206. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
  208. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  210. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  211. package/build/dist/Models/DatabaseModels/Host.js +219 -0
  212. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  214. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
  216. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
  218. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  219. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  220. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  221. package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
  222. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  223. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
  224. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  225. package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
  226. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  227. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  228. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  234. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  235. package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
  236. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  237. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  239. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  240. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  241. package/build/dist/Server/Services/CephClusterService.js +104 -0
  242. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  243. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  244. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  245. package/build/dist/Server/Services/DockerHostService.js +104 -0
  246. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  247. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  248. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  249. package/build/dist/Server/Services/HostService.js +114 -2
  250. package/build/dist/Server/Services/HostService.js.map +1 -1
  251. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  252. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  253. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  254. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  255. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  256. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  257. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  258. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  259. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  260. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  261. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  262. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  263. package/build/dist/Server/Services/RunnerJobService.js +344 -5
  264. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  265. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  266. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  267. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  268. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  269. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  270. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1029 -30
  272. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
  274. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  276. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
  278. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  279. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  280. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  281. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  282. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  283. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  284. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  285. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  286. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  287. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
  288. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +50 -4
  290. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
  292. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  293. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
  294. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  295. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
  296. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  297. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  298. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  299. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  300. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  301. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  302. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  303. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  304. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  305. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  306. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  307. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  308. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  309. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  310. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  311. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
  312. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  313. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
  314. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  315. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  316. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  317. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +263 -110
  318. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  319. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  320. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  321. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  322. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  323. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  324. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  325. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  326. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  327. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  328. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  329. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  330. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  331. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  332. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  333. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  334. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  335. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
  336. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  337. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  338. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  339. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  340. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  341. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  342. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  343. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  344. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  345. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
  346. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  347. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  348. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  349. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  350. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  351. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
  352. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  353. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  354. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  355. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  356. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  357. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  358. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  359. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  360. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  361. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  362. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  363. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  364. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  365. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  366. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  367. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  368. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  369. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  370. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  371. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  372. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  373. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  374. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  375. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  376. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  377. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  378. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  379. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  380. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  381. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  382. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  383. package/package.json +1 -1
@@ -0,0 +1,1301 @@
1
+ /*
2
+ * The docker CLI grammar the docker-engine and docker-swarm command
3
+ * policies share (DockerEngineCommandPolicy, DockerSwarmCommandPolicy):
4
+ * how the docker CLI (docker 29.x, cobra + pflag) finds the command in an
5
+ * argv and parses its flags, the engine's read commands both profiles
6
+ * allow, the commands neither ever allows, and the value checks every
7
+ * command uses.
8
+ *
9
+ * The agent spawns the docker CLI with the argv this grammar read, so the
10
+ * grammar has to see the SAME command, flags and positionals docker will
11
+ * see — otherwise a denied flag or a second container could hide inside
12
+ * what we took for a flag value. The rules, which parseDockerFlags()
13
+ * mirrors exactly (pflag's parseLongArg / parseSingleShortArg):
14
+ *
15
+ * - The command is picked before any flag is read. docker's global flags
16
+ * (-H/--host, -c/--context, --config, --tls*, -D/--debug,
17
+ * -l/--log-level, -v/--version) exist ONLY before the command, and are
18
+ * exactly what would point the CLI at another engine or another
19
+ * credential store, so ANY word that starts with "-" before the
20
+ * command (and, for a command group such as `docker container`, before
21
+ * its subcommand) is refused. After the command, a global flag is just
22
+ * a flag that command does not have.
23
+ * - "--flag=value" carries its value inline. "--flag value" takes the next
24
+ * word ONLY when the flag takes a value — whatever that word looks like
25
+ * ("--since -f" is a since of "-f"). A switch never takes the next word:
26
+ * "--all true" is the switch plus a positional "true". A switch may be
27
+ * written "--all=false"; its value is read like Go's strconv.ParseBool.
28
+ * - "-abc" is a cluster of one-letter flags read letter by letter: each
29
+ * switch letter is followed by another letter, until a letter that
30
+ * takes a value, whose value is "=x" ("-n=5"), the rest of the word
31
+ * ("-n5") or, when nothing is left, the next word ("-n 5").
32
+ * - "--" ends the flags; every word after it is positional. So does the
33
+ * first positional for a command that stops reading flags there
34
+ * (docker top: everything after the container is a ps option).
35
+ * - Flag names are case-sensitive and never normalized ("--no_trunc" is
36
+ * not "--no-trunc"); a flag the command does not have is an error.
37
+ *
38
+ * On top of the CLI's own rules, fail-closed ones: a flag this grammar does
39
+ * not list for the command is Denied (unknown means we cannot know what it
40
+ * does, or which word it swallows); a flag written twice is Denied unless
41
+ * docker appends its values (--filter), since pflag keeps the LAST value
42
+ * and "--no-stream --no-stream=false" would stream; a value that must be a
43
+ * number, a duration or a name is checked against exactly that; and a
44
+ * container, image, network, volume, service, node or stack is only ever
45
+ * a word docker itself would accept as one ([A-Za-z0-9][A-Za-z0-9_.-]*),
46
+ * so a unicode dash, a quote or a template can never pose as one.
47
+ *
48
+ * `--format` is limited to "json" (and "table" for list commands): a Go
49
+ * template can print a container's environment unlabelled, where the
50
+ * output redactor could not recognise it.
51
+ *
52
+ * Part of the import-closed resource policy directory that the resource AI
53
+ * agent carries a byte-identical copy of: relative imports of that set
54
+ * only, and nothing from Node.
55
+ */
56
+ import { ResourceCommandTier } from "../../../Types/ResourceAiAgent/ResourceAiAccess";
57
+ import { MAX_COMMAND_LENGTH_CHARS } from "../../../Types/AutoRemediation/AiRemediationCommandPolicyVerdict";
58
+ import { MAX_RESOURCE_COMMAND_TOKENS, deniedResult, renderResourceDisplayCommand, } from "./ResourceCommandPolicyCore";
59
+ // ---- Flags ------------------------------------------------------------------
60
+ export var DockerFlagKind;
61
+ (function (DockerFlagKind) {
62
+ // A switch: never takes the next word ("-a", "--all", "--all=false").
63
+ DockerFlagKind["Bool"] = "Bool";
64
+ // Always takes a value: "-n 5", "-n5", "-n=5", "--last 5", "--last=5".
65
+ DockerFlagKind["Value"] = "Value";
66
+ })(DockerFlagKind || (DockerFlagKind = {}));
67
+ /*
68
+ * The long and short spellings of docker's global flags, which never follow
69
+ * the command. Used only to explain a refusal better.
70
+ */
71
+ const GLOBAL_LONG_FLAGS = new Set([
72
+ "host",
73
+ "context",
74
+ "config",
75
+ "tls",
76
+ "tlscacert",
77
+ "tlscert",
78
+ "tlskey",
79
+ "tlsverify",
80
+ "debug",
81
+ "log-level",
82
+ "version",
83
+ ]);
84
+ const GLOBAL_SHORT_FLAGS = new Set([
85
+ "H",
86
+ "c",
87
+ "D",
88
+ "l",
89
+ "v",
90
+ ]);
91
+ export const DOCKER_GLOBAL_FLAG_RULE = "docker's global flags (-H/--host, -c/--context, --config, --tls*, -D/--debug, -l/--log-level) are never allowed: the agent always talks to its own engine with its own settings";
92
+ // Go's strconv.ParseBool, which pflag reads a switch's "=value" with.
93
+ const GO_TRUE_WORDS = new Set([
94
+ "1",
95
+ "t",
96
+ "T",
97
+ "TRUE",
98
+ "true",
99
+ "True",
100
+ ]);
101
+ const GO_FALSE_WORDS = new Set([
102
+ "0",
103
+ "f",
104
+ "F",
105
+ "FALSE",
106
+ "false",
107
+ "False",
108
+ ]);
109
+ function parseGoBool(value) {
110
+ if (GO_TRUE_WORDS.has(value)) {
111
+ return true;
112
+ }
113
+ if (GO_FALSE_WORDS.has(value)) {
114
+ return false;
115
+ }
116
+ return null;
117
+ }
118
+ function findLongFlag(flags, name) {
119
+ return flags.find((flag) => {
120
+ return (flag.name === name ||
121
+ (Array.isArray(flag.aliases) && flag.aliases.includes(name)));
122
+ });
123
+ }
124
+ function findShortFlag(flags, letter) {
125
+ return flags.find((flag) => {
126
+ return flag.shorthand !== undefined && flag.shorthand === letter;
127
+ });
128
+ }
129
+ // "-a/--all, --no-trunc, -f/--filter KEY=VALUE" — the flags a command allows.
130
+ export function describeAllowedDockerFlags(flags) {
131
+ const allowed = flags
132
+ .filter((flag) => {
133
+ return !flag.refusal;
134
+ })
135
+ .map((flag) => {
136
+ const long = `--${flag.name}`;
137
+ const written = flag.shorthand
138
+ ? `-${flag.shorthand}/${long}`
139
+ : long;
140
+ return flag.kind === DockerFlagKind.Value ? `${written} VALUE` : written;
141
+ });
142
+ return allowed.length > 0 ? allowed.join(", ") : "none";
143
+ }
144
+ function describeUnknownFlag(data) {
145
+ const isGlobal = data.isShort
146
+ ? GLOBAL_SHORT_FLAGS.has(data.bareName)
147
+ : GLOBAL_LONG_FLAGS.has(data.bareName) || data.bareName.startsWith("tls");
148
+ const allowed = `flags ${data.command} allows: ${describeAllowedDockerFlags(data.flags)}`;
149
+ if (isGlobal) {
150
+ return `the ${data.shown} flag is not allowed: ${DOCKER_GLOBAL_FLAG_RULE} (${allowed})`;
151
+ }
152
+ return `the ${data.shown} flag is not one OneUptime AI may use with ${data.command} (${allowed})`;
153
+ }
154
+ /*
155
+ * Parse the words after the command path exactly the way pflag would (see
156
+ * the header). Stops at the first problem; a caller must refuse the
157
+ * command when `problem` is set.
158
+ */
159
+ export function parseDockerFlags(data) {
160
+ const words = data.words;
161
+ const positionals = [];
162
+ const values = new Map();
163
+ let flagsEnded = false;
164
+ const fail = (problem) => {
165
+ return { positionals, values, problem };
166
+ };
167
+ // Records one value; returns why it cannot be recorded, or null.
168
+ const record = (flag, rawValue, shown) => {
169
+ let value = rawValue;
170
+ if (flag.kind === DockerFlagKind.Bool) {
171
+ const parsed = parseGoBool(rawValue);
172
+ if (parsed === null) {
173
+ return `the ${shown} flag is a switch: write it alone (or =true / =false), not with "${rawValue}"`;
174
+ }
175
+ value = parsed ? "true" : "false";
176
+ }
177
+ const existing = values.get(flag.name);
178
+ if (existing && !flag.repeatable) {
179
+ return `the ${shown} flag is given more than once: docker keeps only the last value, so write it once`;
180
+ }
181
+ if (existing) {
182
+ existing.push(value);
183
+ }
184
+ else {
185
+ values.set(flag.name, [value]);
186
+ }
187
+ return null;
188
+ };
189
+ for (let index = 0; index < words.length; index++) {
190
+ const word = words[index] || "";
191
+ // pflag: "", "-" and anything not starting with "-" are positionals.
192
+ if (flagsEnded || word.length < 2 || !word.startsWith("-")) {
193
+ positionals.push(word);
194
+ if (!data.interspersed) {
195
+ flagsEnded = true;
196
+ }
197
+ continue;
198
+ }
199
+ if (word === "--") {
200
+ flagsEnded = true;
201
+ continue;
202
+ }
203
+ if (word.startsWith("--")) {
204
+ const body = word.slice(2);
205
+ if (body.startsWith("-") || body.startsWith("=")) {
206
+ return fail(`"${word}" is not valid flag syntax`);
207
+ }
208
+ const equals = body.indexOf("=");
209
+ const name = equals >= 0 ? body.slice(0, equals) : body;
210
+ const shown = `--${name}`;
211
+ const flag = findLongFlag(data.flags, name);
212
+ if (!flag) {
213
+ return fail(describeUnknownFlag({
214
+ shown,
215
+ bareName: name,
216
+ isShort: false,
217
+ command: data.command,
218
+ flags: data.flags,
219
+ }));
220
+ }
221
+ if (flag.refusal) {
222
+ return fail(`the ${shown} flag ${flag.refusal}`);
223
+ }
224
+ let value;
225
+ if (equals >= 0) {
226
+ value = body.slice(equals + 1);
227
+ }
228
+ else if (flag.kind === DockerFlagKind.Bool) {
229
+ value = "true";
230
+ }
231
+ else if (index + 1 < words.length) {
232
+ value = words[index + 1] || "";
233
+ index++;
234
+ }
235
+ else {
236
+ return fail(`the ${shown} flag needs a value`);
237
+ }
238
+ const problem = record(flag, value, shown);
239
+ if (problem) {
240
+ return fail(problem);
241
+ }
242
+ continue;
243
+ }
244
+ // "-abc": a cluster of one-letter flags, read letter by letter like pflag.
245
+ let rest = word.slice(1);
246
+ while (rest.length > 0) {
247
+ const letter = rest.charAt(0);
248
+ const shown = word === `-${letter}` || word.startsWith(`-${letter}=`)
249
+ ? `-${letter}`
250
+ : `-${letter} (in "${word}")`;
251
+ const flag = findShortFlag(data.flags, letter);
252
+ if (!flag) {
253
+ return fail(describeUnknownFlag({
254
+ shown,
255
+ bareName: letter,
256
+ isShort: true,
257
+ command: data.command,
258
+ flags: data.flags,
259
+ }));
260
+ }
261
+ if (flag.refusal) {
262
+ return fail(`the ${shown} flag ${flag.refusal}`);
263
+ }
264
+ let value;
265
+ if (rest.length > 2 && rest.charAt(1) === "=") {
266
+ // "-n=5" (and "-a=false")
267
+ value = rest.slice(2);
268
+ rest = "";
269
+ }
270
+ else if (flag.kind === DockerFlagKind.Bool) {
271
+ // "-aq": the next letter is another flag.
272
+ value = "true";
273
+ rest = rest.slice(1);
274
+ }
275
+ else if (rest.length > 1) {
276
+ // "-n5"
277
+ value = rest.slice(1);
278
+ rest = "";
279
+ }
280
+ else if (index + 1 < words.length) {
281
+ // "-n 5"
282
+ value = words[index + 1] || "";
283
+ index++;
284
+ rest = "";
285
+ }
286
+ else {
287
+ return fail(`the ${shown} flag needs a value`);
288
+ }
289
+ const problem = record(flag, value, shown);
290
+ if (problem) {
291
+ return fail(problem);
292
+ }
293
+ }
294
+ }
295
+ return { positionals, values };
296
+ }
297
+ // Every value a flag was given, by its long name.
298
+ export function dockerFlagValues(parsed, name) {
299
+ return parsed.values.get(name) || [];
300
+ }
301
+ // The flag's value (flags other than --filter are given at most once).
302
+ export function dockerFlagValue(parsed, name) {
303
+ const all = dockerFlagValues(parsed, name);
304
+ return all.length > 0 ? all[all.length - 1] : undefined;
305
+ }
306
+ export function isDockerFlagGiven(parsed, name) {
307
+ return dockerFlagValues(parsed, name).length > 0;
308
+ }
309
+ // A switch that is on: given, and not "=false".
310
+ export function isDockerSwitchOn(parsed, name) {
311
+ return dockerFlagValue(parsed, name) === "true";
312
+ }
313
+ // ---- Values -----------------------------------------------------------------
314
+ /*
315
+ * A container, network, volume, service, task, node or stack: a name docker
316
+ * accepts, or an id (or a hex prefix of one). ASCII only, so a unicode dash
317
+ * or a look-alike letter never passes for a name — or for a flag.
318
+ */
319
+ const DOCKER_OBJECT_NAME_REGEX = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,254}$/;
320
+ /*
321
+ * An image reference: [registry[:port]/]repository[:tag][@digest]. Checked
322
+ * loosely (docker checks the rest); never a flag, a space or a template.
323
+ */
324
+ const DOCKER_IMAGE_REFERENCE_REGEX = /^[A-Za-z0-9][A-Za-z0-9_.:/@+-]{0,254}$/;
325
+ export function isDockerObjectName(word) {
326
+ return typeof word === "string" && DOCKER_OBJECT_NAME_REGEX.test(word);
327
+ }
328
+ export function isDockerImageReference(word) {
329
+ return typeof word === "string" && DOCKER_IMAGE_REFERENCE_REGEX.test(word);
330
+ }
331
+ // Why one of these words is not a name of this kind, or null when all are.
332
+ export function findBadDockerName(words, kind, isValid = isDockerObjectName) {
333
+ for (const word of words) {
334
+ if (!isValid(word)) {
335
+ return `"${word}" is not a ${kind} name or id docker accepts (letters, digits, "_", "." and "-", starting with a letter or digit)`;
336
+ }
337
+ }
338
+ return null;
339
+ }
340
+ // Plain digits: no sign, no spaces, at most nine of them.
341
+ const DOCKER_COUNT_DIGITS_REGEX = /^[0-9]{1,9}$/;
342
+ // A whole number 0..max written in plain digits (no sign, no spaces), or null.
343
+ export function parseDockerCount(value, max) {
344
+ if (typeof value !== "string" || !DOCKER_COUNT_DIGITS_REGEX.test(value)) {
345
+ return null;
346
+ }
347
+ const count = Number(value);
348
+ return count <= max ? count : null;
349
+ }
350
+ // A Go duration, as docker reads --since 30m: "90s", "1h30m", "1.5h".
351
+ const GO_DURATION_REGEX = /^(?:[0-9]{1,9}(?:\.[0-9]{1,9})?(?:ns|us|ms|s|m|h)){1,6}$/;
352
+ // An RFC 3339 date or date-time docker's --since/--until accepts.
353
+ const DATE_TIME_REGEX = /^[0-9]{4}-[0-9]{2}-[0-9]{2}(?:T[0-9]{2}:[0-9]{2}(?::[0-9]{2}(?:\.[0-9]{1,9})?)?(?:Z|[+-][0-9]{2}:?[0-9]{2})?)?$/;
354
+ // A Unix timestamp in seconds (2001 onwards), optionally with a fraction.
355
+ const UNIX_TIME_REGEX = /^[0-9]{10}(?:\.[0-9]{1,9})?$/;
356
+ /*
357
+ * A --since / --until value: a relative duration ("30m", "2h"), an RFC 3339
358
+ * date or date-time, or a Unix timestamp. Never empty — docker reads an
359
+ * empty --since as no bound at all — and never "0" (the epoch).
360
+ */
361
+ export function isDockerTimeValue(value) {
362
+ if (typeof value !== "string" || !value || value.length > 64) {
363
+ return false;
364
+ }
365
+ return (GO_DURATION_REGEX.test(value) ||
366
+ DATE_TIME_REGEX.test(value) ||
367
+ UNIX_TIME_REGEX.test(value));
368
+ }
369
+ export function isGoDuration(value) {
370
+ return (typeof value === "string" &&
371
+ value.length <= 64 &&
372
+ (value === "0" || GO_DURATION_REGEX.test(value)));
373
+ }
374
+ const GO_DURATION_PART_REGEX = /([0-9]{1,9}(?:\.[0-9]{1,9})?)(ns|us|ms|s|m|h)/g;
375
+ const GO_DURATION_UNIT_SECONDS = {
376
+ ns: 1e-9,
377
+ us: 1e-6,
378
+ ms: 1e-3,
379
+ s: 1,
380
+ m: 60,
381
+ h: 3600,
382
+ };
383
+ /*
384
+ * A relative --since / --until value ("30m", "1h30m", "0s") in seconds, or
385
+ * null when the value is not one (an RFC 3339 time, a Unix timestamp). docker
386
+ * reads it as that long before now, so it always points into the past.
387
+ */
388
+ export function parseDockerRelativeTimeSeconds(value) {
389
+ if (typeof value !== "string" || !isDockerTimeValue(value)) {
390
+ return null;
391
+ }
392
+ if (!GO_DURATION_REGEX.test(value)) {
393
+ return null;
394
+ }
395
+ let seconds = 0;
396
+ GO_DURATION_PART_REGEX.lastIndex = 0;
397
+ let part = GO_DURATION_PART_REGEX.exec(value);
398
+ while (part) {
399
+ seconds +=
400
+ Number(part[1] || "0") * (GO_DURATION_UNIT_SECONDS[part[2] || ""] || 0);
401
+ part = GO_DURATION_PART_REGEX.exec(value);
402
+ }
403
+ return seconds;
404
+ }
405
+ /*
406
+ * A memory size as docker reads --memory / --limit-memory (units.RAMInBytes):
407
+ * digits, an optional fraction, an optional k/m/g/t (with an optional "i")
408
+ * and an optional "b": "512m", "1g", "1.5GiB", "536870912".
409
+ */
410
+ const MEMORY_SIZE_REGEX = /^[0-9]{1,15}(?:\.[0-9]{1,6})?(?:[kKmMgGtT][iI]?)?[bB]?$/;
411
+ export function isDockerMemorySize(value) {
412
+ return typeof value === "string" && MEMORY_SIZE_REGEX.test(value);
413
+ }
414
+ // A number of CPUs as docker reads --cpus / --limit-cpu: "0.5", "2", "1.25".
415
+ const CPU_COUNT_REGEX = /^[0-9]{1,4}(?:\.[0-9]{1,3})?$/;
416
+ export function isDockerCpuCount(value) {
417
+ return typeof value === "string" && CPU_COUNT_REGEX.test(value);
418
+ }
419
+ // "--filter KEY=VALUE", as docker's filter parser requires.
420
+ const DOCKER_FILTER_REGEX = /^[A-Za-z][A-Za-z0-9_.-]{0,63}=/;
421
+ // Why a --filter value is unusable, or null when every one is KEY=VALUE.
422
+ export function findBadDockerFilter(parsed) {
423
+ for (const filter of dockerFlagValues(parsed, "filter")) {
424
+ if (!DOCKER_FILTER_REGEX.test(filter) || filter.length > 256) {
425
+ return `--filter takes KEY=VALUE (e.g. --filter status=exited or --filter name=web), not "${filter}"`;
426
+ }
427
+ }
428
+ return null;
429
+ }
430
+ // Why --format is unusable, or null when it is absent or one of `allowed`.
431
+ export function findBadDockerFormat(parsed, allowed) {
432
+ const format = dockerFlagValue(parsed, "format");
433
+ if (format === undefined || allowed.includes(format)) {
434
+ return null;
435
+ }
436
+ const choices = allowed
437
+ .map((choice) => {
438
+ return `"${choice}"`;
439
+ })
440
+ .join(" or ");
441
+ return `--format may only be ${choices} (or left out), not "${format}": Go templates are not allowed, since one can print a container's environment unlabelled`;
442
+ }
443
+ // ---- Command paths ----------------------------------------------------------
444
+ /*
445
+ * The command groups whose subcommands this grammar reads one by one, with
446
+ * every alias docker accepts for a subcommand mapped to its canonical name
447
+ * (`docker container list` and `docker container ps` are `container ls`).
448
+ * A subcommand not listed is looked up as written and refused.
449
+ */
450
+ const SUBCOMMAND_GROUPS = new Map([
451
+ [
452
+ "container",
453
+ new Map([
454
+ ["ls", "ls"],
455
+ ["list", "ls"],
456
+ ["ps", "ls"],
457
+ ["inspect", "inspect"],
458
+ ["logs", "logs"],
459
+ ["stats", "stats"],
460
+ ["top", "top"],
461
+ ["port", "port"],
462
+ ["diff", "diff"],
463
+ ["restart", "restart"],
464
+ ["start", "start"],
465
+ ["unpause", "unpause"],
466
+ ["stop", "stop"],
467
+ ["kill", "kill"],
468
+ ["pause", "pause"],
469
+ ["update", "update"],
470
+ ["rm", "rm"],
471
+ ["remove", "rm"],
472
+ ]),
473
+ ],
474
+ [
475
+ "image",
476
+ new Map([
477
+ ["ls", "ls"],
478
+ ["list", "ls"],
479
+ ["inspect", "inspect"],
480
+ ["rm", "rm"],
481
+ ["remove", "rm"],
482
+ ["rmi", "rm"],
483
+ ]),
484
+ ],
485
+ [
486
+ "network",
487
+ new Map([
488
+ ["ls", "ls"],
489
+ ["list", "ls"],
490
+ ["inspect", "inspect"],
491
+ ["rm", "rm"],
492
+ ["remove", "rm"],
493
+ ]),
494
+ ],
495
+ [
496
+ "volume",
497
+ new Map([
498
+ ["ls", "ls"],
499
+ ["list", "ls"],
500
+ ["inspect", "inspect"],
501
+ ["rm", "rm"],
502
+ ["remove", "rm"],
503
+ ]),
504
+ ],
505
+ [
506
+ "system",
507
+ new Map([
508
+ ["df", "df"],
509
+ ["info", "info"],
510
+ ["events", "events"],
511
+ ]),
512
+ ],
513
+ [
514
+ "service",
515
+ new Map([
516
+ ["ls", "ls"],
517
+ ["list", "ls"],
518
+ ["ps", "ps"],
519
+ ["inspect", "inspect"],
520
+ ["logs", "logs"],
521
+ ["update", "update"],
522
+ ["rollback", "rollback"],
523
+ ["scale", "scale"],
524
+ ["rm", "rm"],
525
+ ["remove", "rm"],
526
+ ]),
527
+ ],
528
+ [
529
+ "node",
530
+ new Map([
531
+ ["ls", "ls"],
532
+ ["list", "ls"],
533
+ ["ps", "ps"],
534
+ ["inspect", "inspect"],
535
+ ["update", "update"],
536
+ ["rm", "rm"],
537
+ ["remove", "rm"],
538
+ ]),
539
+ ],
540
+ [
541
+ "stack",
542
+ new Map([
543
+ ["ls", "ls"],
544
+ ["list", "ls"],
545
+ ["ps", "ps"],
546
+ ["services", "services"],
547
+ ["deploy", "deploy"],
548
+ ["up", "deploy"],
549
+ ["rm", "rm"],
550
+ ["remove", "rm"],
551
+ ["down", "rm"],
552
+ ]),
553
+ ],
554
+ ]);
555
+ /*
556
+ * Find the command in an argv whose first word is "docker": the word after
557
+ * it, and for a command group the word after that. Refuses a flag in front
558
+ * of either (see the header: that is where docker's global flags live).
559
+ */
560
+ export function resolveDockerCommandPath(words) {
561
+ const first = words[1];
562
+ if (first === undefined) {
563
+ return {
564
+ path: "",
565
+ written: "",
566
+ rest: [],
567
+ problem: 'a docker command must name what to run after "docker" (e.g. docker ps)',
568
+ };
569
+ }
570
+ if (first.startsWith("-") && first.length > 1) {
571
+ return {
572
+ path: "",
573
+ written: first,
574
+ rest: words.slice(2),
575
+ problem: `"${first}" comes before the docker command: ${DOCKER_GLOBAL_FLAG_RULE}. Write the command right after "docker" (e.g. docker ps)`,
576
+ };
577
+ }
578
+ const group = SUBCOMMAND_GROUPS.get(first);
579
+ if (!group) {
580
+ return { path: first, written: first, rest: words.slice(2) };
581
+ }
582
+ const second = words[2];
583
+ if (second === undefined) {
584
+ return {
585
+ path: first,
586
+ group: first,
587
+ written: first,
588
+ rest: [],
589
+ problem: `docker ${first} needs a subcommand (e.g. docker ${first} ls)`,
590
+ };
591
+ }
592
+ if (second.startsWith("-") && second.length > 1) {
593
+ return {
594
+ path: first,
595
+ group: first,
596
+ written: `${first} ${second}`,
597
+ rest: words.slice(3),
598
+ problem: `"${second}" comes before the docker ${first} subcommand: write the subcommand right after "docker ${first}" and its flags after it (e.g. docker ${first} ls)`,
599
+ };
600
+ }
601
+ return {
602
+ path: `${first} ${group.get(second) || second}`,
603
+ group: first,
604
+ written: `${first} ${second}`,
605
+ rest: words.slice(3),
606
+ };
607
+ }
608
+ export function readJudgement(reason) {
609
+ return { tier: ResourceCommandTier.Read, reason };
610
+ }
611
+ export function deniedJudgement(reason) {
612
+ return { tier: ResourceCommandTier.Denied, reason };
613
+ }
614
+ // The names a write lists, each once, in the order written.
615
+ export function uniqueDockerTargets(words) {
616
+ const seen = new Set();
617
+ const targets = [];
618
+ for (const word of words) {
619
+ if (!seen.has(word)) {
620
+ seen.add(word);
621
+ targets.push(word);
622
+ }
623
+ }
624
+ return targets;
625
+ }
626
+ /*
627
+ * Commands neither profile ever runs, with why (the tail after "it "): by
628
+ * canonical path, and by group for a group no subcommand of which is
629
+ * allowed. A profile's own refusals come first.
630
+ */
631
+ export const DOCKER_COMMAND_REFUSALS = new Map([
632
+ ["exec", "runs a program inside a container"],
633
+ ["container exec", "runs a program inside a container"],
634
+ [
635
+ "run",
636
+ "creates and starts a new container, with any image, mounts and privileges",
637
+ ],
638
+ [
639
+ "container run",
640
+ "creates and starts a new container, with any image, mounts and privileges",
641
+ ],
642
+ ["create", "creates a new container, with any image, mounts and privileges"],
643
+ [
644
+ "container create",
645
+ "creates a new container, with any image, mounts and privileges",
646
+ ],
647
+ ["cp", "copies files into or out of a container"],
648
+ ["container cp", "copies files into or out of a container"],
649
+ ["attach", "attaches to a container's terminal and input"],
650
+ ["container attach", "attaches to a container's terminal and input"],
651
+ ["rm", "deletes containers"],
652
+ ["container rm", "deletes containers"],
653
+ ["rmi", "deletes images"],
654
+ ["image rm", "deletes images"],
655
+ ["container prune", "deletes every stopped container"],
656
+ ["image prune", "deletes images in bulk"],
657
+ ["network prune", "deletes every unused network"],
658
+ ["volume prune", "deletes volumes and the data in them"],
659
+ ["system prune", "deletes containers, networks, images and caches in bulk"],
660
+ ["build", "builds images from files on the host"],
661
+ ["image build", "builds images from files on the host"],
662
+ ["buildx", "builds images or manages the build cache"],
663
+ ["builder", "builds images or manages the build cache"],
664
+ ["bake", "builds images from files on the host"],
665
+ ["push", "uploads images to a registry"],
666
+ ["image push", "uploads images to a registry"],
667
+ ["pull", "downloads images from a registry"],
668
+ ["image pull", "downloads images from a registry"],
669
+ ["login", "handles registry credentials"],
670
+ ["logout", "handles registry credentials"],
671
+ ["save", "copies images out of the engine"],
672
+ ["image save", "copies images out of the engine"],
673
+ ["load", "loads images into the engine"],
674
+ ["image load", "loads images into the engine"],
675
+ ["export", "copies a container's filesystem out of the engine"],
676
+ ["container export", "copies a container's filesystem out of the engine"],
677
+ ["import", "creates an image from a filesystem archive"],
678
+ ["image import", "creates an image from a filesystem archive"],
679
+ ["commit", "turns a container's filesystem into a new image"],
680
+ ["container commit", "turns a container's filesystem into a new image"],
681
+ ["rename", "renames a container, which breaks whatever finds it by name"],
682
+ [
683
+ "container rename",
684
+ "renames a container, which breaks whatever finds it by name",
685
+ ],
686
+ ["wait", "blocks until a container stops"],
687
+ ["container wait", "blocks until a container stops"],
688
+ [
689
+ "history",
690
+ "prints an image's build steps, which can carry build arguments with secrets",
691
+ ],
692
+ [
693
+ "image history",
694
+ "prints an image's build steps, which can carry build arguments with secrets",
695
+ ],
696
+ ["tag", "retags images"],
697
+ ["image tag", "retags images"],
698
+ ["search", "queries a registry"],
699
+ [
700
+ "plugin",
701
+ "installs, enables or reconfigures engine plugins, which run with full privileges",
702
+ ],
703
+ ["secret", "reads or changes swarm secrets"],
704
+ ["config", "reads or changes swarm configs, whose contents it prints"],
705
+ ["swarm", "changes swarm membership, join tokens or unlock keys"],
706
+ ["context", "switches the engine the docker CLI talks to"],
707
+ ["trust", "signs or revokes image trust data"],
708
+ ["manifest", "creates or pushes image manifests"],
709
+ [
710
+ "compose",
711
+ "is not shipped with the agent (it creates, recreates and removes containers from a project file)",
712
+ ],
713
+ ["checkpoint", "checkpoints or restores containers"],
714
+ ["network create", "changes container networking"],
715
+ ["network rm", "changes container networking"],
716
+ ["network connect", "changes container networking"],
717
+ ["network disconnect", "changes container networking"],
718
+ ["volume create", "creates or changes volumes"],
719
+ ["volume update", "creates or changes volumes"],
720
+ ["volume rm", "deletes volumes and the data in them"],
721
+ ["system dial-stdio", "opens a raw connection to the engine's API"],
722
+ [
723
+ "service create",
724
+ "creates a new service, with any image, mounts and privileges",
725
+ ],
726
+ ["service rm", "deletes a service and all of its tasks"],
727
+ ["stack deploy", "creates or replaces a whole stack from a compose file"],
728
+ ["stack rm", "deletes a whole stack"],
729
+ ["stack config", "renders a stack's compose files, environment included"],
730
+ ["node rm", "removes a node from the swarm"],
731
+ ["node promote", "changes which nodes manage the swarm (its Raft quorum)"],
732
+ ["node demote", "changes which nodes manage the swarm (its Raft quorum)"],
733
+ ]);
734
+ /*
735
+ * Evaluate one argv (program included) against a profile. Total — any
736
+ * input, any shape — and fail-closed: a command, subcommand, flag or value
737
+ * the profile does not model is Denied with a reason that says what IS
738
+ * allowed.
739
+ */
740
+ export function evaluateDockerArgv(profile, argv) {
741
+ const words = Array.isArray(argv)
742
+ ? argv.filter((word) => {
743
+ return typeof word === "string";
744
+ })
745
+ : [];
746
+ if (!Array.isArray(argv) || words.length === 0) {
747
+ return deniedResult(words, "Empty command.");
748
+ }
749
+ if (words.length !== argv.length) {
750
+ return deniedResult(words, "Every word of the command must be a string.");
751
+ }
752
+ if (words[0] !== "docker") {
753
+ return deniedResult(words, `the ${profile.name} command policy covers the docker CLI only: start the command with "docker"`);
754
+ }
755
+ // The dispatcher's bounds, again: this policy never relies on a caller.
756
+ if (words.length > MAX_RESOURCE_COMMAND_TOKENS) {
757
+ return deniedResult(words, `A command may have at most ${MAX_RESOURCE_COMMAND_TOKENS} words.`);
758
+ }
759
+ if (words.join(" ").length > MAX_COMMAND_LENGTH_CHARS) {
760
+ return deniedResult(words, `Command exceeds the ${MAX_COMMAND_LENGTH_CHARS}-character limit.`);
761
+ }
762
+ const deny = (reason, verb) => {
763
+ return Object.assign(Object.assign({}, deniedResult(words, reason)), { verb });
764
+ };
765
+ const resolved = resolveDockerCommandPath(words);
766
+ const spec = resolved.problem
767
+ ? undefined
768
+ : profile.commands.get(resolved.path);
769
+ if (!spec) {
770
+ const refusal = resolved.path
771
+ ? profile.refusals.get(resolved.path) ||
772
+ (resolved.group ? profile.refusals.get(resolved.group) : undefined) ||
773
+ DOCKER_COMMAND_REFUSALS.get(resolved.path) ||
774
+ (resolved.group
775
+ ? DOCKER_COMMAND_REFUSALS.get(resolved.group)
776
+ : undefined)
777
+ : undefined;
778
+ let reason;
779
+ if (refusal) {
780
+ // With a problem, only the group was read: name the group alone.
781
+ reason = `docker ${resolved.problem ? resolved.path : resolved.written} is never allowed for ${profile.resourceDescription}: it ${refusal}`;
782
+ }
783
+ else if (resolved.problem) {
784
+ reason = resolved.problem;
785
+ }
786
+ else {
787
+ const lowered = resolved.written.toLowerCase();
788
+ const hint = lowered !== resolved.written &&
789
+ (profile.commands.has(lowered) ||
790
+ profile.commands.has(resolved.path.toLowerCase()))
791
+ ? " (docker commands are lowercase)"
792
+ : "";
793
+ reason = `docker ${resolved.written} is not a docker command OneUptime AI may run${hint}`;
794
+ }
795
+ return deny(`${reason}. ${profile.allowedSummary}`, "");
796
+ }
797
+ const command = `docker ${resolved.written}`;
798
+ const parsed = parseDockerFlags({
799
+ words: resolved.rest,
800
+ flags: spec.flags,
801
+ command,
802
+ interspersed: spec.interspersed !== false,
803
+ });
804
+ if (parsed.problem) {
805
+ return deny(`${command}: ${parsed.problem}`, spec.verb);
806
+ }
807
+ const judgement = spec.judge(parsed, command);
808
+ const isWrite = judgement.tier === ResourceCommandTier.SafeWrite ||
809
+ judgement.tier === ResourceCommandTier.RiskyWrite;
810
+ // Denied, and anything that is not a tier at all, never runs.
811
+ if (!isWrite && judgement.tier !== ResourceCommandTier.Read) {
812
+ return deny(`${command}: ${judgement.reason}`, spec.verb);
813
+ }
814
+ const result = {
815
+ tier: judgement.tier,
816
+ reason: judgement.reason,
817
+ program: "docker",
818
+ args: words.slice(1),
819
+ verb: spec.verb,
820
+ displayCommand: renderResourceDisplayCommand(words),
821
+ targets: isWrite ? uniqueDockerTargets(judgement.targets || []) : [],
822
+ };
823
+ if (isWrite && judgement.requiresHuman === true) {
824
+ result.requiresHuman = true;
825
+ }
826
+ return result;
827
+ }
828
+ // A total wrapper: a bug in a judge is a Denied, never an exception.
829
+ export function evaluateDockerArgvSafely(profile, argv) {
830
+ try {
831
+ return evaluateDockerArgv(profile, argv);
832
+ }
833
+ catch (_a) {
834
+ const words = Array.isArray(argv)
835
+ ? argv.filter((word) => {
836
+ return typeof word === "string";
837
+ })
838
+ : [];
839
+ return deniedResult(words, `the ${profile.name} command policy could not evaluate this command`);
840
+ }
841
+ }
842
+ // ---- The engine's read commands (both profiles) -----------------------------
843
+ const FILTER_FLAG = {
844
+ name: "filter",
845
+ shorthand: "f",
846
+ kind: DockerFlagKind.Value,
847
+ repeatable: true,
848
+ };
849
+ const FORMAT_FLAG = {
850
+ name: "format",
851
+ kind: DockerFlagKind.Value,
852
+ };
853
+ // inspect, info and version spell --format with -f.
854
+ const FORMAT_FLAG_WITH_SHORTHAND = {
855
+ name: "format",
856
+ shorthand: "f",
857
+ kind: DockerFlagKind.Value,
858
+ };
859
+ const QUIET_FLAG = {
860
+ name: "quiet",
861
+ shorthand: "q",
862
+ kind: DockerFlagKind.Bool,
863
+ };
864
+ const NO_TRUNC_FLAG = {
865
+ name: "no-trunc",
866
+ kind: DockerFlagKind.Bool,
867
+ };
868
+ export const DOCKER_FILTER_FLAG = FILTER_FLAG;
869
+ export const DOCKER_FORMAT_FLAG = FORMAT_FLAG;
870
+ export const DOCKER_FORMAT_FLAG_WITH_SHORTHAND = FORMAT_FLAG_WITH_SHORTHAND;
871
+ export const DOCKER_QUIET_FLAG = QUIET_FLAG;
872
+ export const DOCKER_NO_TRUNC_FLAG = NO_TRUNC_FLAG;
873
+ export const DOCKER_FOLLOW_REFUSAL = "is never allowed: it streams the log until the command is killed. Read a bounded slice with --tail N (N up to 2000) or --since 30m instead";
874
+ // The most log lines one command may ask for.
875
+ export const DOCKER_MAX_LOG_TAIL_LINES = 2000;
876
+ /*
877
+ * How far back --since alone (no --tail) may reach, in hours. Without
878
+ * --tail, --since is the only bound on what a logs command streams: an old
879
+ * --since (2001-01-01, 100000h) reads the whole log history, which the
880
+ * daemon and the CLI keep producing until the command ends or is killed.
881
+ */
882
+ export const DOCKER_MAX_LOG_SINCE_HOURS = 24;
883
+ // The flags `docker logs` and `docker service logs` share.
884
+ export const DOCKER_LOG_FLAGS = [
885
+ { name: "details", kind: DockerFlagKind.Bool },
886
+ {
887
+ name: "follow",
888
+ shorthand: "f",
889
+ kind: DockerFlagKind.Bool,
890
+ refusal: DOCKER_FOLLOW_REFUSAL,
891
+ },
892
+ { name: "since", kind: DockerFlagKind.Value },
893
+ { name: "tail", shorthand: "n", kind: DockerFlagKind.Value },
894
+ { name: "timestamps", shorthand: "t", kind: DockerFlagKind.Bool },
895
+ ];
896
+ /*
897
+ * Why a logs command does not read a bounded slice, or null when it does:
898
+ * --tail N with N up to DOCKER_MAX_LOG_TAIL_LINES, or — without --tail — a
899
+ * relative --since of at most DOCKER_MAX_LOG_SINCE_HOURS (an absolute time
900
+ * can lie any distance back, so it needs --tail too); --until (for docker
901
+ * logs) a time too. Relative, so the verdict is the same whenever the
902
+ * command is checked (at enqueue, and again on the agent).
903
+ */
904
+ export function findUnboundedLogProblem(parsed, command) {
905
+ const tail = dockerFlagValue(parsed, "tail");
906
+ const since = dockerFlagValue(parsed, "since");
907
+ const until = dockerFlagValue(parsed, "until");
908
+ if (tail !== undefined &&
909
+ parseDockerCount(tail, DOCKER_MAX_LOG_TAIL_LINES) === null) {
910
+ return `--tail takes a number of lines from 0 to ${DOCKER_MAX_LOG_TAIL_LINES}, not "${tail}"`;
911
+ }
912
+ if (since !== undefined && !isDockerTimeValue(since)) {
913
+ return `--since takes a duration (30m, 2h), an RFC 3339 time or a Unix timestamp, not "${since}"`;
914
+ }
915
+ if (until !== undefined && !isDockerTimeValue(until)) {
916
+ return `--until takes a duration (5m), an RFC 3339 time or a Unix timestamp, not "${until}"`;
917
+ }
918
+ if (tail === undefined && since === undefined) {
919
+ return `${command} needs --tail N (N up to ${DOCKER_MAX_LOG_TAIL_LINES}) or --since (e.g. --since 30m), so it reads a bounded slice of the log`;
920
+ }
921
+ if (tail === undefined && since !== undefined) {
922
+ const sinceSeconds = parseDockerRelativeTimeSeconds(since);
923
+ if (sinceSeconds === null) {
924
+ return `--since ${since} is an absolute time, which alone does not bound how much of the log ${command} reads: add --tail N (N up to ${DOCKER_MAX_LOG_TAIL_LINES}), or write --since as a duration of at most ${DOCKER_MAX_LOG_SINCE_HOURS}h (e.g. --since 30m)`;
925
+ }
926
+ if (sinceSeconds > DOCKER_MAX_LOG_SINCE_HOURS * 3600) {
927
+ return `--since ${since} reaches further back than ${DOCKER_MAX_LOG_SINCE_HOURS}h, which alone does not bound how much of the log ${command} reads: add --tail N (N up to ${DOCKER_MAX_LOG_TAIL_LINES}), or use a shorter --since`;
928
+ }
929
+ }
930
+ return null;
931
+ }
932
+ function judgeContainerList(parsed) {
933
+ if (parsed.positionals.length > 0) {
934
+ return deniedJudgement(`it takes no container names (got "${parsed.positionals[0]}"); narrow it with --filter name=NAME instead`);
935
+ }
936
+ const last = dockerFlagValue(parsed, "last");
937
+ if (last !== undefined && parseDockerCount(last, 100000) === null) {
938
+ return deniedJudgement(`-n/--last takes a number, not "${last}"`);
939
+ }
940
+ const problem = findBadDockerFilter(parsed) ||
941
+ findBadDockerFormat(parsed, ["json", "table"]);
942
+ return problem
943
+ ? deniedJudgement(problem)
944
+ : readJudgement("lists containers; it changes nothing");
945
+ }
946
+ const CONTAINER_LIST_SPEC = (verb) => {
947
+ return {
948
+ verb,
949
+ flags: [
950
+ { name: "all", shorthand: "a", kind: DockerFlagKind.Bool },
951
+ FILTER_FLAG,
952
+ FORMAT_FLAG,
953
+ { name: "last", shorthand: "n", kind: DockerFlagKind.Value },
954
+ { name: "latest", shorthand: "l", kind: DockerFlagKind.Bool },
955
+ NO_TRUNC_FLAG,
956
+ QUIET_FLAG,
957
+ { name: "size", shorthand: "s", kind: DockerFlagKind.Bool },
958
+ ],
959
+ judge: judgeContainerList,
960
+ };
961
+ };
962
+ // The object kinds `docker inspect --type` may name.
963
+ export const DOCKER_INSPECT_TYPES = [
964
+ "container",
965
+ "image",
966
+ "network",
967
+ "volume",
968
+ ];
969
+ function judgeInspectOf(kind, parsed) {
970
+ if (parsed.positionals.length === 0) {
971
+ return deniedJudgement(`it needs at least one ${kind} name or id`);
972
+ }
973
+ const problem = findBadDockerName(parsed.positionals, kind, kind === "image" ? isDockerImageReference : isDockerObjectName) || findBadDockerFormat(parsed, ["json"]);
974
+ return problem
975
+ ? deniedJudgement(problem)
976
+ : readJudgement(`shows each ${kind}'s configuration and state (environment values are redacted); it changes nothing`);
977
+ }
978
+ const INSPECT_SPEC = {
979
+ verb: "inspect",
980
+ flags: [
981
+ FORMAT_FLAG_WITH_SHORTHAND,
982
+ { name: "size", shorthand: "s", kind: DockerFlagKind.Bool },
983
+ { name: "type", kind: DockerFlagKind.Value },
984
+ ],
985
+ judge(parsed) {
986
+ const type = dockerFlagValue(parsed, "type");
987
+ if (type === undefined || !DOCKER_INSPECT_TYPES.includes(type)) {
988
+ return deniedJudgement(`it needs --type ${DOCKER_INSPECT_TYPES.join("|")}${type === undefined ? "" : ` (not "${type}")`}: without it docker also matches swarm configs and secrets by name, and prints a config's contents. Or use docker container inspect NAME`);
989
+ }
990
+ return judgeInspectOf(type, parsed);
991
+ },
992
+ };
993
+ const inspectSpecFor = (kind, extraFlags) => {
994
+ return {
995
+ verb: `${kind} inspect`,
996
+ flags: [FORMAT_FLAG_WITH_SHORTHAND, ...extraFlags],
997
+ judge(parsed) {
998
+ return judgeInspectOf(kind, parsed);
999
+ },
1000
+ };
1001
+ };
1002
+ const LOGS_SPEC = (verb) => {
1003
+ return {
1004
+ verb,
1005
+ flags: [...DOCKER_LOG_FLAGS, { name: "until", kind: DockerFlagKind.Value }],
1006
+ judge(parsed, command) {
1007
+ if (parsed.positionals.length !== 1) {
1008
+ return deniedJudgement(`it reads exactly one container's log (got ${parsed.positionals.length} names)`);
1009
+ }
1010
+ const problem = findBadDockerName(parsed.positionals, "container") ||
1011
+ findUnboundedLogProblem(parsed, command);
1012
+ return problem
1013
+ ? deniedJudgement(problem)
1014
+ : readJudgement("reads a bounded slice of one container's log; it changes nothing");
1015
+ },
1016
+ };
1017
+ };
1018
+ const STATS_SPEC = (verb) => {
1019
+ return {
1020
+ verb,
1021
+ flags: [
1022
+ { name: "all", shorthand: "a", kind: DockerFlagKind.Bool },
1023
+ FORMAT_FLAG,
1024
+ { name: "no-stream", kind: DockerFlagKind.Bool },
1025
+ NO_TRUNC_FLAG,
1026
+ ],
1027
+ judge(parsed) {
1028
+ if (!isDockerSwitchOn(parsed, "no-stream")) {
1029
+ return deniedJudgement("it needs --no-stream: without it docker stats streams until the command is killed");
1030
+ }
1031
+ const problem = findBadDockerName(parsed.positionals, "container") ||
1032
+ findBadDockerFormat(parsed, ["json", "table"]);
1033
+ return problem
1034
+ ? deniedJudgement(problem)
1035
+ : readJudgement("takes one snapshot of container resource usage; it changes nothing");
1036
+ },
1037
+ };
1038
+ };
1039
+ const TOP_SPEC = (verb) => {
1040
+ return {
1041
+ verb,
1042
+ flags: [],
1043
+ // docker top CONTAINER [ps OPTIONS]: every word after the container goes to ps.
1044
+ interspersed: false,
1045
+ judge(parsed) {
1046
+ if (parsed.positionals.length !== 1) {
1047
+ return deniedJudgement("it takes exactly one container and no ps options (write docker top NAME)");
1048
+ }
1049
+ const problem = findBadDockerName(parsed.positionals, "container");
1050
+ return problem
1051
+ ? deniedJudgement(problem)
1052
+ : readJudgement("lists the processes running in one container; it changes nothing");
1053
+ },
1054
+ };
1055
+ };
1056
+ const EVENTS_SPEC = (verb) => {
1057
+ return {
1058
+ verb,
1059
+ flags: [
1060
+ FILTER_FLAG,
1061
+ FORMAT_FLAG,
1062
+ { name: "since", kind: DockerFlagKind.Value },
1063
+ { name: "until", kind: DockerFlagKind.Value },
1064
+ ],
1065
+ judge(parsed) {
1066
+ if (parsed.positionals.length > 0) {
1067
+ return deniedJudgement(`it takes no names (got "${parsed.positionals[0]}"); narrow it with --filter KEY=VALUE`);
1068
+ }
1069
+ const since = dockerFlagValue(parsed, "since");
1070
+ const until = dockerFlagValue(parsed, "until");
1071
+ if (since === undefined || until === undefined) {
1072
+ return deniedJudgement("it needs both --since and --until (e.g. --since 30m --until 0s): without --until docker events streams until the command is killed");
1073
+ }
1074
+ if (!isDockerTimeValue(since) || !isDockerTimeValue(until)) {
1075
+ return deniedJudgement(`--since and --until take a duration (30m, 0s), an RFC 3339 time or a Unix timestamp, not "${isDockerTimeValue(since) ? until : since}"`);
1076
+ }
1077
+ /*
1078
+ * docker reads a duration as that long before now, so a relative
1079
+ * --until always closes the window in the past; an absolute one can
1080
+ * lie in the future (2099-12-31, 9999999999), and docker events then
1081
+ * streams until the command is killed.
1082
+ */
1083
+ if (parseDockerRelativeTimeSeconds(until) === null) {
1084
+ return deniedJudgement(`--until must be a duration (e.g. --until 0s, or --until 30m for a window that closed 30 minutes ago), not the absolute time "${until}": an absolute --until can lie in the future, and docker events then streams until the command is killed`);
1085
+ }
1086
+ const problem = findBadDockerFilter(parsed) || findBadDockerFormat(parsed, ["json"]);
1087
+ return problem
1088
+ ? deniedJudgement(problem)
1089
+ : readJudgement("lists the engine's events in a closed time window; it changes nothing");
1090
+ },
1091
+ };
1092
+ };
1093
+ /*
1094
+ * A read command that takes no positionals: its flags, and the --format
1095
+ * values it accepts (--filter values are checked when it has --filter).
1096
+ */
1097
+ export const dockerNoArgumentReadSpec = (verb, reason, flags, formats) => {
1098
+ return {
1099
+ verb,
1100
+ flags,
1101
+ judge(parsed) {
1102
+ if (parsed.positionals.length > 0) {
1103
+ return deniedJudgement(`it takes no arguments (got "${parsed.positionals[0]}")`);
1104
+ }
1105
+ const problem = findBadDockerFilter(parsed) || findBadDockerFormat(parsed, formats);
1106
+ return problem ? deniedJudgement(problem) : readJudgement(reason);
1107
+ },
1108
+ };
1109
+ };
1110
+ const INFO_REASON = "shows the engine's configuration and health; it changes nothing";
1111
+ /*
1112
+ * docker system df [-v] [--format json|table]. -v with --format json is
1113
+ * refused: it prints every build-cache record's full Description — the
1114
+ * command line of each RUN step (`mount / from exec /bin/sh -c ...`), the
1115
+ * very build steps `docker history` is refused for. The verbose table
1116
+ * leaves Description out.
1117
+ */
1118
+ const SYSTEM_DF_SPEC = {
1119
+ verb: "system df",
1120
+ flags: [
1121
+ FORMAT_FLAG,
1122
+ { name: "verbose", shorthand: "v", kind: DockerFlagKind.Bool },
1123
+ ],
1124
+ judge(parsed) {
1125
+ if (parsed.positionals.length > 0) {
1126
+ return deniedJudgement(`it takes no arguments (got "${parsed.positionals[0]}")`);
1127
+ }
1128
+ const problem = findBadDockerFormat(parsed, [
1129
+ "json",
1130
+ "table",
1131
+ ]);
1132
+ if (problem) {
1133
+ return deniedJudgement(problem);
1134
+ }
1135
+ if (isDockerSwitchOn(parsed, "verbose") &&
1136
+ dockerFlagValue(parsed, "format") === "json") {
1137
+ return deniedJudgement("-v/--verbose with --format json is not allowed: it prints each build-cache record's Description, the command line of every RUN build step, which can carry secrets (the reason docker history is refused). Use docker system df -v (its table leaves Description out) or docker system df --format json");
1138
+ }
1139
+ return readJudgement("shows the engine's disk usage; it changes nothing");
1140
+ },
1141
+ };
1142
+ const IMAGES_SPEC = (verb) => {
1143
+ return {
1144
+ verb,
1145
+ flags: [
1146
+ { name: "all", shorthand: "a", kind: DockerFlagKind.Bool },
1147
+ { name: "digests", kind: DockerFlagKind.Bool },
1148
+ FILTER_FLAG,
1149
+ FORMAT_FLAG,
1150
+ NO_TRUNC_FLAG,
1151
+ QUIET_FLAG,
1152
+ ],
1153
+ judge(parsed) {
1154
+ if (parsed.positionals.length > 1) {
1155
+ return deniedJudgement("it takes at most one REPOSITORY[:TAG] to list");
1156
+ }
1157
+ const problem = findBadDockerName(parsed.positionals, "image", isDockerImageReference) ||
1158
+ findBadDockerFilter(parsed) ||
1159
+ findBadDockerFormat(parsed, ["json", "table"]);
1160
+ return problem
1161
+ ? deniedJudgement(problem)
1162
+ : readJudgement("lists images; it changes nothing");
1163
+ },
1164
+ };
1165
+ };
1166
+ // PORT[/PROTOCOL], as `docker port` reads it: 80, 80/tcp, 53/udp, 9/sctp.
1167
+ const DOCKER_PORT_ARGUMENT_REGEX = /^[0-9]{1,5}(?:\/(?:tcp|udp|sctp))?$/;
1168
+ const PORT_SPEC = (verb) => {
1169
+ return {
1170
+ verb,
1171
+ flags: [],
1172
+ judge(parsed) {
1173
+ const container = parsed.positionals[0];
1174
+ const port = parsed.positionals[1];
1175
+ if (container === undefined || parsed.positionals.length > 2) {
1176
+ return deniedJudgement("it takes one container and, optionally, one PORT[/PROTOCOL] (write docker port NAME 80/tcp)");
1177
+ }
1178
+ if (port !== undefined && !DOCKER_PORT_ARGUMENT_REGEX.test(port)) {
1179
+ return deniedJudgement(`"${port}" is not a PORT[/PROTOCOL] (e.g. 80 or 53/udp)`);
1180
+ }
1181
+ const problem = findBadDockerName([container], "container");
1182
+ return problem
1183
+ ? deniedJudgement(problem)
1184
+ : readJudgement("lists one container's published ports; it changes nothing");
1185
+ },
1186
+ };
1187
+ };
1188
+ const DIFF_SPEC = (verb) => {
1189
+ return {
1190
+ verb,
1191
+ flags: [],
1192
+ judge(parsed) {
1193
+ if (parsed.positionals.length !== 1) {
1194
+ return deniedJudgement("it takes exactly one container");
1195
+ }
1196
+ const problem = findBadDockerName(parsed.positionals, "container");
1197
+ return problem
1198
+ ? deniedJudgement(problem)
1199
+ : readJudgement("lists the files changed in one container's filesystem; it changes nothing");
1200
+ },
1201
+ };
1202
+ };
1203
+ /*
1204
+ * The engine's read commands, by canonical path. Both profiles allow all of
1205
+ * them (the swarm agent runs on a manager node, whose engine is a Docker
1206
+ * engine like any other).
1207
+ */
1208
+ export const DOCKER_ENGINE_READ_COMMANDS = new Map([
1209
+ ["ps", CONTAINER_LIST_SPEC("ps")],
1210
+ ["container ls", CONTAINER_LIST_SPEC("container ls")],
1211
+ ["inspect", INSPECT_SPEC],
1212
+ [
1213
+ "container inspect",
1214
+ inspectSpecFor("container", [
1215
+ { name: "size", shorthand: "s", kind: DockerFlagKind.Bool },
1216
+ ]),
1217
+ ],
1218
+ ["image inspect", inspectSpecFor("image", [])],
1219
+ [
1220
+ "network inspect",
1221
+ inspectSpecFor("network", [
1222
+ { name: "verbose", shorthand: "v", kind: DockerFlagKind.Bool },
1223
+ ]),
1224
+ ],
1225
+ ["volume inspect", inspectSpecFor("volume", [])],
1226
+ ["logs", LOGS_SPEC("logs")],
1227
+ ["container logs", LOGS_SPEC("container logs")],
1228
+ ["stats", STATS_SPEC("stats")],
1229
+ ["container stats", STATS_SPEC("container stats")],
1230
+ ["top", TOP_SPEC("top")],
1231
+ ["container top", TOP_SPEC("container top")],
1232
+ ["events", EVENTS_SPEC("events")],
1233
+ ["system events", EVENTS_SPEC("system events")],
1234
+ [
1235
+ "info",
1236
+ dockerNoArgumentReadSpec("info", INFO_REASON, [FORMAT_FLAG_WITH_SHORTHAND], ["json"]),
1237
+ ],
1238
+ [
1239
+ "system info",
1240
+ dockerNoArgumentReadSpec("system info", INFO_REASON, [FORMAT_FLAG_WITH_SHORTHAND], ["json"]),
1241
+ ],
1242
+ [
1243
+ "version",
1244
+ dockerNoArgumentReadSpec("version", "shows the docker client and engine versions; it changes nothing", [FORMAT_FLAG_WITH_SHORTHAND], ["json"]),
1245
+ ],
1246
+ ["system df", SYSTEM_DF_SPEC],
1247
+ ["images", IMAGES_SPEC("images")],
1248
+ ["image ls", IMAGES_SPEC("image ls")],
1249
+ [
1250
+ "network ls",
1251
+ dockerNoArgumentReadSpec("network ls", "lists networks; it changes nothing", [FILTER_FLAG, FORMAT_FLAG, NO_TRUNC_FLAG, QUIET_FLAG], ["json", "table"]),
1252
+ ],
1253
+ [
1254
+ "volume ls",
1255
+ dockerNoArgumentReadSpec("volume ls", "lists volumes; it changes nothing", [FILTER_FLAG, FORMAT_FLAG, QUIET_FLAG], ["json", "table"]),
1256
+ ],
1257
+ ["port", PORT_SPEC("port")],
1258
+ ["container port", PORT_SPEC("container port")],
1259
+ ["diff", DIFF_SPEC("diff")],
1260
+ ["container diff", DIFF_SPEC("container diff")],
1261
+ ]);
1262
+ /*
1263
+ * The engine's write commands, by canonical path — the docker-engine
1264
+ * profile allows them; the docker-swarm profile refuses every one (see
1265
+ * DockerSwarmCommandPolicy).
1266
+ */
1267
+ export const DOCKER_ENGINE_WRITE_PATHS = [
1268
+ "restart",
1269
+ "container restart",
1270
+ "start",
1271
+ "container start",
1272
+ "unpause",
1273
+ "container unpause",
1274
+ "stop",
1275
+ "container stop",
1276
+ "kill",
1277
+ "container kill",
1278
+ "pause",
1279
+ "container pause",
1280
+ "update",
1281
+ "container update",
1282
+ ];
1283
+ // The command groups only a swarm manager runs.
1284
+ export const DOCKER_SWARM_GROUPS = [
1285
+ "service",
1286
+ "node",
1287
+ "stack",
1288
+ ];
1289
+ // Merge command tables; a later table never overrides an earlier path.
1290
+ export function mergeDockerCommands(...tables) {
1291
+ const merged = new Map();
1292
+ for (const table of tables) {
1293
+ table.forEach((spec, path) => {
1294
+ if (!merged.has(path)) {
1295
+ merged.set(path, spec);
1296
+ }
1297
+ });
1298
+ }
1299
+ return merged;
1300
+ }
1301
+ //# sourceMappingURL=DockerCliGrammar.js.map