You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There is no integrity binding between a generated recipe artifact and the signed catalog it was supposedly derived from. A recipe altered between aicr recipe -o recipe.yaml and aicr bundle -r recipe.yaml --attest is bundled and attested as-is. The bundle attestation records only the criteria label and the checksums.txt digest, never the recipe content or a re-derivation against the catalog, so a tampered recipe produces a structurally valid attestation over a tampered bundle.
This is the recipe-artifact peer of the recipe-data provenance shipped in #1155 (signed catalog/registry). #1155 protects the inputs to recipe generation; this issue covers the gap that the generated recipe is trusted verbatim by the bundler with no link back to that signed catalog.
Surfaced while reviewing #1216. Filed standalone and attached to #1149 per the epic's "new gaps surfaced while implementing should be filed as standalone issues" note.
Why this matters (security-meaningful edit vectors)
The recipe is the build contract for the bundle, and several ComponentRef fields (pkg/recipe/metadata.go) flow verbatim into deployer output with no re-validation against the registry:
Source (chart repo / OCI reference) — becomes the repository the cluster pulls from at deploy time: Repository: ref.Source (pkg/bundler/deployer/helmfile/helmfile.go:346), HelmRepository.URL: ref.Source (pkg/bundler/deployer/flux/sources.go:64-68). Repointing gpu-operator's Source to oci://attacker.io/charts makes the generated ArgoCD/Flux/Helmfile artifact pull the operator chart from an attacker.
Overrides (map[string]any, arbitrary Helm values, highest merge precedence) — image.repository/image.tag, securityContext.privileged: true, hostPath/hostPID/hostNetwork, serviceAccount/RBAC toggles, bounded only by the chart schema (wide for these charts).
Version / Tag — downgrade a chart/image to a known-CVE version or pin a malicious tag.
PreManifestFiles / ManifestFiles — raw manifests embedded and applied at sync-wave N-1 (before the chart); with --data the caller controls the data dir, enabling injection of a privileged DaemonSet or ClusterRoleBinding that lands first. (Path traversal out of the data root is already blocked.)
Namespace / Path — retarget into a namespace with relaxed PodSecurity, or (Kustomize) a different kustomization path.
What constrains this today (and why it is not sufficient)
The only recipe-vs-policy check is WithAllowLists, which validates criteria (service/accelerator/intent/os/platform), not Source/Version/Overrides (pkg/bundler/bundler.go:93-95).
The bundle attestation signs checksums.txt, which covers the emitted (tampered) values, but only under the editor's signing identity. Identity-pinned aicr verify rejects a non-NVIDIA/aicr signer, and --data caps trust at attested rather than verified.
So an external attacker cannot launder a tampered recipe into a "verified" NVIDIA bundle. The residual gap: nothing recomputes the recipe from (criteria + signed catalog) and compares, so trust rests entirely on the signer identity, not on recipe correctness. A tamper inside an otherwise-trusted pipeline (between recipe and bundle) still verifies clean, and the recipe content is not even captured as a digest in the attestation.
Proposed options (not mutually exclusive)
Record a recipe-content digest as a resolvedDependency in the bundle attestation, so a verifier can detect divergence. Cheapest; detection only.
Re-derive at bundle time — have the bundler regenerate the recipe from criteria against the verified catalog and reject -r inputs that do not match. Strongest; this is exactly what feat: recipe-data provenance (signed catalog/registry) #1155 (signed catalog) makes possible (signed catalog + deterministic generation = verifier can recompute the expected recipe).
Summary
There is no integrity binding between a generated recipe artifact and the signed catalog it was supposedly derived from. A recipe altered between
aicr recipe -o recipe.yamlandaicr bundle -r recipe.yaml --attestis bundled and attested as-is. The bundle attestation records only the criteria label and thechecksums.txtdigest, never the recipe content or a re-derivation against the catalog, so a tampered recipe produces a structurally valid attestation over a tampered bundle.This is the recipe-artifact peer of the recipe-data provenance shipped in #1155 (signed catalog/registry). #1155 protects the inputs to recipe generation; this issue covers the gap that the generated recipe is trusted verbatim by the bundler with no link back to that signed catalog.
Surfaced while reviewing #1216. Filed standalone and attached to #1149 per the epic's "new gaps surfaced while implementing should be filed as standalone issues" note.
Why this matters (security-meaningful edit vectors)
The recipe is the build contract for the bundle, and several
ComponentReffields (pkg/recipe/metadata.go) flow verbatim into deployer output with no re-validation against the registry:Source(chart repo / OCI reference) — becomes the repository the cluster pulls from at deploy time:Repository: ref.Source(pkg/bundler/deployer/helmfile/helmfile.go:346),HelmRepository.URL: ref.Source(pkg/bundler/deployer/flux/sources.go:64-68). Repointinggpu-operator'sSourcetooci://attacker.io/chartsmakes the generated ArgoCD/Flux/Helmfile artifact pull the operator chart from an attacker.Overrides(map[string]any, arbitrary Helm values, highest merge precedence) —image.repository/image.tag,securityContext.privileged: true, hostPath/hostPID/hostNetwork, serviceAccount/RBAC toggles, bounded only by the chart schema (wide for these charts).Version/Tag— downgrade a chart/image to a known-CVE version or pin a malicious tag.PreManifestFiles/ManifestFiles— raw manifests embedded and applied at sync-wave N-1 (before the chart); with--datathe caller controls the data dir, enabling injection of a privileged DaemonSet orClusterRoleBindingthat lands first. (Path traversal out of the data root is already blocked.)Namespace/Path— retarget into a namespace with relaxed PodSecurity, or (Kustomize) a different kustomization path.What constrains this today (and why it is not sufficient)
WithAllowLists, which validates criteria (service/accelerator/intent/os/platform), notSource/Version/Overrides(pkg/bundler/bundler.go:93-95).checksums.txt, which covers the emitted (tampered) values, but only under the editor's signing identity. Identity-pinnedaicr verifyrejects a non-NVIDIA/aicrsigner, and--datacaps trust atattestedrather thanverified.So an external attacker cannot launder a tampered recipe into a "verified" NVIDIA bundle. The residual gap: nothing recomputes the recipe from (criteria + signed catalog) and compares, so trust rests entirely on the signer identity, not on recipe correctness. A tamper inside an otherwise-trusted pipeline (between
recipeandbundle) still verifies clean, and the recipe content is not even captured as a digest in the attestation.Proposed options (not mutually exclusive)
resolvedDependencyin the bundle attestation, so a verifier can detect divergence. Cheapest; detection only.-rinputs that do not match. Strongest; this is exactly what feat: recipe-data provenance (signed catalog/registry) #1155 (signed catalog) makes possible (signed catalog + deterministic generation = verifier can recompute the expected recipe).recipe attest/recipe verifypair mirroring feat: recipe-data provenance (signed catalog/registry) #1155.Acceptance criteria (draft)
aicr verifytime (or refused at bundle time), independent of the signer identity.make test/make lintand docs updated.Out of scope
Related: #1149 (epic), #1155 (signed catalog), #1216 (PR that surfaced this).