NVIDIA / NVIDIA/cluster-readiness-engine
Close the env-laundering class in TestAttestPredicateUsesOnlyTrustedContext with a sentinel harness
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 55
- Forks
- 19
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 107
Description
Part of #262. Follow-up to #301, gated in #308. Size: S.
Problem
TestAttestPredicateUsesOnlyTrustedContext (test/releasepolicy/attest_boundary_test.go:105) keeps caller-controlled values out of the provenance origin fields two ways, and both share one blind spot.
- A static scan over
step.MergedEnvandstep.Runforinputs.,inputs[,needs.*.outputs.*andneeds[(attest_boundary_test.go:113-129). It reads workflow, job and step env for the provenance step only. It never sees an earlier step in the same job writing to$GITHUB_ENV. - Executing the extracted run block and asserting the emitted origin fields (
attest_boundary_test.go:141onward). The harness sets a fixed trusted set:CALLER_WORKFLOW_REF,GITHUB_REPOSITORY,GITHUB_REF,GITHUB_SHA,GITHUB_SERVER_URL,GITHUB_RUN_ID,SUBJECT_KIND(attest_boundary_test.go:158-166). Any other variable the run block references is left unset, so${VAR:-}expands empty and the exact-value assertions still pass.
Both miss the same thing. Adding this to the attest job ahead of the provenance step at attest.yml:463:
- name: Stage build metadata
env:
X: ${{ inputs.subject_name }}
run: |
set -euo pipefail
echo "SNEAKY=${X}" >> "$GITHUB_ENV"
and folding it into attest.yml:489 as --arg repo "${GITHUB_REPOSITORY}${SNEAKY:-}" leaves test/releasepolicy, test/docspolicy and test/helm all green. Running that same extracted step with SNEAKY set the way a real run would set it gives:
externalParameters.repository = NVIDIA/cluster-readiness-engine/../attacker-controlled
The needle list closes the two syntaxes demonstrated on #308 rather than the class behind them, so every new laundering route needs a new needle.
Scope
Add a sentinel pass to the executed half of the test, and treat it as the primary control rather than the scan:
- Parse the provenance step's run block for every shell variable it references.
- Set each one outside the trusted set to a unique sentinel value, rather than leaving it unset.
- Execute, then assert the sentinel reaches none of the origin fields:
externalParameters.repository,externalParameters.ref,resolvedDependencies[].uri,resolvedDependencies[].digest.gitCommit,runDetails.builder.id,runDetails.metadata.invocationId. - Keep
externalParameters.subjectKindcarved out on the same reasoning as today. It is a caller-chosen, enum-validated pass-through that shapes the predicate without claiming origin.
One detail to watch: the harness builds its environment with append(os.Environ(), ...), so the sentinel pass should start from a clean environment. Otherwise an inherited value can stand in for a variable the test meant to control.
This closes the class however the value arrives, whether through $GITHUB_ENV from an earlier step, a second jq writer, or an expression syntax nobody has thought of yet.
Acceptance criteria
- A
$GITHUB_ENVwrite from an earlier step in theattestjob, folded into any origin field, failstest/releasepolicy. - The two forms gated in #308,
inputs['subject_name']in the provenance step env andneeds['validate'].outputs.*, still fail. -
attest.ymlas it stands onmainstill passes, withexternalParameters.subjectKindstill driven by the caller.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with TestAttestPredicateUsesOnlyTrustedContext in test/releasepolicy/attest_boundary_test.go, especially its environment setup and executed run-block assertions, then inspect the provenance step at attest.yml:463 and :489. Run the existing release-policy tests before and after the change. Done means sentinel values from every non-trusted shell variable cannot reach the listed origin fields, while the existing gated cases fail and attest.yml on main still passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bash, go, yaml
- Domain
- ci-cd, security, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100