Skip to content

fix(bundler): redact the flag value when the CRD step rejects it - #2856

Merged
mchmarny merged 1 commit into
mainfrom
fix/crd-step-error-redacts-flag-value
Sep 19, 2026
Merged

mchmarny merged 1 commit into
mainfrom
fix/crd-step-error-redacts-flag-value

Conversation

@mchmarny

Copy link
Copy Markdown
Member

Summary

The CRD step's catch-all echoed the whole rejected token. helm's --kube-token
carries a bearer token in its joined form, so --kube-token=<bearer> was
printed verbatim. Print the option name only.

Motivation / Context

Follow-up to review feedback on #2849
(#2849 (review)), raised
as non-blocking and deferred to a separate PR.

deploy.sh runs install.sh with its output attached to the terminal and to CI
logs, inside a retry loop, so a disclosed token is both logged and repeated.
kubectl reports an unknown flag by name alone (error: unknown flag: --kube-token), so the pre-#2849 path never disclosed the value — this is a
regression #2849 introduced, not an inherited gap.

Fixes: N/A
Related: #2849

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Build/CI/tooling

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • API server (cmd/aicrd, pkg/server)
  • Recipe engine / data (pkg/recipe)
  • Bundlers (pkg/bundler, pkg/component/*)
  • Collectors / snapshotter (pkg/collector, pkg/snapshotter)
  • Validator (pkg/validator)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)

Implementation Notes

Only the joined form leaked. The separated form (--kube-token SECRET)
already stopped on the flag name, exiting before the value was reached. Both are
covered by tests regardless, since the asymmetry is not obvious from the code.

The message also misstated its own reason. "No known kubectl spelling" is
false: --kube-token is --token, --kube-apiserver is --server,
--kube-ca-file is --certificate-authority. Not translating them is a scope
decision. An error that misstates its reason sends a reader looking for a flag
that exists, so it now says the step does not support them. The generated bundle
README matches.

The GatesAndBounds pin was repointed, not dropped. The wording change broke
its old pin. It now pins ${helm_conn[0]%%=*}, so removing the redaction fails
the unit suite rather than only the new row.

Testing

make qualify   # exit 0, 8m37s
Stage Result
Coverage 84.8% (threshold 83%)
Tests (-race) no failures
e2e / chainsaw 29 passed, 0 failed, 0 skipped
api-diff no incompatible SDK changes since v0.21.0
openapi-diff no unacknowledged REST breaking changes

Rendered script, all three shapes — no value in any:

KUBECONFIG_FLAG Output
--kube-token=SUPERSECRET carries '--kube-token'
--kube-token SUPERSECRET carries '--kube-token'
--kube-apiserver=https://secret.internal carries '--kube-apiserver'

Two new rows in TestApplyCRDsScript_TranslatesHelmConnectionFlags assert the
marker value is absent from captured output; the existing rejection rows checked
exit status only. Mutation-verified: restoring ${helm_conn[0]} fails the
joined row.

No production Go code changed — the only .go edit is test assertions.

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert
  • Medium — Touches multiple components or has broader impact
  • High — Breaking change, affects critical paths, or complex rollout

Rollout notes: None. Operators who hit the old message see the same failure
with the argument removed.

Checklist

  • Tests pass locally (make test with -race)
  • Linter passes (make lint)
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality
  • I updated docs if user-facing behavior changed (generated bundle README)
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)
@mchmarny
mchmarny requested a review from a team as a code owner September 19, 2026 01:28
@mchmarny mchmarny added the theme/deployer Helm, ArgoCD, and deployment bundle generation label Sep 19, 2026
@mchmarny mchmarny self-assigned this Sep 19, 2026
@mchmarny
mchmarny marked this pull request as draft September 19, 2026 01:28
@github-actions

Copy link
Copy Markdown
Contributor

Recipe evidence check

No leaf overlays affected by this PR.

This gate is warning-only and never blocks merge.

@coderabbitai

coderabbitai Bot commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/aicr/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 2a2fa022-501a-405f-8c03-8d6c362b9505

📥 Commits

Reviewing files that changed from the base of the PR and between e69aac2 and 4c9ce35.

📒 Files selected for processing (5)
  • pkg/bundler/deployer/helm/testdata/owns_crds/001-k8s-aibom/apply-crds.sh
  • pkg/bundler/deployer/localformat/templates/apply-crds.sh.tmpl
  • pkg/bundler/deployer/localformat/testdata/apply_crds_upstream/001-k8s-aibom/apply-crds.sh
  • pkg/bundler/deployer/localformat/testdata/apply_crds_vendored/001-k8s-aibom/apply-crds.sh
  • pkg/bundler/testdata/stock_render_golden.yaml

Included review availability: Your plan provides up to 12 included reviews per hour; 10 remain after this review.


📝 Walkthrough

Walkthrough

The CRD application template now reports only unsupported option names and omits option values from diagnostics. Tests cover joined and separated secret values and verify that sensitive values do not appear in generated output. Rendered scripts and Helm documentation describe support for only --kube-context and --kubeconfig. Stock render SHA-256 goldens were regenerated.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: lockwobr

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: redacting rejected Helm flag values in the CRD step.
Description check ✅ Passed The description directly explains the redaction fix, its security motivation, affected bundler behavior, tests, and documentation updates.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

@mchmarny this PR now has merge conflicts with main. Please rebase to resolve them.

The catch-all echoed the whole token. helm's --kube-token carries a
bearer token in its joined form, and deploy.sh runs install.sh with
output attached to the terminal and to CI logs, then retries it, so
`--kube-token=<bearer>` was printed verbatim and repeated.

kubectl reports an unknown flag by name alone, so the unpatched path
never disclosed the value. Translating the flags introduced this, which
makes it a regression of the change that added the parser rather than
anything inherited.

Print `${helm_conn[0]%%=*}`: the option, never its argument. The
separated form already stopped on the flag name, so only the joined
form leaked; both are covered now.

Correct the reason the message gives, in all three places it appears.
Several rejected flags do have kubectl equivalents -- --kube-token is
--token, --kube-apiserver is --server -- so "no known kubectl spelling"
was false in the message, in the block comment above the parser, and in
the generated README. Not translating them is a scope boundary, and an
error that misstates its own reason sends a reader looking for a flag
that exists.

Name the supported spellings in the error too. An operator reading it
in a CI log otherwise has to find the bundle README to learn what to
put in KUBECONFIG_FLAG instead. "Refuses to guess" is dropped with the
ambiguity framing it belonged to.

The rejection rows asserted exit status only, so nothing guarded the
output; they now also assert the argument is absent. Verified by
restoring the unredacted expansion, which fails the joined row. The
separated row guards a different future change: word-splitting already
keeps the value out of helm_conn[0], so it holds either way.

Signed-off-by: Mark Chmarny <mark@chmarny.com>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report ✅

Metric Value
Coverage 84.8%
Threshold 83%
Status Pass
Coverage Badge
![Coverage](https://img.shields.io/badge/coverage-84.8%25-brightgreen)

No Go source files changed in this PR.

@mchmarny
mchmarny force-pushed the fix/crd-step-error-redacts-flag-value branch from e69aac2 to 4c9ce35 Compare September 19, 2026 01:43
@github-actions github-actions Bot added size/XL and removed size/L labels Sep 19, 2026
@mchmarny
mchmarny marked this pull request as ready for review September 19, 2026 01:43
@mchmarny
mchmarny enabled auto-merge (squash) September 19, 2026 01:57

@lockwobr lockwobr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mchmarny
mchmarny merged commit ebddbb2 into main Sep 19, 2026
77 checks passed
@mchmarny
mchmarny deleted the fix/crd-step-error-redacts-flag-value branch September 19, 2026 01:58
lockwobr added a commit that referenced this pull request Sep 19, 2026
deploy.sh exported KUBECONFIG_FLAG for helm but passed no context to its
own kubectl calls. With KUBECONFIG_FLAG="--kube-context prod-b" and an
ambient context of prod-a, helm installed releases into prod-b while
deploy.sh deleted Jobs, removed node taints and restarted DaemonSets on
prod-a. Those call sites are 2>/dev/null || true, so nothing reported it.

Callers now export KUBE_CONTEXT and KUBECONFIG, and each generated script
renders the flag its own binary spells. The prologue lives once and is
rendered into deploy.sh, both install.sh templates and apply-crds.sh,
because each is a documented standalone entry point and cannot depend on
a sibling file written by whichever deployer assembled the bundle.

KUBECONFIG_FLAG is still accepted and translated, with a warning. An
option it does not translate, a missing or option-shaped value, an empty
joined value, or a context disagreeing with KUBE_CONTEXT all exit before
the first cluster call: a dropped connection option is indistinguishable
from one never set, and the fallback is the ambient context. Rejection
messages name the option but never its argument, so a flag carrying a
credential does not reach the log.

Related: #2849, #2856
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 22, 2026
deploy.sh exported KUBECONFIG_FLAG for helm but passed no context to its
own kubectl calls. With KUBECONFIG_FLAG="--kube-context prod-b" and an
ambient context of prod-a, helm installed releases into prod-b while
deploy.sh deleted Jobs, removed node taints and restarted DaemonSets on
prod-a. Those call sites are 2>/dev/null || true, so nothing reported it.

Callers now export KUBE_CONTEXT and KUBECONFIG, and each generated script
renders the flag its own binary spells. The prologue lives once and is
rendered into deploy.sh, both install.sh templates and apply-crds.sh,
because each is a documented standalone entry point and cannot depend on
a sibling file written by whichever deployer assembled the bundle.

KUBECONFIG_FLAG is still accepted and translated, with a warning. An
option it does not translate, a missing or option-shaped value, an empty
joined value, or a context disagreeing with KUBE_CONTEXT all exit before
the first cluster call: a dropped connection option is indistinguishable
from one never set, and the fallback is the ambient context. Rejection
messages name the option but never its argument, so a flag carrying a
credential does not reach the log.

Related: #2849, #2856
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 22, 2026
deploy.sh exported KUBECONFIG_FLAG for helm but passed no context to its
own kubectl calls. With KUBECONFIG_FLAG="--kube-context prod-b" and an
ambient context of prod-a, helm installed releases into prod-b while
deploy.sh deleted Jobs, removed node taints and restarted DaemonSets on
prod-a. Those call sites are 2>/dev/null || true, so nothing reported it.

Callers now export KUBE_CONTEXT and KUBECONFIG, and each generated script
renders the flag its own binary spells. The prologue lives once and is
rendered into deploy.sh, both install.sh templates and apply-crds.sh,
because each is a documented standalone entry point and cannot depend on
a sibling file written by whichever deployer assembled the bundle.

KUBECONFIG_FLAG is still accepted and translated, with a warning. An
option it does not translate, a missing or option-shaped value, an empty
joined value, or a context disagreeing with KUBE_CONTEXT all exit before
the first cluster call: a dropped connection option is indistinguishable
from one never set, and the fallback is the ambient context. Rejection
messages name the option but never its argument, so a flag carrying a
credential does not reach the log.

Related: #2849, #2856
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 23, 2026
deploy.sh exported KUBECONFIG_FLAG for helm but passed no context to its
own kubectl calls. With KUBECONFIG_FLAG="--kube-context prod-b" and an
ambient context of prod-a, helm installed releases into prod-b while
deploy.sh deleted Jobs, removed node taints and restarted DaemonSets on
prod-a. Those call sites are 2>/dev/null || true, so nothing reported it.

Callers now export KUBE_CONTEXT and KUBECONFIG, and each generated script
renders the flag its own binary spells. The prologue lives once and is
rendered into deploy.sh, both install.sh templates and apply-crds.sh,
because each is a documented standalone entry point and cannot depend on
a sibling file written by whichever deployer assembled the bundle.

KUBECONFIG_FLAG is still accepted and translated, with a warning. An
option it does not translate, a missing or option-shaped value, an empty
joined value, or a context disagreeing with KUBE_CONTEXT all exit before
the first cluster call: a dropped connection option is indistinguishable
from one never set, and the fallback is the ambient context. Rejection
messages name the option but never its argument, so a flag carrying a
credential does not reach the log.

Related: #2849, #2856
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
lockwobr added a commit that referenced this pull request Sep 23, 2026
deploy.sh exported KUBECONFIG_FLAG for helm but passed no context to its
own kubectl calls. With KUBECONFIG_FLAG="--kube-context prod-b" and an
ambient context of prod-a, helm installed releases into prod-b while
deploy.sh deleted Jobs, removed node taints and restarted DaemonSets on
prod-a. Those call sites are 2>/dev/null || true, so nothing reported it.

Callers now export KUBE_CONTEXT and KUBECONFIG, and each generated script
renders the flag its own binary spells. The prologue lives once and is
rendered into deploy.sh, both install.sh templates and apply-crds.sh,
because each is a documented standalone entry point and cannot depend on
a sibling file written by whichever deployer assembled the bundle.

KUBECONFIG_FLAG is still accepted and translated, with a warning. An
option it does not translate, a missing or option-shaped value, an empty
joined value, or a context disagreeing with KUBE_CONTEXT all exit before
the first cluster call: a dropped connection option is indistinguishable
from one never set, and the fallback is the ambient context. Rejection
messages name the option but never its argument, so a flag carrying a
credential does not reach the log.

Related: #2849, #2856
Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/bundler needs-rebase size/XL theme/deployer Helm, ArgoCD, and deployment bundle generation

2 participants