From 483840669cb167fd875bea46bbe5a9506d0379b8 Mon Sep 17 00:00:00 2001 From: Behnam Mozafari Date: Tue, 12 May 2026 12:18:51 +1000 Subject: [PATCH 1/5] UID2-6764: add attestation_enabled opt-out to docker publish workflows Adds a boolean attestation_enabled input (default true) on the attest_image composite and pipes it through both shared docker publish workflows + the non-Java composite. Callers opt out with one line (attestation_enabled: false) instead of granting id-token / attestations / artifact-metadata permissions they don't otherwise need. Includes a throwaway .github/workflows/test-attest-opt-out.yaml that exercises both paths against a built-inline alpine image and asserts via external gh attestation verify that: - default: attestation exists, verify exits 0 - opt-out: no attestation exists, verify fails with "no attestations" Co-Authored-By: Claude Opus 4.7 (1M context) --- ...ared-publish-java-to-docker-versioned.yaml | 5 + .../shared-publish-to-docker-versioned.yaml | 5 + .github/workflows/test-attest-opt-out.yaml | 127 ++++++++++++++++++ actions/attest_image/action.yaml | 7 + actions/shared_publish_to_docker/action.yaml | 4 + 5 files changed, 148 insertions(+) create mode 100644 .github/workflows/test-attest-opt-out.yaml diff --git a/.github/workflows/shared-publish-java-to-docker-versioned.yaml b/.github/workflows/shared-publish-java-to-docker-versioned.yaml index 813ef6cc..c24dae14 100644 --- a/.github/workflows/shared-publish-java-to-docker-versioned.yaml +++ b/.github/workflows/shared-publish-java-to-docker-versioned.yaml @@ -42,6 +42,10 @@ on: description: GitHub Environment to use for accessing the merge token. Leave empty to use the default GITHUB_TOKEN. type: string default: '' + attestation_enabled: + description: If false, skips SLSA build-provenance attestation + verification. Defaults to true. + type: boolean + default: true outputs: version_number_output: description: The complete version number @@ -223,6 +227,7 @@ jobs: with: subject_name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ inputs.append_image_name }} subject_digest: ${{ steps.push.outputs.digest }} + attestation_enabled: ${{ inputs.attestation_enabled }} - name: Build Changelog id: github_release diff --git a/.github/workflows/shared-publish-to-docker-versioned.yaml b/.github/workflows/shared-publish-to-docker-versioned.yaml index 559b1d94..769cc7ff 100644 --- a/.github/workflows/shared-publish-to-docker-versioned.yaml +++ b/.github/workflows/shared-publish-to-docker-versioned.yaml @@ -42,6 +42,10 @@ on: description: If true, will attempt to publish any vulnerabilities to GitHub. Defaults to true. Set to false for private repos. type: string default: 'true' + attestation_enabled: + description: If false, skips SLSA build-provenance attestation + verification. Defaults to true. + type: boolean + default: true jobs: buildImage: name: Build Image @@ -77,6 +81,7 @@ jobs: docker_context: ${{ inputs.docker_context }} publish_vulnerabilities: ${{ inputs.publish_vulnerabilities }} scan_type: image + attestation_enabled: ${{ inputs.attestation_enabled }} - name: Create Release id: github_release diff --git a/.github/workflows/test-attest-opt-out.yaml b/.github/workflows/test-attest-opt-out.yaml new file mode 100644 index 00000000..50f53909 --- /dev/null +++ b/.github/workflows/test-attest-opt-out.yaml @@ -0,0 +1,127 @@ +name: Smoke test attest_image opt-out + +# Mirrors run 25542801315 — builds a throwaway alpine image inline and exercises +# the attest_image composite directly. Two jobs: +# 1. default-enabled: omits attestation_enabled, expects an attestation to exist + verify to succeed +# 2. opted-out: passes attestation_enabled=false, expects NO attestation to exist +# Uses push: trigger so it can fire before the workflow file exists on main +# (workflow_dispatch API requires the workflow on the default branch). +# Delete this file after evidence is captured — the run ID is permanent. + +on: + push: + branches: + - bmz-UID2-6764-attestation-opt-out + +permissions: + contents: read + packages: write + id-token: write + attestations: write + artifact-metadata: write + +env: + IMAGE_BASE: ghcr.io/iabtechlab/uid2-shared-actions + +jobs: + default-enabled: + name: Default (attestation_enabled omitted) — expect attestation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare context + run: | + mkdir -p ${{ runner.temp }}/ctx-default + cat > ${{ runner.temp }}/ctx-default/Dockerfile < /tag.txt + EOF + + - name: Build + push throwaway image + id: build + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: ${{ runner.temp }}/ctx-default + push: true + tags: ${{ env.IMAGE_BASE }}/test-attest-optout-default:${{ github.run_id }} + + - name: Call attest_image (default) + uses: ./actions/attest_image + with: + subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-default + subject_digest: ${{ steps.build.outputs.digest }} + + - name: External verify — expect SUCCESS + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh attestation verify \ + "oci://${{ env.IMAGE_BASE }}/test-attest-optout-default@${{ steps.build.outputs.digest }}" \ + --owner IABTechLab \ + --signer-repo IABTechLab/uid2-shared-actions + echo "Default behavior confirmed: attestation present, verify succeeded." + + opted-out: + name: attestation_enabled=false — expect no attestation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare context + run: | + mkdir -p ${{ runner.temp }}/ctx-optout + cat > ${{ runner.temp }}/ctx-optout/Dockerfile < /tag.txt + EOF + + - name: Build + push throwaway image + id: build + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: ${{ runner.temp }}/ctx-optout + push: true + tags: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled:${{ github.run_id }} + + - name: Call attest_image (attestation_enabled=false) + uses: ./actions/attest_image + with: + subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled + subject_digest: ${{ steps.build.outputs.digest }} + attestation_enabled: 'false' + + - name: External verify — expect FAILURE (no attestations) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + output=$(gh attestation verify \ + "oci://${{ env.IMAGE_BASE }}/test-attest-optout-disabled@${{ steps.build.outputs.digest }}" \ + --owner IABTechLab \ + --signer-repo IABTechLab/uid2-shared-actions 2>&1) + rc=$? + echo "$output" + if [ "$rc" -eq 0 ]; then + echo "::error::Expected verify to fail (no attestations) but it succeeded — opt-out did not skip the attest step." + exit 1 + fi + if ! echo "$output" | grep -qi "no attestations"; then + echo "::error::Expected 'no attestations' in failure output; got a different error." + exit 1 + fi + echo "Opt-out confirmed: no attestation present, verify failed as expected." diff --git a/actions/attest_image/action.yaml b/actions/attest_image/action.yaml index dad23376..d5967034 100644 --- a/actions/attest_image/action.yaml +++ b/actions/attest_image/action.yaml @@ -11,11 +11,16 @@ inputs: subject_digest: description: OCI manifest digest (sha256:...) emitted by docker/build-push-action. required: true + attestation_enabled: + description: Set to 'false' to skip attestation + verification. Default 'true'. + required: false + default: 'true' runs: using: composite steps: - name: Lowercase image reference + if: ${{ inputs.attestation_enabled == 'true' }} id: ref shell: bash run: | @@ -23,6 +28,7 @@ runs: echo "value=${value}" >> "$GITHUB_OUTPUT" - name: Attest build provenance + if: ${{ inputs.attestation_enabled == 'true' }} uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 env: # Mirrors actions/attest-build-provenance, prevents oversized OCI registry auth-challenge headers triggering HPE_HEADER_OVERFLOW. @@ -33,6 +39,7 @@ runs: push-to-registry: true - name: Verify attestation + if: ${{ inputs.attestation_enabled == 'true' }} shell: bash env: GH_TOKEN: ${{ github.token }} diff --git a/actions/shared_publish_to_docker/action.yaml b/actions/shared_publish_to_docker/action.yaml index 096acd5f..5636ad48 100644 --- a/actions/shared_publish_to_docker/action.yaml +++ b/actions/shared_publish_to_docker/action.yaml @@ -32,6 +32,9 @@ inputs: scan_type: description: The scan-type for aquasecurity/trivy-action action. Default to a fs scan. default: fs + attestation_enabled: + description: Set to 'false' to skip SLSA build-provenance attestation + verification. Default 'true'. + default: 'true' outputs: tags: @@ -108,3 +111,4 @@ runs: with: subject_name: ${{ inputs.docker_registry }}/${{ inputs.docker_image_name }} subject_digest: ${{ steps.push.outputs.digest }} + attestation_enabled: ${{ inputs.attestation_enabled }} From c5beb6aabd29e5fca6df765f04ee923f70f2b395 Mon Sep 17 00:00:00 2001 From: Behnam Mozafari Date: Tue, 12 May 2026 12:20:36 +1000 Subject: [PATCH 2/5] =?UTF-8?q?UID2-6764:=20fix=20opt-out=20smoke=20assert?= =?UTF-8?q?ion=20=E2=80=94=20gh=20verify=20returns=20HTTP=20404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty-attestation lookup returns "HTTP 404: Not Found" against the GitHub attestations API, not "no attestations found". Accept either. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-attest-opt-out.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-attest-opt-out.yaml b/.github/workflows/test-attest-opt-out.yaml index 50f53909..a198de80 100644 --- a/.github/workflows/test-attest-opt-out.yaml +++ b/.github/workflows/test-attest-opt-out.yaml @@ -120,8 +120,10 @@ jobs: echo "::error::Expected verify to fail (no attestations) but it succeeded — opt-out did not skip the attest step." exit 1 fi - if ! echo "$output" | grep -qi "no attestations"; then - echo "::error::Expected 'no attestations' in failure output; got a different error." + # gh attestation verify against a digest with no attestation returns: + # Error: HTTP 404: Not Found (https://api.github.com/orgs//attestations/...) + if ! echo "$output" | grep -qE "HTTP 404|no attestations"; then + echo "::error::Expected 'HTTP 404' or 'no attestations' in failure output; got a different error." exit 1 fi echo "Opt-out confirmed: no attestation present, verify failed as expected." From 1e58e0688536ae2a7f19c50a3aee8209fc1476b3 Mon Sep 17 00:00:00 2001 From: Behnam Mozafari Date: Tue, 12 May 2026 12:22:14 +1000 Subject: [PATCH 3/5] UID2-6764: delete throwaway attest_image opt-out smoke workflow Evidence permanent in run 25709336298 (both jobs green). Re-add ad-hoc if a future change to attest_image needs re-validation, mirroring the UID2-6764 precedent (test-attest-image.yaml lived only long enough to capture run 25542801315). Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-attest-opt-out.yaml | 129 --------------------- 1 file changed, 129 deletions(-) delete mode 100644 .github/workflows/test-attest-opt-out.yaml diff --git a/.github/workflows/test-attest-opt-out.yaml b/.github/workflows/test-attest-opt-out.yaml deleted file mode 100644 index a198de80..00000000 --- a/.github/workflows/test-attest-opt-out.yaml +++ /dev/null @@ -1,129 +0,0 @@ -name: Smoke test attest_image opt-out - -# Mirrors run 25542801315 — builds a throwaway alpine image inline and exercises -# the attest_image composite directly. Two jobs: -# 1. default-enabled: omits attestation_enabled, expects an attestation to exist + verify to succeed -# 2. opted-out: passes attestation_enabled=false, expects NO attestation to exist -# Uses push: trigger so it can fire before the workflow file exists on main -# (workflow_dispatch API requires the workflow on the default branch). -# Delete this file after evidence is captured — the run ID is permanent. - -on: - push: - branches: - - bmz-UID2-6764-attestation-opt-out - -permissions: - contents: read - packages: write - id-token: write - attestations: write - artifact-metadata: write - -env: - IMAGE_BASE: ghcr.io/iabtechlab/uid2-shared-actions - -jobs: - default-enabled: - name: Default (attestation_enabled omitted) — expect attestation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Log in to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Prepare context - run: | - mkdir -p ${{ runner.temp }}/ctx-default - cat > ${{ runner.temp }}/ctx-default/Dockerfile < /tag.txt - EOF - - - name: Build + push throwaway image - id: build - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 - with: - context: ${{ runner.temp }}/ctx-default - push: true - tags: ${{ env.IMAGE_BASE }}/test-attest-optout-default:${{ github.run_id }} - - - name: Call attest_image (default) - uses: ./actions/attest_image - with: - subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-default - subject_digest: ${{ steps.build.outputs.digest }} - - - name: External verify — expect SUCCESS - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh attestation verify \ - "oci://${{ env.IMAGE_BASE }}/test-attest-optout-default@${{ steps.build.outputs.digest }}" \ - --owner IABTechLab \ - --signer-repo IABTechLab/uid2-shared-actions - echo "Default behavior confirmed: attestation present, verify succeeded." - - opted-out: - name: attestation_enabled=false — expect no attestation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Log in to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Prepare context - run: | - mkdir -p ${{ runner.temp }}/ctx-optout - cat > ${{ runner.temp }}/ctx-optout/Dockerfile < /tag.txt - EOF - - - name: Build + push throwaway image - id: build - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 - with: - context: ${{ runner.temp }}/ctx-optout - push: true - tags: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled:${{ github.run_id }} - - - name: Call attest_image (attestation_enabled=false) - uses: ./actions/attest_image - with: - subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled - subject_digest: ${{ steps.build.outputs.digest }} - attestation_enabled: 'false' - - - name: External verify — expect FAILURE (no attestations) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set +e - output=$(gh attestation verify \ - "oci://${{ env.IMAGE_BASE }}/test-attest-optout-disabled@${{ steps.build.outputs.digest }}" \ - --owner IABTechLab \ - --signer-repo IABTechLab/uid2-shared-actions 2>&1) - rc=$? - echo "$output" - if [ "$rc" -eq 0 ]; then - echo "::error::Expected verify to fail (no attestations) but it succeeded — opt-out did not skip the attest step." - exit 1 - fi - # gh attestation verify against a digest with no attestation returns: - # Error: HTTP 404: Not Found (https://api.github.com/orgs//attestations/...) - if ! echo "$output" | grep -qE "HTTP 404|no attestations"; then - echo "::error::Expected 'HTTP 404' or 'no attestations' in failure output; got a different error." - exit 1 - fi - echo "Opt-out confirmed: no attestation present, verify failed as expected." From ed7c5981f9be202be364db9947a24d9b2132cc71 Mon Sep 17 00:00:00 2001 From: Behnam Mozafari Date: Tue, 12 May 2026 12:50:53 +1000 Subject: [PATCH 4/5] UID2-6764: move opt-out gate from composite to caller if-conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit attest_image is just the implementation — opt-out is a caller concern. Drop the input on the composite; the existing callers already had to gate on not_snapshot, so adding && inputs.attestation_enabled (workflow) or && inputs.attestation_enabled == 'true' (composite) keeps the gate where it belongs. Re-adds the smoke workflow (deleted in the previous commit) to verify the new caller-side gating pattern end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...ared-publish-java-to-docker-versioned.yaml | 3 +- .github/workflows/test-attest-opt-out.yaml | 134 ++++++++++++++++++ actions/attest_image/action.yaml | 7 - actions/shared_publish_to_docker/action.yaml | 3 +- 4 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/test-attest-opt-out.yaml diff --git a/.github/workflows/shared-publish-java-to-docker-versioned.yaml b/.github/workflows/shared-publish-java-to-docker-versioned.yaml index c24dae14..c9020603 100644 --- a/.github/workflows/shared-publish-java-to-docker-versioned.yaml +++ b/.github/workflows/shared-publish-java-to-docker-versioned.yaml @@ -222,12 +222,11 @@ jobs: IMAGE_VERSION=${{ steps.version.outputs.new_version }} - name: Attest build provenance - if: ${{ steps.checkRelease.outputs.not_snapshot == 'true' }} + if: ${{ steps.checkRelease.outputs.not_snapshot == 'true' && inputs.attestation_enabled }} uses: IABTechLab/uid2-shared-actions/actions/attest_image@v3 with: subject_name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}${{ inputs.append_image_name }} subject_digest: ${{ steps.push.outputs.digest }} - attestation_enabled: ${{ inputs.attestation_enabled }} - name: Build Changelog id: github_release diff --git a/.github/workflows/test-attest-opt-out.yaml b/.github/workflows/test-attest-opt-out.yaml new file mode 100644 index 00000000..cb5eeb14 --- /dev/null +++ b/.github/workflows/test-attest-opt-out.yaml @@ -0,0 +1,134 @@ +name: Smoke test attest_image opt-out + +# Mirrors run 25542801315 — builds a throwaway alpine image inline and +# exercises the caller-side gating pattern (an `if:` on the attest_image +# step, matching exactly what the shared docker workflows do). Two jobs: +# 1. default-enabled: ATTEST_ENABLED=true, expect attestation present + verify exit 0 +# 2. opted-out: ATTEST_ENABLED=false, expect HTTP 404 from gh attestation verify +# Uses push: trigger so it can fire before the workflow file exists on main. +# Delete this file after evidence captured — run ID is permanent. + +on: + push: + branches: + - bmz-UID2-6764-attestation-opt-out + +permissions: + contents: read + packages: write + id-token: write + attestations: write + artifact-metadata: write + +env: + IMAGE_BASE: ghcr.io/iabtechlab/uid2-shared-actions + +jobs: + default-enabled: + name: Caller passes attestation_enabled=true — expect attestation + runs-on: ubuntu-latest + env: + ATTEST_ENABLED: 'true' + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare context + run: | + mkdir -p ${{ runner.temp }}/ctx-default + cat > ${{ runner.temp }}/ctx-default/Dockerfile < /tag.txt + EOF + + - name: Build + push throwaway image + id: build + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: ${{ runner.temp }}/ctx-default + push: true + tags: ${{ env.IMAGE_BASE }}/test-attest-optout-default:${{ github.run_id }} + + - name: Call attest_image (gated, expect runs) + if: ${{ env.ATTEST_ENABLED == 'true' }} + uses: ./actions/attest_image + with: + subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-default + subject_digest: ${{ steps.build.outputs.digest }} + + - name: External verify — expect SUCCESS + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh attestation verify \ + "oci://${{ env.IMAGE_BASE }}/test-attest-optout-default@${{ steps.build.outputs.digest }}" \ + --owner IABTechLab \ + --signer-repo IABTechLab/uid2-shared-actions + echo "Default behavior confirmed: attestation present, verify succeeded." + + opted-out: + name: Caller passes attestation_enabled=false — expect no attestation + runs-on: ubuntu-latest + env: + ATTEST_ENABLED: 'false' + steps: + - uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare context + run: | + mkdir -p ${{ runner.temp }}/ctx-optout + cat > ${{ runner.temp }}/ctx-optout/Dockerfile < /tag.txt + EOF + + - name: Build + push throwaway image + id: build + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: ${{ runner.temp }}/ctx-optout + push: true + tags: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled:${{ github.run_id }} + + - name: Call attest_image (gated, expect skipped) + if: ${{ env.ATTEST_ENABLED == 'true' }} + uses: ./actions/attest_image + with: + subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled + subject_digest: ${{ steps.build.outputs.digest }} + + - name: External verify — expect FAILURE (no attestations) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + output=$(gh attestation verify \ + "oci://${{ env.IMAGE_BASE }}/test-attest-optout-disabled@${{ steps.build.outputs.digest }}" \ + --owner IABTechLab \ + --signer-repo IABTechLab/uid2-shared-actions 2>&1) + rc=$? + echo "$output" + if [ "$rc" -eq 0 ]; then + echo "::error::Expected verify to fail (no attestations) but it succeeded — opt-out did not skip the attest step." + exit 1 + fi + # gh attestation verify against a digest with no attestation returns: + # Error: HTTP 404: Not Found (https://api.github.com/orgs//attestations/...) + if ! echo "$output" | grep -qE "HTTP 404|no attestations"; then + echo "::error::Expected 'HTTP 404' or 'no attestations' in failure output; got a different error." + exit 1 + fi + echo "Opt-out confirmed: no attestation present, verify failed as expected." diff --git a/actions/attest_image/action.yaml b/actions/attest_image/action.yaml index d5967034..dad23376 100644 --- a/actions/attest_image/action.yaml +++ b/actions/attest_image/action.yaml @@ -11,16 +11,11 @@ inputs: subject_digest: description: OCI manifest digest (sha256:...) emitted by docker/build-push-action. required: true - attestation_enabled: - description: Set to 'false' to skip attestation + verification. Default 'true'. - required: false - default: 'true' runs: using: composite steps: - name: Lowercase image reference - if: ${{ inputs.attestation_enabled == 'true' }} id: ref shell: bash run: | @@ -28,7 +23,6 @@ runs: echo "value=${value}" >> "$GITHUB_OUTPUT" - name: Attest build provenance - if: ${{ inputs.attestation_enabled == 'true' }} uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 env: # Mirrors actions/attest-build-provenance, prevents oversized OCI registry auth-challenge headers triggering HPE_HEADER_OVERFLOW. @@ -39,7 +33,6 @@ runs: push-to-registry: true - name: Verify attestation - if: ${{ inputs.attestation_enabled == 'true' }} shell: bash env: GH_TOKEN: ${{ github.token }} diff --git a/actions/shared_publish_to_docker/action.yaml b/actions/shared_publish_to_docker/action.yaml index 5636ad48..09eac13c 100644 --- a/actions/shared_publish_to_docker/action.yaml +++ b/actions/shared_publish_to_docker/action.yaml @@ -106,9 +106,8 @@ runs: IMAGE_VERSION=${{ inputs.new_version }} - name: Attest build provenance - if: ${{ inputs.not_snapshot == 'true' }} + if: ${{ inputs.not_snapshot == 'true' && inputs.attestation_enabled == 'true' }} uses: IABTechLab/uid2-shared-actions/actions/attest_image@v3 with: subject_name: ${{ inputs.docker_registry }}/${{ inputs.docker_image_name }} subject_digest: ${{ steps.push.outputs.digest }} - attestation_enabled: ${{ inputs.attestation_enabled }} From f19d4798cda6dc2c3d380ffeb45eaadd9c8e4304 Mon Sep 17 00:00:00 2001 From: Behnam Mozafari Date: Tue, 12 May 2026 12:51:58 +1000 Subject: [PATCH 5/5] UID2-6764: delete throwaway smoke workflow (run 25710314847 captured) Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/test-attest-opt-out.yaml | 134 --------------------- 1 file changed, 134 deletions(-) delete mode 100644 .github/workflows/test-attest-opt-out.yaml diff --git a/.github/workflows/test-attest-opt-out.yaml b/.github/workflows/test-attest-opt-out.yaml deleted file mode 100644 index cb5eeb14..00000000 --- a/.github/workflows/test-attest-opt-out.yaml +++ /dev/null @@ -1,134 +0,0 @@ -name: Smoke test attest_image opt-out - -# Mirrors run 25542801315 — builds a throwaway alpine image inline and -# exercises the caller-side gating pattern (an `if:` on the attest_image -# step, matching exactly what the shared docker workflows do). Two jobs: -# 1. default-enabled: ATTEST_ENABLED=true, expect attestation present + verify exit 0 -# 2. opted-out: ATTEST_ENABLED=false, expect HTTP 404 from gh attestation verify -# Uses push: trigger so it can fire before the workflow file exists on main. -# Delete this file after evidence captured — run ID is permanent. - -on: - push: - branches: - - bmz-UID2-6764-attestation-opt-out - -permissions: - contents: read - packages: write - id-token: write - attestations: write - artifact-metadata: write - -env: - IMAGE_BASE: ghcr.io/iabtechlab/uid2-shared-actions - -jobs: - default-enabled: - name: Caller passes attestation_enabled=true — expect attestation - runs-on: ubuntu-latest - env: - ATTEST_ENABLED: 'true' - steps: - - uses: actions/checkout@v4 - - - name: Log in to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Prepare context - run: | - mkdir -p ${{ runner.temp }}/ctx-default - cat > ${{ runner.temp }}/ctx-default/Dockerfile < /tag.txt - EOF - - - name: Build + push throwaway image - id: build - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 - with: - context: ${{ runner.temp }}/ctx-default - push: true - tags: ${{ env.IMAGE_BASE }}/test-attest-optout-default:${{ github.run_id }} - - - name: Call attest_image (gated, expect runs) - if: ${{ env.ATTEST_ENABLED == 'true' }} - uses: ./actions/attest_image - with: - subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-default - subject_digest: ${{ steps.build.outputs.digest }} - - - name: External verify — expect SUCCESS - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh attestation verify \ - "oci://${{ env.IMAGE_BASE }}/test-attest-optout-default@${{ steps.build.outputs.digest }}" \ - --owner IABTechLab \ - --signer-repo IABTechLab/uid2-shared-actions - echo "Default behavior confirmed: attestation present, verify succeeded." - - opted-out: - name: Caller passes attestation_enabled=false — expect no attestation - runs-on: ubuntu-latest - env: - ATTEST_ENABLED: 'false' - steps: - - uses: actions/checkout@v4 - - - name: Log in to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Prepare context - run: | - mkdir -p ${{ runner.temp }}/ctx-optout - cat > ${{ runner.temp }}/ctx-optout/Dockerfile < /tag.txt - EOF - - - name: Build + push throwaway image - id: build - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 - with: - context: ${{ runner.temp }}/ctx-optout - push: true - tags: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled:${{ github.run_id }} - - - name: Call attest_image (gated, expect skipped) - if: ${{ env.ATTEST_ENABLED == 'true' }} - uses: ./actions/attest_image - with: - subject_name: ${{ env.IMAGE_BASE }}/test-attest-optout-disabled - subject_digest: ${{ steps.build.outputs.digest }} - - - name: External verify — expect FAILURE (no attestations) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set +e - output=$(gh attestation verify \ - "oci://${{ env.IMAGE_BASE }}/test-attest-optout-disabled@${{ steps.build.outputs.digest }}" \ - --owner IABTechLab \ - --signer-repo IABTechLab/uid2-shared-actions 2>&1) - rc=$? - echo "$output" - if [ "$rc" -eq 0 ]; then - echo "::error::Expected verify to fail (no attestations) but it succeeded — opt-out did not skip the attest step." - exit 1 - fi - # gh attestation verify against a digest with no attestation returns: - # Error: HTTP 404: Not Found (https://api.github.com/orgs//attestations/...) - if ! echo "$output" | grep -qE "HTTP 404|no attestations"; then - echo "::error::Expected 'HTTP 404' or 'no attestations' in failure output; got a different error." - exit 1 - fi - echo "Opt-out confirmed: no attestation present, verify failed as expected."