From 01a1b81f474daf23670567664ea5c7ed6a1614bb Mon Sep 17 00:00:00 2001 From: sean wibisono Date: Tue, 22 Sep 2026 09:49:36 +1000 Subject: [PATCH 1/2] UID2-7932 Fix attestation identity case matching --- actions/attest_image/action.yaml | 4 +- .../attest_image/tests/test_identity_regex.sh | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 actions/attest_image/tests/test_identity_regex.sh diff --git a/actions/attest_image/action.yaml b/actions/attest_image/action.yaml index 07ed2977..5b11b3c9 100644 --- a/actions/attest_image/action.yaml +++ b/actions/attest_image/action.yaml @@ -43,8 +43,8 @@ runs: SUBJECT_DIGEST: ${{ inputs.subject_digest }} IMAGE_REF: ${{ steps.ref.outputs.value }} run: | - # Accept either signer SAN: the reusable workflow under IABTechLab/uid2-shared-actions, or the caller's workflow via the composite path. --owner alone would reject the former (see https://github.com/cli/cli/issues/9045). + # Accept either signer SAN: the reusable workflow under IABTechLab/uid2-shared-actions, or the caller's workflow via the composite path. GitHub certificate identities are case-normalized, so match repository names case-insensitively. --owner alone would reject the former (see https://github.com/cli/cli/issues/9045). gh attestation verify \ "oci://${IMAGE_REF}@${SUBJECT_DIGEST}" \ --owner "${{ github.repository_owner }}" \ - --cert-identity-regex "^https://github\.com/(IABTechLab/uid2-shared-actions|${{ github.repository }})/" + --cert-identity-regex "(?i)^https://github\.com/(IABTechLab/uid2-shared-actions|${{ github.repository }})/" diff --git a/actions/attest_image/tests/test_identity_regex.sh b/actions/attest_image/tests/test_identity_regex.sh new file mode 100755 index 00000000..e72091c0 --- /dev/null +++ b/actions/attest_image/tests/test_identity_regex.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Tests the certificate identity policy embedded in ../action.yaml. +set -euo pipefail + +action_file="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/action.yaml" + +python3 - "$action_file" <<'PY' +import re +import sys +from pathlib import Path + +action = Path(sys.argv[1]).read_text() +match = re.search(r'--cert-identity-regex "([^"]+)"', action) +if not match: + raise AssertionError("certificate identity regex not found in action.yaml") + +identity_regex = match.group(1).replace( + "${{ github.repository }}", "IABTechLab/uid2-examples" +) +policy = re.compile(identity_regex) + +accepted = [ + "https://github.com/IABTechLab/uid2-shared-actions/.github/workflows/publish.yaml@refs/tags/v3", + "https://github.com/iabtechlab/uid2-shared-actions/.github/workflows/publish.yaml@refs/tags/v3", + "https://github.com/IABTechLab/uid2-examples/.github/workflows/release.yaml@refs/heads/main", + "https://github.com/iabtechlab/uid2-examples/.github/workflows/release.yaml@refs/heads/main", +] +rejected = [ + "https://github.com/IABTechLab/uid2-shared-actions-malicious/.github/workflows/publish.yaml@refs/tags/v3", + "https://github.com/other-org/uid2-examples/.github/workflows/release.yaml@refs/heads/main", + "https://example.com/IABTechLab/uid2-shared-actions/.github/workflows/publish.yaml@refs/tags/v3", +] + +for identity in accepted: + if not policy.match(identity): + raise AssertionError(f"expected identity to be accepted: {identity}") + +for identity in rejected: + if policy.match(identity): + raise AssertionError(f"expected identity to be rejected: {identity}") + +print("certificate identity regex tests passed") +PY From 9dea8adc1221184aeedd61e59586747372c37754 Mon Sep 17 00:00:00 2001 From: sean wibisono Date: Tue, 22 Sep 2026 10:01:46 +1000 Subject: [PATCH 2/2] UID2-7932 Clarify certificate identity casing --- actions/attest_image/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/attest_image/action.yaml b/actions/attest_image/action.yaml index 5b11b3c9..2018562b 100644 --- a/actions/attest_image/action.yaml +++ b/actions/attest_image/action.yaml @@ -43,7 +43,7 @@ runs: SUBJECT_DIGEST: ${{ inputs.subject_digest }} IMAGE_REF: ${{ steps.ref.outputs.value }} run: | - # Accept either signer SAN: the reusable workflow under IABTechLab/uid2-shared-actions, or the caller's workflow via the composite path. GitHub certificate identities are case-normalized, so match repository names case-insensitively. --owner alone would reject the former (see https://github.com/cli/cli/issues/9045). + # Accept either signer SAN: the reusable workflow under IABTechLab/uid2-shared-actions, or the caller's workflow via the composite path. Certificate identities can differ in repository-name casing, so match them case-insensitively. --owner alone would reject the former (see https://github.com/cli/cli/issues/9045). gh attestation verify \ "oci://${IMAGE_REF}@${SUBJECT_DIGEST}" \ --owner "${{ github.repository_owner }}" \