@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.4

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 (473) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/styles/global/_layout.scss +4 -0
  56. package/assets/translations/en-us.yaml +165 -45
  57. package/assets/translations/zh-hans.yaml +1 -7
  58. package/chart/monitoring/ClusterSelector.vue +0 -21
  59. package/chart/monitoring/index.vue +10 -1
  60. package/chart/monitoring/prometheus/index.vue +6 -3
  61. package/components/ActionDropdownShell.vue +2 -1
  62. package/components/CruResource.vue +161 -14
  63. package/components/CruResourceFooter.vue +9 -5
  64. package/components/ExplorerMembers.vue +8 -4
  65. package/components/ExplorerProjectsNamespaces.vue +11 -7
  66. package/components/GrowlManager.vue +4 -0
  67. package/components/InstallHelmCharts.vue +2 -2
  68. package/components/LandingPagePreference.vue +14 -5
  69. package/components/MgmtNodeList.vue +184 -0
  70. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +90 -1
  71. package/components/Resource/Detail/Card/StateCard/composables.ts +57 -87
  72. package/components/Resource/Detail/Card/StatusCard/__tests__/StatusCard.test.ts +61 -0
  73. package/components/Resource/Detail/Card/StatusCard/index.vue +61 -15
  74. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +17 -1
  75. package/components/Resource/Detail/Metadata/KeyValue.vue +5 -2
  76. package/components/Resource/Detail/Metadata/KeyValueRow.vue +2 -6
  77. package/components/Resource/Detail/Metadata/index.vue +6 -0
  78. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  79. package/components/Resource/Detail/SpacedRow.vue +3 -1
  80. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  81. package/components/ResourceDetail/index.vue +1 -1
  82. package/components/ResourceList/Masthead.vue +19 -9
  83. package/components/ResourceList/index.vue +82 -1
  84. package/components/RichTranslation.vue +5 -2
  85. package/components/SelectIconGrid.vue +0 -10
  86. package/components/Setting.vue +1 -0
  87. package/components/SingleClusterInfo.vue +1 -0
  88. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  89. package/components/SortableTable/index.vue +6 -9
  90. package/components/SortableTable/selection.js +23 -5
  91. package/components/SortableTable/sorting.js +6 -3
  92. package/components/SubtleLink.vue +31 -6
  93. package/components/Tabbed/Tab.vue +29 -3
  94. package/components/Tabbed/index.vue +25 -3
  95. package/components/TableOfContents/TableOfContents.vue +109 -0
  96. package/components/TableOfContents/composables.ts +258 -0
  97. package/components/Window/ContainerShell.vue +21 -11
  98. package/components/Window/__tests__/ContainerShell.test.ts +107 -37
  99. package/components/Wizard.vue +23 -17
  100. package/components/fleet/AppCoChartGrid.vue +401 -0
  101. package/components/fleet/AppCoEmptyState.vue +127 -0
  102. package/components/fleet/AppCoPageHeader.vue +119 -0
  103. package/components/fleet/AppCoVersionSelect.vue +70 -0
  104. package/components/fleet/FleetBundles.vue +100 -12
  105. package/components/fleet/FleetClusterTargets/ClusterSelectionFields.vue +217 -0
  106. package/components/fleet/FleetClusterTargets/TargetsList.vue +123 -35
  107. package/components/fleet/FleetClusterTargets/index.vue +226 -161
  108. package/components/fleet/FleetIntro.vue +7 -3
  109. package/components/fleet/FleetNoWorkspaces.vue +7 -3
  110. package/components/fleet/FleetSecretSelector.vue +5 -3
  111. package/components/fleet/FleetValuesFrom.vue +8 -2
  112. package/components/fleet/GitRepoTargetTab.vue +0 -2
  113. package/components/fleet/HelmOpAdvancedTab.vue +19 -53
  114. package/components/fleet/HelmOpAppCoConfigTab.vue +593 -0
  115. package/components/fleet/HelmOpAppCoResourcesSection.vue +162 -0
  116. package/components/fleet/HelmOpResourcesSection.vue +82 -0
  117. package/components/fleet/HelmOpTargetOptionsSection.vue +89 -0
  118. package/components/fleet/HelmOpTargetTab.vue +64 -60
  119. package/components/fleet/HelmOpValuesTab.vue +129 -105
  120. package/components/fleet/__tests__/AppCoEmptyState.test.ts +71 -0
  121. package/components/fleet/__tests__/AppCoVersionSelect.test.ts +36 -0
  122. package/components/fleet/__tests__/ClusterSelectionFields.test.ts +62 -0
  123. package/components/fleet/__tests__/FleetClusterTargets.test.ts +402 -115
  124. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  125. package/components/fleet/__tests__/FleetSecretSelector.test.ts +16 -0
  126. package/components/fleet/__tests__/FleetValuesFrom.test.ts +44 -0
  127. package/components/fleet/__tests__/HelmOpAppCoConfigTab.test.ts +59 -0
  128. package/components/fleet/__tests__/HelmOpAppCoResourcesSection.test.ts +62 -0
  129. package/components/fleet/__tests__/HelmOpResourcesSection.test.ts +43 -0
  130. package/components/fleet/__tests__/HelmOpTargetOptionsSection.test.ts +34 -0
  131. package/components/fleet/__tests__/HelmOpValuesTab.test.ts +39 -0
  132. package/components/fleet/__tests__/__snapshots__/AppCoEmptyState.test.ts.snap +97 -0
  133. package/components/fleet/__tests__/__snapshots__/AppCoVersionSelect.test.ts.snap +30 -0
  134. package/components/fleet/__tests__/__snapshots__/ClusterSelectionFields.test.ts.snap +209 -0
  135. package/components/fleet/__tests__/__snapshots__/HelmOpTargetOptionsSection.test.ts.snap +140 -0
  136. package/components/fleet/dashboard/Empty.vue +8 -4
  137. package/components/fleet/dashboard/ResourceCard.vue +28 -0
  138. package/components/fleet/dashboard/ResourceDetails.vue +28 -0
  139. package/components/fleet/dashboard/__tests__/ResourceCard.test.ts +87 -0
  140. package/components/form/ArrayList.vue +61 -4
  141. package/components/form/KeyValue.vue +23 -2
  142. package/components/form/LabeledSelect.vue +59 -4
  143. package/components/form/Labels.vue +22 -3
  144. package/components/form/NameNsDescription.vue +24 -5
  145. package/components/form/ResourceTabs/index.vue +1 -0
  146. package/components/form/Security.vue +6 -2
  147. package/components/form/WorkloadPorts.vue +2 -7
  148. package/components/form/__tests__/NameNsDescription.test.ts +75 -0
  149. package/components/form/__tests__/Security.test.ts +76 -0
  150. package/components/formatter/Autoscaler.vue +4 -4
  151. package/components/formatter/ClusterKubeVersion.vue +27 -0
  152. package/components/formatter/ClusterLink.vue +1 -7
  153. package/components/formatter/ClusterProvider.vue +6 -10
  154. package/components/formatter/FleetSummaryGraph.vue +0 -3
  155. package/components/formatter/InternalExternalIP.vue +10 -4
  156. package/components/formatter/MachineSummaryGraph.vue +1 -1
  157. package/components/formatter/PodsUsage.vue +2 -2
  158. package/components/formatter/ServiceTargets.vue +26 -7
  159. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  160. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  161. package/components/formatter/__tests__/InternalExternalIP.test.ts +132 -0
  162. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  163. package/components/formatter/__tests__/ServiceTargets.test.ts +412 -0
  164. package/components/nav/Header.vue +4 -0
  165. package/components/nav/NamespaceFilter.vue +2 -2
  166. package/components/nav/TopLevelMenu.helper.ts +15 -3
  167. package/components/nav/TopLevelMenu.vue +22 -6
  168. package/components/nav/__tests__/Header.test.ts +15 -0
  169. package/components/nav/__tests__/TopLevelMenu.test.ts +263 -21
  170. package/components/templates/default.vue +9 -4
  171. package/components/templates/home.vue +23 -0
  172. package/components/templates/plain.vue +23 -0
  173. package/components/templates/standalone.vue +17 -0
  174. package/composables/useFormValidation.ts +93 -0
  175. package/composables/useHelmOpResources.test.ts +56 -0
  176. package/composables/useHelmOpResources.ts +32 -0
  177. package/composables/useStateColor.test.ts +325 -0
  178. package/composables/useStateColor.ts +128 -0
  179. package/composables/useVeeValidateField.test.ts +159 -0
  180. package/composables/useVeeValidateField.ts +67 -0
  181. package/config/home-links.js +1 -1
  182. package/config/labels-annotations.js +1 -0
  183. package/config/pagination-table-headers.js +18 -1
  184. package/config/product/explorer.js +17 -4
  185. package/config/product/manager.js +84 -21
  186. package/config/router/index.js +16 -0
  187. package/config/router/navigation-guards/__tests__/authentication.test.ts +130 -0
  188. package/config/router/navigation-guards/authentication.js +10 -4
  189. package/config/router/routes.js +26 -6
  190. package/config/settings.ts +0 -2
  191. package/config/table-headers.js +23 -5
  192. package/config/types.js +11 -1
  193. package/core/__tests__/plugin-products.test.ts +904 -20
  194. package/core/plugin-products-base.ts +110 -10
  195. package/core/plugin-products.ts +4 -0
  196. package/core/plugin-types.ts +194 -31
  197. package/core/plugin.ts +18 -7
  198. package/core/productDebugger.js +9 -4
  199. package/core/types-provisioning.ts +77 -31
  200. package/core/types.ts +72 -22
  201. package/detail/__tests__/pod.test.ts +41 -0
  202. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +114 -0
  203. package/detail/__tests__/workload.test.ts +3 -152
  204. package/detail/catalog.cattle.io.clusterrepo.vue +1 -1
  205. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  206. package/detail/pod.vue +1 -1
  207. package/detail/provisioning.cattle.io.cluster.vue +34 -14
  208. package/detail/workload/index.vue +12 -55
  209. package/edit/__tests__/catalog.cattle.io.clusterrepo.test.ts +248 -0
  210. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +105 -0
  211. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/General.test.ts.snap +6 -0
  212. package/edit/auditlog.cattle.io.auditpolicy/__tests__/__snapshots__/index.test.ts.snap +1 -0
  213. package/edit/auth/__tests__/azuread.test.ts +247 -39
  214. package/edit/auth/__tests__/github.test.ts +234 -0
  215. package/edit/auth/__tests__/oidc.test.ts +26 -6
  216. package/edit/auth/__tests__/saml.test.ts +196 -0
  217. package/edit/auth/azuread.vue +197 -56
  218. package/edit/auth/github.vue +72 -13
  219. package/edit/auth/ldap/__tests__/index.test.ts +206 -0
  220. package/edit/auth/ldap/config.vue +8 -0
  221. package/edit/auth/ldap/index.vue +75 -1
  222. package/edit/auth/oidc.vue +119 -73
  223. package/edit/auth/saml.vue +76 -12
  224. package/edit/catalog.cattle.io.clusterrepo.vue +140 -32
  225. package/edit/fleet.cattle.io.helmop.vue +491 -136
  226. package/edit/management.cattle.io.user.vue +5 -2
  227. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  228. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  229. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  230. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  231. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  232. package/edit/provisioning.cattle.io.cluster/rke2.vue +92 -14
  233. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +22 -0
  234. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  235. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  236. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  237. package/edit/secret/__tests__/ssh.test.ts +5 -6
  238. package/edit/secret/basic.vue +31 -0
  239. package/edit/secret/index.vue +68 -17
  240. package/edit/secret/registry.vue +38 -0
  241. package/edit/secret/ssh.vue +29 -0
  242. package/edit/secret/tls.vue +30 -0
  243. package/edit/service.vue +4 -4
  244. package/edit/workload/Upgrading.vue +3 -3
  245. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  246. package/edit/workload/mixins/workload.js +2 -1
  247. package/list/fleet.cattle.io.bundle.vue +7 -104
  248. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  249. package/list/group.principal.vue +5 -4
  250. package/list/harvesterhci.io.management.cluster.vue +8 -9
  251. package/list/management.cattle.io.user.vue +12 -9
  252. package/list/provisioning.cattle.io.cluster.vue +268 -180
  253. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  254. package/mixins/__tests__/auth-config.test.ts +90 -0
  255. package/mixins/__tests__/chart.test.ts +206 -0
  256. package/mixins/__tests__/resource-fetch-api-pagination.test.ts +48 -0
  257. package/mixins/auth-config.js +7 -0
  258. package/mixins/brand.js +2 -1
  259. package/mixins/chart.js +22 -9
  260. package/mixins/child-hook.js +12 -6
  261. package/mixins/create-edit-view/impl.js +5 -3
  262. package/mixins/resource-fetch-api-pagination.js +62 -6
  263. package/models/__tests__/catalog.cattle.io.clusterrepo.test.ts +57 -0
  264. package/models/__tests__/compliance.cattle.io.clusterscan.test.ts +144 -0
  265. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  266. package/models/__tests__/fleet-application.test.ts +175 -0
  267. package/models/__tests__/fleet.cattle.io.bundle.test.ts +169 -0
  268. package/models/__tests__/fleet.cattle.io.helmop.test.ts +84 -0
  269. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  270. package/models/__tests__/management.cattle.io.node.ts +28 -5
  271. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  272. package/models/__tests__/namespace.test.ts +36 -0
  273. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +81 -11
  274. package/models/__tests__/workload.test.ts +401 -26
  275. package/models/base-cluster.x-k8s.io.js +26 -0
  276. package/models/catalog.cattle.io.clusterrepo.js +28 -4
  277. package/models/cluster.js +1 -1
  278. package/models/cluster.x-k8s.io.machine.js +4 -22
  279. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  280. package/models/cluster.x-k8s.io.machineset.js +2 -20
  281. package/models/compliance.cattle.io.clusterscan.js +165 -2
  282. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  283. package/models/fleet-application.js +7 -1
  284. package/models/fleet.cattle.io.helmop.js +20 -1
  285. package/models/management.cattle.io.cluster.js +434 -41
  286. package/models/management.cattle.io.node.js +50 -7
  287. package/models/management.cattle.io.nodepool.js +1 -1
  288. package/models/namespace.js +1 -1
  289. package/models/networking.k8s.io.ingress.js +12 -4
  290. package/models/pod.js +33 -1
  291. package/models/provisioning.cattle.io.cluster.js +51 -334
  292. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  293. package/models/workload.js +108 -13
  294. package/models/workload.service.js +5 -0
  295. package/package.json +22 -39
  296. package/pages/__tests__/readme.test.ts +49 -0
  297. package/pages/about.vue +5 -6
  298. package/pages/auth/login.vue +0 -35
  299. package/pages/auth/setup.vue +13 -3
  300. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +2 -2
  301. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +10 -1
  302. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  303. package/pages/c/_cluster/apps/charts/__tests__/index.test.ts +93 -0
  304. package/pages/c/_cluster/apps/charts/chart.vue +62 -9
  305. package/pages/c/_cluster/apps/charts/index.vue +48 -10
  306. package/pages/c/_cluster/apps/charts/install.vue +122 -113
  307. package/pages/c/_cluster/auth/roles/index.vue +5 -4
  308. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  309. package/pages/c/_cluster/explorer/index.vue +5 -49
  310. package/pages/c/_cluster/explorer/workload-dashboard/ByNamespaceSection.vue +31 -0
  311. package/pages/c/_cluster/explorer/workload-dashboard/ByStateSection.vue +138 -0
  312. package/pages/c/_cluster/explorer/workload-dashboard/ByTypeSection.vue +30 -0
  313. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadCard.vue +155 -0
  314. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadNamespaceCard.vue +142 -0
  315. package/pages/c/_cluster/explorer/workload-dashboard/WorkloadTypeCard.vue +159 -0
  316. package/pages/c/_cluster/explorer/workload-dashboard/__tests__/composable.test.ts +561 -0
  317. package/pages/c/_cluster/explorer/workload-dashboard/composable.ts +440 -0
  318. package/pages/c/_cluster/explorer/workload-dashboard/index.vue +187 -0
  319. package/pages/c/_cluster/explorer/workload-dashboard/types.ts +80 -0
  320. package/pages/c/_cluster/fleet/application/create.vue +187 -136
  321. package/pages/c/_cluster/fleet/application/index.vue +5 -3
  322. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailBody.vue +338 -0
  323. package/pages/c/_cluster/fleet/application/suse-app-collection/ChartDetailHeader.vue +121 -0
  324. package/pages/c/_cluster/fleet/application/suse-app-collection/chart.vue +369 -0
  325. package/pages/c/_cluster/fleet/application/suse-app-collection/charts.vue +248 -0
  326. package/pages/c/_cluster/fleet/application/suse-app-collection/credentials.vue +310 -0
  327. package/pages/c/_cluster/fleet/index.vue +2 -2
  328. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  329. package/pages/c/_cluster/istio/index.vue +21 -6
  330. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  331. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +815 -2
  332. package/pages/c/_cluster/uiplugins/index.vue +218 -197
  333. package/pages/diagnostic.vue +13 -17
  334. package/pages/fail-whale.vue +30 -7
  335. package/pages/home.vue +93 -306
  336. package/pages/readme.vue +88 -0
  337. package/plugins/clean-html.d.ts +9 -0
  338. package/plugins/dashboard-store/__tests__/resource-class.test.ts +181 -0
  339. package/plugins/dashboard-store/actions.js +40 -18
  340. package/plugins/dashboard-store/resource-class.js +67 -9
  341. package/plugins/steve/__tests__/actions.test.ts +212 -0
  342. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  343. package/plugins/steve/actions.js +96 -0
  344. package/plugins/steve/steve-pagination-utils.ts +12 -4
  345. package/plugins/steve/subscribe.js +35 -5
  346. package/rancher-components/Accordion/Accordion.vue +53 -9
  347. package/rancher-components/Form/Checkbox/Checkbox.vue +14 -0
  348. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  349. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  350. package/rancher-components/Form/Radio/RadioButton.vue +17 -1
  351. package/rancher-components/Form/Radio/RadioGroup.vue +10 -0
  352. package/rancher-components/Pill/RcTag/RcTag.vue +3 -2
  353. package/rancher-components/RcButton/RcButton.test.ts +140 -1
  354. package/rancher-components/RcButton/RcButton.vue +126 -17
  355. package/rancher-components/RcButton/types.ts +3 -0
  356. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  357. package/rancher-components/RcItemCard/RcItemCard.test.ts +18 -0
  358. package/rancher-components/RcItemCard/RcItemCard.vue +2 -2
  359. package/rancher-components/RcSection/RcSection.vue +28 -3
  360. package/scripts/extension/helm/package/Dockerfile +1 -1
  361. package/scripts/test-plugins-build.sh +2 -1
  362. package/store/__tests__/catalog.test.ts +115 -1
  363. package/store/__tests__/notifications.test.ts +434 -0
  364. package/store/__tests__/type-map.test.ts +556 -1
  365. package/store/action-menu.js +8 -3
  366. package/store/auth.js +1 -1
  367. package/store/aws.js +27 -16
  368. package/store/catalog.js +84 -3
  369. package/store/digitalocean.js +20 -38
  370. package/store/index.js +2 -0
  371. package/store/linode.js +25 -40
  372. package/store/plugins.js +7 -4
  373. package/store/pnap.js +1 -0
  374. package/store/type-map.js +111 -29
  375. package/tsconfig.paths.json +8 -8
  376. package/types/components/buttonGroup.ts +5 -0
  377. package/types/kube/kube-api.ts +14 -1
  378. package/types/rancher/steve.api.ts +12 -12
  379. package/types/resources/settings.d.ts +2 -1
  380. package/types/shell/index.d.ts +206 -72
  381. package/types/store/dashboard-store.types.ts +108 -11
  382. package/types/store/pagination.types.ts +6 -3
  383. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  384. package/utils/__tests__/async.test.ts +87 -0
  385. package/utils/__tests__/auth.test.ts +273 -0
  386. package/utils/__tests__/aws.test.ts +140 -0
  387. package/utils/__tests__/banners.test.ts +176 -0
  388. package/utils/__tests__/chart.test.ts +64 -1
  389. package/utils/__tests__/color.test.ts +226 -0
  390. package/utils/__tests__/computed.test.ts +193 -0
  391. package/utils/__tests__/cspAdaptor.test.ts +163 -0
  392. package/utils/__tests__/dom.test.ts +81 -0
  393. package/utils/__tests__/duration.test.ts +176 -0
  394. package/utils/__tests__/dynamic-importer.test.ts +102 -0
  395. package/utils/__tests__/fleet-appco.test.ts +312 -0
  396. package/utils/__tests__/fleet.test.ts +340 -0
  397. package/utils/__tests__/ingress.test.ts +553 -0
  398. package/utils/__tests__/kube.test.ts +68 -0
  399. package/utils/__tests__/monitoring.test.ts +130 -0
  400. package/utils/__tests__/namespace-filter.test.ts +109 -0
  401. package/utils/__tests__/object.test.ts +22 -0
  402. package/utils/__tests__/pagination-utils.test.ts +361 -0
  403. package/utils/__tests__/parse-externalid.test.ts +137 -0
  404. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  405. package/utils/__tests__/platform.test.ts +91 -0
  406. package/utils/__tests__/poller-sequential.test.ts +177 -0
  407. package/utils/__tests__/poller.test.ts +170 -0
  408. package/utils/__tests__/position.test.ts +237 -0
  409. package/utils/__tests__/promise.test.ts +346 -0
  410. package/utils/__tests__/provider.test.ts +51 -1
  411. package/utils/__tests__/queue.test.ts +232 -0
  412. package/utils/__tests__/release-notes.test.ts +221 -0
  413. package/utils/__tests__/router.test.js +254 -1
  414. package/utils/__tests__/select.test.ts +208 -0
  415. package/utils/__tests__/settings.test.ts +140 -0
  416. package/utils/__tests__/sort-utils.test.ts +301 -0
  417. package/utils/__tests__/string-utils.test.ts +798 -0
  418. package/utils/__tests__/string.test.ts +23 -1
  419. package/utils/__tests__/style.test.ts +154 -0
  420. package/utils/__tests__/svg-filter.test.ts +184 -0
  421. package/utils/__tests__/time.test.ts +265 -1
  422. package/utils/__tests__/title.test.ts +47 -0
  423. package/utils/__tests__/units.test.ts +417 -0
  424. package/utils/__tests__/versions.test.ts +128 -0
  425. package/utils/__tests__/width.test.ts +53 -0
  426. package/utils/__tests__/window.test.ts +158 -0
  427. package/utils/__tests__/xccdf.test.ts +511 -0
  428. package/utils/chart.js +36 -0
  429. package/utils/crypto/__tests__/browserHashUtils.test.ts +98 -0
  430. package/utils/crypto/__tests__/index.test.ts +144 -0
  431. package/utils/duration.ts +104 -0
  432. package/utils/dynamic-content/__tests__/notification-handler.test.ts +196 -0
  433. package/utils/dynamic-content/info.ts +2 -1
  434. package/utils/error.js +13 -0
  435. package/utils/fleet-appco.ts +323 -0
  436. package/utils/fleet.ts +13 -3
  437. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  438. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  439. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  440. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  441. package/utils/gc/__tests__/gc.test.ts +487 -0
  442. package/utils/ingress.ts +9 -1
  443. package/utils/object.js +22 -2
  444. package/utils/pagination-utils.ts +2 -1
  445. package/utils/provider.ts +12 -0
  446. package/utils/string.js +25 -2
  447. package/utils/uiplugins.ts +5 -5
  448. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  449. package/utils/validators/__tests__/container-images.test.ts +104 -0
  450. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  451. package/utils/validators/__tests__/flow-output.test.ts +91 -0
  452. package/utils/validators/__tests__/index.test.ts +481 -0
  453. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  454. package/utils/validators/__tests__/logging-outputs.test.ts +58 -0
  455. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  456. package/utils/validators/__tests__/monitoring-route.test.ts +119 -0
  457. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  458. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  459. package/utils/validators/__tests__/role-template.test.ts +149 -0
  460. package/utils/validators/__tests__/service.test.ts +283 -0
  461. package/utils/validators/__tests__/setting.test.js +32 -0
  462. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  463. package/utils/validators/formRules/index.ts +5 -5
  464. package/utils/validators/machine-pool.ts +1 -1
  465. package/utils/validators/setting.js +18 -3
  466. package/utils/xccdf.ts +415 -0
  467. package/vue.config.js +1 -1
  468. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  469. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  470. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  471. package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
  472. package/pages/support/index.vue +0 -264
  473. package/utils/duration.js +0 -43
@@ -0,0 +1,158 @@
1
+ import { open, Popup, popupWindowOptions } from '@shell/utils/window';
2
+
3
+ function mockScreen(width: number, height: number) {
4
+ Object.defineProperty(window, 'screen', {
5
+ configurable: true,
6
+ writable: true,
7
+ value: { width, height },
8
+ });
9
+ }
10
+
11
+ describe('window utils', () => {
12
+ describe('popupWindowOptions', () => {
13
+ afterEach(() => {
14
+ mockScreen(0, 0);
15
+ });
16
+
17
+ it.each([
18
+ {
19
+ desc: 'uses default 1040×768 and centers on a large screen when no size is given',
20
+ screenWidth: 2560,
21
+ screenHeight: 1440,
22
+ width: undefined as number | undefined,
23
+ height: undefined as number | undefined,
24
+ expected: 'width=1040,height=768,resizable=1,scrollbars=1,left=760,top=336',
25
+ },
26
+ {
27
+ desc: 'uses custom size and centers on a large screen',
28
+ screenWidth: 2560,
29
+ screenHeight: 1440,
30
+ width: 800,
31
+ height: 600,
32
+ expected: 'width=800,height=600,resizable=1,scrollbars=1,left=880,top=420',
33
+ },
34
+ {
35
+ desc: 'caps size to screen dimensions when requested size exceeds screen',
36
+ screenWidth: 2560,
37
+ screenHeight: 1440,
38
+ width: 3000,
39
+ height: 2000,
40
+ expected: 'width=2560,height=1440,resizable=1,scrollbars=1,left=0,top=0',
41
+ },
42
+ {
43
+ desc: 'caps size to screen dimensions and uses left/top 0 when screen is smaller than defaults',
44
+ screenWidth: 800,
45
+ screenHeight: 600,
46
+ width: undefined as number | undefined,
47
+ height: undefined as number | undefined,
48
+ expected: 'width=800,height=600,resizable=1,scrollbars=1,left=0,top=0',
49
+ },
50
+ ])('$desc', ({
51
+ screenWidth, screenHeight, width, height, expected
52
+ }) => {
53
+ mockScreen(screenWidth, screenHeight);
54
+ expect(popupWindowOptions(width, height)).toStrictEqual(expected);
55
+ });
56
+ });
57
+
58
+ describe('open', () => {
59
+ let mockWindowOpen: jest.SpyInstance;
60
+
61
+ beforeEach(() => {
62
+ mockWindowOpen = jest.spyOn(window, 'open').mockReturnValue(null);
63
+ });
64
+
65
+ afterEach(() => {
66
+ mockWindowOpen.mockRestore();
67
+ });
68
+
69
+ it('calls window.open with the given url, name, and options', () => {
70
+ open('https://example.com', '_blank', 'width=800');
71
+ expect(mockWindowOpen).toHaveBeenCalledWith('https://example.com', '_blank', 'width=800');
72
+ });
73
+
74
+ it('uses default name _blank and empty options when not specified', () => {
75
+ open('https://example.com');
76
+ expect(mockWindowOpen).toHaveBeenCalledWith('https://example.com', '_blank', '');
77
+ });
78
+ });
79
+
80
+ describe('Popup class', () => {
81
+ const mockPopup = { closed: false };
82
+ let mockWindowOpen: jest.SpyInstance;
83
+
84
+ beforeEach(() => {
85
+ mockPopup.closed = false;
86
+ mockWindowOpen = jest
87
+ .spyOn(window, 'open')
88
+ .mockReturnValue(mockPopup as unknown as Window);
89
+ });
90
+
91
+ afterEach(() => {
92
+ mockWindowOpen.mockRestore();
93
+ });
94
+
95
+ it('initialises with null popup reference', () => {
96
+ const popup = new Popup();
97
+
98
+ expect(popup.popup).toBeNull();
99
+ });
100
+
101
+ it('calls onOpen callback when open is called', () => {
102
+ const onOpen = jest.fn();
103
+ const popup = new Popup(onOpen);
104
+
105
+ popup.open('https://example.com', '_blank', '', true);
106
+
107
+ expect(onOpen).toHaveBeenCalledWith();
108
+ });
109
+
110
+ it('sets popup reference to the opened window', () => {
111
+ const popup = new Popup();
112
+
113
+ popup.open('https://example.com', '_blank', '', true);
114
+
115
+ expect(popup.popup).toStrictEqual(mockPopup);
116
+ });
117
+
118
+ it('throws when the popup is blocked by the browser', () => {
119
+ mockWindowOpen.mockReturnValue(null);
120
+ const popup = new Popup();
121
+
122
+ expect(() => popup.open('https://example.com', '_blank', '', true))
123
+ .toThrow('Please disable your popup blocker for this site');
124
+ });
125
+
126
+ describe('when polling for window closure', () => {
127
+ beforeEach(() => {
128
+ jest.useFakeTimers();
129
+ });
130
+
131
+ afterEach(() => {
132
+ jest.useRealTimers();
133
+ });
134
+
135
+ it('calls onClose callback when the popup window is closed', () => {
136
+ const onClose = jest.fn();
137
+ const popup = new Popup(undefined, onClose);
138
+
139
+ popup.open('https://example.com', '_blank', '', false);
140
+ mockPopup.closed = true;
141
+ jest.advanceTimersByTime(500);
142
+
143
+ expect(onClose).toHaveBeenCalledWith();
144
+ });
145
+
146
+ it('does not call onClose when doNotPollForClosure is true', () => {
147
+ const onClose = jest.fn();
148
+ const popup = new Popup(undefined, onClose);
149
+
150
+ popup.open('https://example.com', '_blank', '', true);
151
+ mockPopup.closed = true;
152
+ jest.advanceTimersByTime(1000);
153
+
154
+ expect(onClose).not.toHaveBeenCalled();
155
+ });
156
+ });
157
+ });
158
+ });
@@ -0,0 +1,511 @@
1
+ import { generateXCCDF, generateXCCDFPerNode } from '@shell/utils/xccdf';
2
+
3
+ describe('xccdf util: generateXCCDF', () => {
4
+ const baseReport = {
5
+ version: '1.0',
6
+ total: 2,
7
+ pass: 1,
8
+ nodes: { master: ['node-a'], node: ['node-b'] },
9
+ results: [{
10
+ id: '5.1',
11
+ description: 'RBAC and Service Accounts',
12
+ checks: [{
13
+ id: '5.1.1',
14
+ description: 'Ensure that the cluster-admin role is only used where required',
15
+ audit: 'kubectl get clusterrolebindings',
16
+ remediation: 'Identify all clusterrolebindings to the cluster-admin role',
17
+ scored: true,
18
+ state: 'pass',
19
+ }, {
20
+ id: '5.1.2',
21
+ description: 'Minimize access to secrets',
22
+ audit: 'kubectl get roles',
23
+ remediation: 'Where possible, remove get, list and watch access to Secret objects',
24
+ scored: false,
25
+ state: 'fail',
26
+ }],
27
+ }],
28
+ };
29
+
30
+ it('produces an XML document with an XML header and a Benchmark root', () => {
31
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
32
+
33
+ expect(xml).toMatch(/^<\?xml version="1\.0" encoding="UTF-8"\?>/);
34
+ expect(xml).toContain('<Benchmark');
35
+ expect(xml).toContain('xmlns="http://checklists.nist.gov/xccdf/1.2"');
36
+ expect(xml).toContain('id="xccdf_compliance-operator_benchmark_kubernetes"');
37
+ });
38
+
39
+ it('uses the metadata title when provided, otherwise falls back to a CIS title from benchmarkVersion', () => {
40
+ const fallback = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
41
+
42
+ expect(fallback).toContain('<title>Kubernetes CIS Benchmark cis-1.7</title>');
43
+
44
+ const withTitle = generateXCCDF({
45
+ report: baseReport,
46
+ benchmarkVersion: 'cis-1.7',
47
+ metadata: { title: 'Custom Benchmark Title' },
48
+ });
49
+
50
+ expect(withTitle).toContain('<title>Custom Benchmark Title</title>');
51
+ });
52
+
53
+ it('emits one Group per result and one Rule per check', () => {
54
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
55
+
56
+ expect(xml).toContain('<Group id="5.1">');
57
+ expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.1"');
58
+ expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.2"');
59
+ });
60
+
61
+ it('maps state values to XCCDF result strings', () => {
62
+ const report = {
63
+ ...baseReport,
64
+ results: [{
65
+ id: '1',
66
+ description: 'g',
67
+ checks: [
68
+ {
69
+ id: 'a', description: 'a', state: 'pass' as const
70
+ },
71
+ {
72
+ id: 'b', description: 'b', state: 'fail' as const
73
+ },
74
+ {
75
+ id: 'c', description: 'c', state: 'skip' as const
76
+ },
77
+ {
78
+ id: 'd', description: 'd', state: 'warn' as const
79
+ },
80
+ {
81
+ id: 'e', description: 'e', state: 'notApplicable' as const
82
+ },
83
+ ],
84
+ }],
85
+ };
86
+
87
+ const xml = generateXCCDF({ report, benchmarkVersion: 'cis-1.7' });
88
+
89
+ expect(xml).toContain('<result>pass</result>');
90
+ expect(xml).toContain('<result>fail</result>');
91
+ expect(xml).toContain('<result>notselected</result>');
92
+ expect(xml).toContain('<result>informational</result>');
93
+ expect(xml).toContain('<result>notapplicable</result>');
94
+ });
95
+
96
+ it('marks scored checks with severity=medium and weight=10; unscored as low and 0', () => {
97
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
98
+
99
+ expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.1"[^>]*severity="medium"/);
100
+ expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.1"[^>]*weight="10"/);
101
+ expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.2"[^>]*severity="low"/);
102
+ expect(xml).toMatch(/id="xccdf_compliance-operator_rule_5\.1\.2"[^>]*weight="0"/);
103
+ });
104
+
105
+ it('computes score as pass/total*100, formatted to one decimal', () => {
106
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
107
+
108
+ expect(xml).toMatch(/<score[^>]*maximum="100\.0"[^>]*>50\.0<\/score>/);
109
+ });
110
+
111
+ it('returns score 0.0 when total is 0', () => {
112
+ const xml = generateXCCDF({
113
+ report: {
114
+ ...baseReport, total: 0, pass: 0, results: [],
115
+ },
116
+ benchmarkVersion: 'cis-1.7',
117
+ });
118
+
119
+ expect(xml).toMatch(/<score[^>]*>0\.0<\/score>/);
120
+ });
121
+
122
+ it('uses report.nodes for <target> elements when no clusterName is set', () => {
123
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
124
+
125
+ expect(xml).toContain('<target>node-a</target>');
126
+ expect(xml).toContain('<target>node-b</target>');
127
+ });
128
+
129
+ it('clusterName argument overrides metadata.clusterName and node-derived targets', () => {
130
+ const xml = generateXCCDF({
131
+ report: baseReport,
132
+ benchmarkVersion: 'cis-1.7',
133
+ metadata: { clusterName: 'from-metadata' },
134
+ clusterName: 'from-arg',
135
+ });
136
+
137
+ expect(xml).toContain('<target>from-arg</target>');
138
+ expect(xml).not.toContain('<target>from-metadata</target>');
139
+ expect(xml).not.toContain('<target>node-a</target>');
140
+ });
141
+
142
+ it('falls back to "not-applicable" for missing target addresses and target facts', () => {
143
+ const xml = generateXCCDF({ report: baseReport, benchmarkVersion: 'cis-1.7' });
144
+
145
+ expect(xml).toContain('<target-address>not-applicable</target-address>');
146
+ expect(xml).toContain('<fact name="not-applicable" type="string">not-applicable</fact>');
147
+ });
148
+
149
+ it('emits a single placeholder Group when results is empty', () => {
150
+ const xml = generateXCCDF({
151
+ report: {
152
+ version: '1.0', total: 0, pass: 0, results: [],
153
+ },
154
+ benchmarkVersion: 'cis-1.7',
155
+ });
156
+
157
+ expect(xml).toContain('<Group id="not-applicable">');
158
+ });
159
+
160
+ it('applies a per-check decoration verbatim when the decoration key matches the check id exactly', () => {
161
+ const xml = generateXCCDF({
162
+ report: {
163
+ ...baseReport,
164
+ results: [{
165
+ id: '5.1',
166
+ description: 'g',
167
+ checks: [{
168
+ id: '5.1.1', description: 'exact', scored: true, state: 'pass',
169
+ }],
170
+ }],
171
+ },
172
+ benchmarkVersion: 'cis-1.7',
173
+ decorations: {
174
+ '5.1.1': {
175
+ ruleId: 'CIS-5.1.1-rule', version: '2024-01', severity: 'high', fixId: 'F-5.1.1', checkId: 'C-5.1.1',
176
+ },
177
+ },
178
+ });
179
+
180
+ expect(xml).toContain('id="CIS-5.1.1-rule"');
181
+ expect(xml).toContain('idref="CIS-5.1.1-rule"');
182
+ expect(xml).toMatch(/id="CIS-5\.1\.1-rule"[^>]*severity="high"/);
183
+ expect(xml).toContain('<version>2024-01</version>');
184
+ expect(xml).toContain('fixref="F-5.1.1"');
185
+ expect(xml).toContain('<check system="C-5.1.1">');
186
+ });
187
+
188
+ it('applies a group-keyed decoration with a suffix derived from the check id', () => {
189
+ const xml = generateXCCDF({
190
+ report: {
191
+ ...baseReport,
192
+ results: [{
193
+ id: 'V-254553',
194
+ description: 'STIG group',
195
+ checks: [{
196
+ id: 'V-254553-TLS-apiserver', description: 'STIG check', scored: true, state: 'pass',
197
+ }],
198
+ }],
199
+ },
200
+ benchmarkVersion: 'rke2-stig-1.31',
201
+ decorations: {
202
+ 'V-254553': {
203
+ ruleId: 'SV-254553r1016525_rule', version: 'SV-254553r1016525_rule', severity: 'high', fixId: 'F-254553', checkId: 'C-254553',
204
+ },
205
+ },
206
+ });
207
+
208
+ expect(xml).toContain('id="SV-254553r1016525_rule_TLS-apiserver"');
209
+ expect(xml).toContain('idref="SV-254553r1016525_rule_TLS-apiserver"');
210
+ expect(xml).toContain('severity="high"');
211
+ expect(xml).toContain('fixref="F-254553"');
212
+ expect(xml).toContain('<check system="C-254553">');
213
+ });
214
+
215
+ it('falls back to the full check id as suffix when the check id does not start with the group id', () => {
216
+ const xml = generateXCCDF({
217
+ report: {
218
+ ...baseReport,
219
+ results: [{
220
+ id: 'group-a',
221
+ description: 'g',
222
+ checks: [{
223
+ id: 'unrelated-check', description: 'c', scored: true, state: 'pass',
224
+ }],
225
+ }],
226
+ },
227
+ benchmarkVersion: 'cis-1.7',
228
+ decorations: { 'group-a': { ruleId: 'GROUP-A-rule' } },
229
+ });
230
+
231
+ expect(xml).toContain('id="GROUP-A-rule_unrelated-check"');
232
+ expect(xml).toContain('idref="GROUP-A-rule_unrelated-check"');
233
+ });
234
+
235
+ it('emits generic XCCDF idents from the decoration with their declared system unchanged', () => {
236
+ const xml = generateXCCDF({
237
+ report: {
238
+ ...baseReport,
239
+ results: [{
240
+ id: '5.1.1',
241
+ description: 'g',
242
+ checks: [{
243
+ id: '5.1.1', description: 'c', scored: true, state: 'pass',
244
+ }],
245
+ }],
246
+ },
247
+ benchmarkVersion: 'cis-1.7',
248
+ decorations: {
249
+ '5.1.1': {
250
+ idents: [
251
+ { system: 'https://www.cisecurity.org/controls/', value: 'CIS-CSC-3' },
252
+ { system: 'http://cyber.mil/cci', value: 'CCI-000366' },
253
+ ],
254
+ },
255
+ },
256
+ });
257
+
258
+ expect(xml).toContain('<ident system="https://www.cisecurity.org/controls/">CIS-CSC-3</ident>');
259
+ expect(xml).toContain('<ident system="http://cyber.mil/cci">CCI-000366</ident>');
260
+ });
261
+
262
+ it('emits a not-applicable ident placeholder when the decoration has no idents', () => {
263
+ const xml = generateXCCDF({
264
+ report: baseReport,
265
+ benchmarkVersion: 'cis-1.7',
266
+ decorations: { '5.1.1': { ruleId: 'r-5.1.1' } },
267
+ });
268
+
269
+ expect(xml).toMatch(/id="r-5\.1\.1"[\s\S]*?<ident system="not-applicable">not-applicable<\/ident>/);
270
+ });
271
+
272
+ it('falls back to the operator-style rule id when no decoration matches either the check id or the group id', () => {
273
+ const xml = generateXCCDF({
274
+ report: baseReport,
275
+ benchmarkVersion: 'cis-1.7',
276
+ decorations: { 'no-match': { ruleId: 'should-not-apply' } },
277
+ });
278
+
279
+ expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.1"');
280
+ expect(xml).not.toContain('should-not-apply');
281
+ });
282
+
283
+ it('prefers a per-check decoration over a group-keyed decoration when both are present', () => {
284
+ const xml = generateXCCDF({
285
+ report: {
286
+ ...baseReport,
287
+ results: [{
288
+ id: '5.1',
289
+ description: 'g',
290
+ checks: [{
291
+ id: '5.1.1', description: 'c', scored: true, state: 'pass',
292
+ }],
293
+ }],
294
+ },
295
+ benchmarkVersion: 'cis-1.7',
296
+ decorations: {
297
+ 5.1: { ruleId: 'group-rule', severity: 'low' },
298
+ '5.1.1': { ruleId: 'check-rule', severity: 'high' },
299
+ },
300
+ });
301
+
302
+ expect(xml).toContain('id="check-rule"');
303
+ expect(xml).not.toContain('id="group-rule_5.1.1"');
304
+ expect(xml).toMatch(/id="check-rule"[^>]*severity="high"/);
305
+ });
306
+
307
+ it('preserves the operator-style rule id when no decorations are supplied', () => {
308
+ const xml = generateXCCDF({
309
+ report: baseReport,
310
+ benchmarkVersion: 'cis-1.7',
311
+ });
312
+
313
+ expect(xml).toContain('id="xccdf_compliance-operator_rule_5.1.1"');
314
+ });
315
+ });
316
+
317
+ describe('xccdf util: generateXCCDFPerNode', () => {
318
+ const multiNodeReport = {
319
+ version: '1.0',
320
+ total: 3,
321
+ pass: 1,
322
+ nodes: { master: ['m-1'], node: ['w-1', 'w-2'] },
323
+ results: [{
324
+ id: '1.1',
325
+ description: 'Master Node Configuration',
326
+ checks: [{
327
+ id: '1.1.1',
328
+ description: 'master check',
329
+ scored: true,
330
+ state: 'pass' as const,
331
+ }],
332
+ }, {
333
+ id: '4.1',
334
+ description: 'Worker Node Configuration',
335
+ checks: [{
336
+ id: '4.1.1',
337
+ description: 'mixed check',
338
+ scored: true,
339
+ state: 'mixed' as const,
340
+ nodes: ['w-2'],
341
+ }, {
342
+ id: '4.1.2',
343
+ description: 'failing check',
344
+ scored: false,
345
+ state: 'fail' as const,
346
+ }],
347
+ }],
348
+ };
349
+
350
+ it('emits a single <target> equal to the hostname', () => {
351
+ const xml = generateXCCDFPerNode({
352
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
353
+ });
354
+
355
+ expect(xml).toContain('<target>w-1</target>');
356
+ expect(xml).not.toContain('<target>w-2</target>');
357
+ expect(xml).not.toContain('<target>m-1</target>');
358
+ });
359
+
360
+ it('assigns each per-node document a TestResult id suffixed with the hostname so co-loaded files do not collide', () => {
361
+ const a = generateXCCDFPerNode({
362
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
363
+ });
364
+ const b = generateXCCDFPerNode({
365
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
366
+ });
367
+
368
+ expect(a).toContain('<TestResult id="xccdf_compliance-operator_testresult_1_w-1"');
369
+ expect(b).toContain('<TestResult id="xccdf_compliance-operator_testresult_1_w-2"');
370
+ expect(a).not.toContain('id="xccdf_compliance-operator_testresult_1_w-2"');
371
+ });
372
+
373
+ it('emits every rule from the cluster report regardless of role', () => {
374
+ const workerXml = generateXCCDFPerNode({
375
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
376
+ });
377
+ const masterXml = generateXCCDFPerNode({
378
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'm-1', role: 'master',
379
+ });
380
+
381
+ [workerXml, masterXml].forEach((xml) => {
382
+ expect(xml).toContain('xccdf_compliance-operator_rule_1.1.1');
383
+ expect(xml).toContain('xccdf_compliance-operator_rule_4.1.1');
384
+ expect(xml).toContain('xccdf_compliance-operator_rule_4.1.2');
385
+ });
386
+ });
387
+
388
+ it('maps mixed-state checks to fail for dissenting hosts and pass for the rest', () => {
389
+ const dissenter = generateXCCDFPerNode({
390
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
391
+ });
392
+ const compliant = generateXCCDFPerNode({
393
+ report: multiNodeReport, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
394
+ });
395
+
396
+ expect(dissenter).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.1"[\s\S]*?<result>fail<\/result>/);
397
+ expect(compliant).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.1"[\s\S]*?<result>pass<\/result>/);
398
+ });
399
+
400
+ it('treats mixed-state checks with no dissent list as pass for all nodes', () => {
401
+ const report = {
402
+ ...multiNodeReport,
403
+ results: [{
404
+ id: '4.1',
405
+ description: 'g',
406
+ checks: [{
407
+ id: '4.1.9', description: 'm', state: 'mixed' as const,
408
+ }],
409
+ }],
410
+ };
411
+ const xml = generateXCCDFPerNode({
412
+ report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
413
+ });
414
+
415
+ expect(xml).toMatch(/idref="xccdf_compliance-operator_rule_4\.1\.9"[\s\S]*?<result>pass<\/result>/);
416
+ });
417
+
418
+ it('recomputes pass count per node while preserving cluster total as the scoring denominator', () => {
419
+ const report = {
420
+ version: '1.0',
421
+ total: 2,
422
+ pass: 1,
423
+ nodes: { node: ['w-1', 'w-2'] },
424
+ results: [{
425
+ id: '1',
426
+ description: 'g',
427
+ checks: [
428
+ {
429
+ id: 'a', description: 'a', state: 'pass' as const
430
+ },
431
+ {
432
+ id: 'b', description: 'b', state: 'mixed' as const, nodes: ['w-2'],
433
+ },
434
+ ],
435
+ }],
436
+ };
437
+ const compliant = generateXCCDFPerNode({
438
+ report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
439
+ });
440
+ const dissenter = generateXCCDFPerNode({
441
+ report, benchmarkVersion: 'cis-1.7', hostname: 'w-2', role: 'node',
442
+ });
443
+
444
+ expect(compliant).toMatch(/<score[^>]*>100\.0<\/score>/);
445
+ expect(dissenter).toMatch(/<score[^>]*>50\.0<\/score>/);
446
+ });
447
+
448
+ it('preserves full rule metadata (title, fixtext, idents, check) from the cluster report', () => {
449
+ const report = {
450
+ version: '1.0',
451
+ total: 1,
452
+ pass: 1,
453
+ nodes: { node: ['w-1'] },
454
+ results: [{
455
+ id: 'V-254554',
456
+ description: 'controller manager group',
457
+ checks: [{
458
+ id: 'V-254554',
459
+ description: 'use-service-account-credentials',
460
+ audit: '/bin/ps -fC kube-controller-manager',
461
+ remediation: 'set use-service-account-credentials=true',
462
+ scored: true,
463
+ state: 'pass' as const,
464
+ }],
465
+ }],
466
+ };
467
+ const xml = generateXCCDFPerNode({
468
+ report, benchmarkVersion: 'rke2-stig-1.31-rgs', hostname: 'w-1', role: 'node',
469
+ });
470
+
471
+ expect(xml).toContain('<Group id="V-254554">');
472
+ expect(xml).toContain('<check-content>/bin/ps -fC kube-controller-manager</check-content>');
473
+ expect(xml).toContain('set use-service-account-credentials=true');
474
+ });
475
+
476
+ it('passes through non-mixed states unchanged', () => {
477
+ const report = {
478
+ ...multiNodeReport,
479
+ results: [{
480
+ id: '4.1',
481
+ description: 'g',
482
+ checks: [
483
+ {
484
+ id: 'a', description: 'a', state: 'pass' as const
485
+ },
486
+ {
487
+ id: 'b', description: 'b', state: 'fail' as const
488
+ },
489
+ {
490
+ id: 'c', description: 'c', state: 'skip' as const
491
+ },
492
+ {
493
+ id: 'd', description: 'd', state: 'warn' as const
494
+ },
495
+ {
496
+ id: 'e', description: 'e', state: 'notApplicable' as const
497
+ },
498
+ ],
499
+ }],
500
+ };
501
+ const xml = generateXCCDFPerNode({
502
+ report, benchmarkVersion: 'cis-1.7', hostname: 'w-1', role: 'node',
503
+ });
504
+
505
+ expect(xml).toContain('<result>pass</result>');
506
+ expect(xml).toContain('<result>fail</result>');
507
+ expect(xml).toContain('<result>notselected</result>');
508
+ expect(xml).toContain('<result>informational</result>');
509
+ expect(xml).toContain('<result>notapplicable</result>');
510
+ });
511
+ });