Skip to content

Support validating keyless signed images in verify-enterprise-contract tekton task#3140

Open
simonbaird wants to merge 4 commits intoconforma:mainfrom
simonbaird:task-keyless-support
Open

Support validating keyless signed images in verify-enterprise-contract tekton task#3140
simonbaird wants to merge 4 commits intoconforma:mainfrom
simonbaird:task-keyless-support

Conversation

@simonbaird
Copy link
Member

See commit messages for explanations.

Ref: https://issues.redhat.com/browse/EC-1652

simonbaird and others added 4 commits February 28, 2026 13:24
I'm doing this first in its own separate commit because it will make
the next commit is easier to understand and review.

There's no functional change happening, we're just defining the task
step in a different way.

Note:
- There are some clear inconsistencies in the way we use
  `--arg=value` or `--arg value` but I'm keeping all that as-is,
  since I want this change to be as small as possible.
- For the same reason I'm keeping the commentary, and the timeout
  param, which I think could be removed now. Not touching it.
- I'm focussing on just the verify-enterprise-contract task only
  here. My plan is to get that working first, then tackle the other
  tasks.

Ref: https://issues.redhat.com/browse/EC-1652
Co-authored-by: Claude Code <noreply@anthropic.com>
We're keeping the new param handling bash as light as possible. My
thinking is that ec should produce a good enough error if the params
are wrong or incorrectly specified, so we don't want or need a layer
messy bash to check that they are correct.

Ref: https://issues.redhat.com/browse/EC-1652
Co-authored-by: Claude Code <noreply@anthropic.com>
As per the comments, I'm creating an image that we can use with the
acceptance tests. Unlike the "golden-container" it wasn't built in
Konflux and hence it doesn't have a realistic Chains style
attestation. But it does have a keylessly sig and an a valid
attestation.

And actually there are two images, one created with cosign v2, and
one with cosign v3. This should be useful since we need to support
the new sigstore bundle introduced in cosign v3.

The verify.sh isn't important for anything, but I want to check it
in anyhow, since it serves as a nice instruction on how to verify
the keyless signed images and attestations, and it's useful to
sanity check the images when/if they need recreating.

Ref: https://issues.redhat.com/browse/EC-1652
Ref: https://issues.redhat.com/browse/EC-1652
Co-authored-by: Claude Code <noreply@anthropic.com>
@qodo-code-review
Copy link
Contributor

Review Summary by Qodo

Add keyless signing verification support to verify-enterprise-contract task

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add keyless signing verification support to verify-enterprise-contract task
  - New CERTIFICATE_IDENTITY and CERTIFICATE_OIDC_ISSUER parameters
  - Conditional logic to use keyless or public-key verification
• Convert task validate step from command to script format
  - Enables flexible parameter handling for keyless signatures
• Create test infrastructure for keyless signed images
  - Scripts to generate and verify test images with cosign v2 and v3
• Add acceptance tests for keyless signature verification
  - Tests for both cosign v2 and v3 signature formats
Diagram
flowchart LR
  A["verify-enterprise-contract Task"] -->|"Add Parameters"| B["CERTIFICATE_IDENTITY<br/>CERTIFICATE_OIDC_ISSUER"]
  A -->|"Convert to Script"| C["Flexible Parameter<br/>Handling"]
  C -->|"Support"| D["Keyless Verification"]
  C -->|"Support"| E["Public-Key Verification"]
  F["Test Image Creation"] -->|"Generate"| G["Keyless Signed Images<br/>v2 and v3"]
  G -->|"Verify in"| H["Acceptance Tests"]
Loading

Grey Divider

File Changes

1. tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml ✨ Enhancement +62/-42

Add keyless verification parameters and convert to script

• Added two new parameters CERTIFICATE_IDENTITY and CERTIFICATE_OIDC_ISSUER for keyless
 verification
• Converted validate step from command format to bash script format
• Implemented conditional logic to choose between keyless and public-key verification based on
 provided parameters
• Maintained all existing parameters and functionality while adding new capability

tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml


2. docs/modules/ROOT/pages/verify-enterprise-contract.adoc 📝 Documentation +2/-0

Document keyless verification parameters

• Added documentation for two new parameters: CERTIFICATE_IDENTITY and CERTIFICATE_OIDC_ISSUER
• Explained purpose of each parameter for keyless verification workflow

docs/modules/ROOT/pages/verify-enterprise-contract.adoc


3. hack/keyless-test-image/create.sh 🧪 Tests +76/-0

Create keyless signed test images script

• Script to create and sign test images with cosign v2 and v3
• Builds minimal UBI-based container images
• Signs images keylessly and creates SLSA provenance attestations
• Creates two versions to test different cosign signature formats

hack/keyless-test-image/create.sh


View more (3)
4. hack/keyless-test-image/helpers.sh 🧪 Tests +27/-0

Helper functions for test image scripts

• Utility functions for test image creation scripts
• Provides formatted output functions (h1, pause, nl)
• Supports interactive script execution with user prompts

hack/keyless-test-image/helpers.sh


5. hack/keyless-test-image/verify.sh 🧪 Tests +75/-0

Verify keyless signed test images script

• Script to verify keyless signed test images
• Tests cosign v2 and v3 verification commands
• Demonstrates backwards and forwards compatibility between cosign versions
• Uses personal identity for verification (sbaird@redhat.com with Google OIDC)

hack/keyless-test-image/verify.sh


6. features/task_validate_image.feature 🧪 Tests +78/-0

Add keyless signature acceptance tests

• Added two new acceptance test scenarios for keyless signing verification
• Test for cosign v2 style signatures with expected success
• Test for cosign v3 style signatures with expected failure (pending upstream fix)
• Uses external test images from quay.io/conforma/test registry
• Includes certificate identity and OIDC issuer parameters in test configuration

features/task_validate_image.feature


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 28, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Tekton script param injection 🐞 Bug ⛨ Security
Description
The validate step now inlines $(params.*) into a bash script; parameter values containing
quotes/newlines (e.g., JSON policy configs) can break the script or be exploited for shell
injection, and unquoted --flag=$(params.X) entries can be word-split by bash.
Code

tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[R253-300]

+      script: |
+        #!/bin/bash
+        set -euo pipefail
+
+        cmd_args=(
+          "validate"
+          "image"
+          "--images" "/tekton/home/snapshot.json"
+          "--policy" "$(params.POLICY_CONFIGURATION)"
+        )
+
+        if [ -n "$(params.CERTIFICATE_IDENTITY)" ] && [ -n "$(params.CERTIFICATE_OIDC_ISSUER)" ]; then
+          cmd_args+=(
+            "--certificate-identity" "$(params.CERTIFICATE_IDENTITY)"
+            "--certificate-oidc-issuer" "$(params.CERTIFICATE_OIDC_ISSUER)"
+          )
+        elif [ -n "$(params.PUBLIC_KEY)" ]; then
+          cmd_args+=(
+            "--public-key" "$(params.PUBLIC_KEY)"
+          )
+        fi
+
+        cmd_args+=(
+          "--rekor-url" "$(params.REKOR_HOST)"
+          "--ignore-rekor=$(params.IGNORE_REKOR)"
+          "--workers" "$(params.WORKERS)"
+          # NOTE: The syntax below is required to negate boolean parameters
+          "--info=$(params.INFO)"
+          # Fresh versions of ec support "--timeout=0" to indicate no timeout, but this would break
+          # the task if it's used with an older version of ec. In an abundance of caution, let's set
+          # an arbitrary high value instead of using 0 here. In future we can change it to 0.
+          # (The reason to not use an explicit timeout for ec is so Tekton can handle the timeouts).
+          "--timeout=100h"
+          "--strict=false"
+          "--show-successes"
+          "--effective-time=$(params.EFFECTIVE_TIME)"
+          "--extra-rule-data=$(params.EXTRA_RULE_DATA)"
+          "--retry-max-wait" "$(params.RETRY_MAX_WAIT)"
+          "--retry-max-retry" "$(params.RETRY_MAX_RETRY)"
+          "--retry-duration" "$(params.RETRY_DURATION)"
+          "--retry-factor" "$(params.RETRY_FACTOR)"
+          "--retry-jitter" "$(params.RETRY_JITTER)"
+          "--output" "text=$(params.HOMEDIR)/text-report.txt?show-successes=false"
+          "--output" "appstudio=$(results.TEST_OUTPUT.path)"
+          "--output" "json=$(params.HOMEDIR)/report-json.json"
+        )
+
+        exec ec "${cmd_args[@]}"
Evidence
The validate step embeds Tekton param substitutions directly into the bash script, including
"--policy" "$(params.POLICY_CONFIGURATION)". Another task in this repo explicitly avoids this
pattern by passing POLICY_CONFIGURATION via env to avoid shell quoting issues, and repo acceptance
tests show POLICY_CONFIGURATION can indeed be a JSON string with embedded quotes/backslashes.

tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[253-300]
tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml[291-346]
features/ta_task_validate_image.feature[65-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `validate` step uses a Tekton `script:` that directly interpolates `$(params.*)` into bash. If a param contains quotes/newlines (notably `POLICY_CONFIGURATION` when supplied as JSON), it can break the script or allow shell injection. Several `--flag=$(params.X)` arguments are also unquoted and can be word-split.

### Issue Context
This repo already documents this exact problem and solves it in `verify-conforma-konflux-ta` by passing `POLICY_CONFIGURATION` via environment variables.

### Fix Focus Areas
- tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[253-310]

### Suggested approach
- Add env vars for values used in the script (at least `POLICY_CONFIGURATION`, `PUBLIC_KEY`, `CERTIFICATE_IDENTITY`, `CERTIFICATE_OIDC_ISSUER`, and any other user-controlled strings).
- In the script, reference variables (e.g. `&quot;$POLICY_CONFIGURATION&quot;`) and ensure any `--flag=value` entries are wrapped/constructed safely (e.g. `&quot;--extra-rule-data=${EXTRA_RULE_DATA}&quot;`).
- Avoid placing raw `$(params.X)` inside the script body wherever possible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Partial keyless config ignored 🐞 Bug ✓ Correctness
Description
Keyless verification is enabled only when BOTH CERTIFICATE_IDENTITY and CERTIFICATE_OIDC_ISSUER are
set; if a user sets only one, the task silently ignores it (and may fall back to PUBLIC_KEY or
neither), causing unexpected verification behavior.
Code

tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[R264-273]

+        if [ -n "$(params.CERTIFICATE_IDENTITY)" ] && [ -n "$(params.CERTIFICATE_OIDC_ISSUER)" ]; then
+          cmd_args+=(
+            "--certificate-identity" "$(params.CERTIFICATE_IDENTITY)"
+            "--certificate-oidc-issuer" "$(params.CERTIFICATE_OIDC_ISSUER)"
+          )
+        elif [ -n "$(params.PUBLIC_KEY)" ]; then
+          cmd_args+=(
+            "--public-key" "$(params.PUBLIC_KEY)"
+          )
+        fi
Evidence
The script checks both keyless params with an AND; there is no validation/error when only one is
provided. A similar task in this repo fails fast on incomplete paired configuration (showing the
repo’s preferred UX for misconfiguration).

tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[264-273]
tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[72-84]
tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml[325-329]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The task currently ignores partial keyless configuration (only identity or only issuer). This can lead to confusing behavior and potentially running verification with unintended settings.

### Issue Context
The keyless flags are logically paired (identity + issuer). The repo already uses fail-fast validation for incomplete paired config in other tasks.

### Fix Focus Areas
- tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[264-273]

### Suggested approach
- In the script:
 - If exactly one of `CERTIFICATE_IDENTITY` / `CERTIFICATE_OIDC_ISSUER` is set: print an ERROR and `exit 1`.
 - If both are set and `PUBLIC_KEY` is also set: either error out (preferred to avoid ambiguity) or log which mode wins.
 - Keep current behavior when neither keyless param is set (allow policy-provided key config).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Keyless test is fragile 🐞 Bug ⛯ Reliability
Description
New acceptance tests depend on an externally hosted image and a hard-coded personal signing
identity, which may break if the image is rebuilt/removed or re-signed under a different identity.
Code

features/task_validate_image.feature[R362-375]

+    # See hack/keyless-test-image for how the test image was created. It's not ideal
+    # that this test requires an external image, but we already do this elsewhere, so
+    # I guess one more is okay. I'm hard coding the identity used to sign the image
+    # which is my personal account. That might have to change if the image is recreated.
+    #
+    # Todo: We should be able test this also with an internal image similar to how it's
+    # done in the "happy day with keyless" scenario in validate_image.feature.
+    #
+    When version 0.1 of the task named "verify-enterprise-contract" is run with parameters:
+      | IMAGES                  | {"components": [{"containerImage": "quay.io/conforma/test:keyless_v2@sha256:2dbc250c79306c30801216e37cd25164c64fda9ac3b9677c5eb0860cb13dbb87"}]} |
+      | POLICY_CONFIGURATION    | ${NAMESPACE}/${POLICY_NAME}                                               |
+      | CERTIFICATE_IDENTITY    | sbaird@redhat.com                                                         |
+      | CERTIFICATE_OIDC_ISSUER | https://accounts.google.com                                               |
+      | REKOR_HOST              | https://rekor.sigstore.dev                                                |
Evidence
The scenario explicitly calls out the external dependency and that the identity is a personal
account, and the helper scripts also bake in that identity/issuer. This is a known maintenance risk
for CI stability.

features/task_validate_image.feature[362-376]
hack/keyless-test-image/verify.sh[24-29]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Acceptance tests rely on an external quay.io image signed with a personal identity. This is brittle and can cause unrelated CI failures if the image/identity changes.

### Issue Context
The scenario itself acknowledges this risk.

### Fix Focus Areas
- features/task_validate_image.feature[341-381]
- hack/keyless-test-image/create.sh[22-33]
- hack/keyless-test-image/verify.sh[24-33]

### Suggested approach
- Replace the external fixture with a test image created/signed during the test run (if possible), or
- Move the fixture to a project-controlled repository and sign with a stable service identity; document rotation procedure.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +253 to +300
script: |
#!/bin/bash
set -euo pipefail

cmd_args=(
"validate"
"image"
"--images" "/tekton/home/snapshot.json"
"--policy" "$(params.POLICY_CONFIGURATION)"
)

if [ -n "$(params.CERTIFICATE_IDENTITY)" ] && [ -n "$(params.CERTIFICATE_OIDC_ISSUER)" ]; then
cmd_args+=(
"--certificate-identity" "$(params.CERTIFICATE_IDENTITY)"
"--certificate-oidc-issuer" "$(params.CERTIFICATE_OIDC_ISSUER)"
)
elif [ -n "$(params.PUBLIC_KEY)" ]; then
cmd_args+=(
"--public-key" "$(params.PUBLIC_KEY)"
)
fi

cmd_args+=(
"--rekor-url" "$(params.REKOR_HOST)"
"--ignore-rekor=$(params.IGNORE_REKOR)"
"--workers" "$(params.WORKERS)"
# NOTE: The syntax below is required to negate boolean parameters
"--info=$(params.INFO)"
# Fresh versions of ec support "--timeout=0" to indicate no timeout, but this would break
# the task if it's used with an older version of ec. In an abundance of caution, let's set
# an arbitrary high value instead of using 0 here. In future we can change it to 0.
# (The reason to not use an explicit timeout for ec is so Tekton can handle the timeouts).
"--timeout=100h"
"--strict=false"
"--show-successes"
"--effective-time=$(params.EFFECTIVE_TIME)"
"--extra-rule-data=$(params.EXTRA_RULE_DATA)"
"--retry-max-wait" "$(params.RETRY_MAX_WAIT)"
"--retry-max-retry" "$(params.RETRY_MAX_RETRY)"
"--retry-duration" "$(params.RETRY_DURATION)"
"--retry-factor" "$(params.RETRY_FACTOR)"
"--retry-jitter" "$(params.RETRY_JITTER)"
"--output" "text=$(params.HOMEDIR)/text-report.txt?show-successes=false"
"--output" "appstudio=$(results.TEST_OUTPUT.path)"
"--output" "json=$(params.HOMEDIR)/report-json.json"
)

exec ec "${cmd_args[@]}"
Copy link
Contributor

Choose a reason for hiding this comment

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

Action required

1. Tekton script param injection 🐞 Bug ⛨ Security

The validate step now inlines $(params.*) into a bash script; parameter values containing
quotes/newlines (e.g., JSON policy configs) can break the script or be exploited for shell
injection, and unquoted --flag=$(params.X) entries can be word-split by bash.
Agent Prompt
### Issue description
The `validate` step uses a Tekton `script:` that directly interpolates `$(params.*)` into bash. If a param contains quotes/newlines (notably `POLICY_CONFIGURATION` when supplied as JSON), it can break the script or allow shell injection. Several `--flag=$(params.X)` arguments are also unquoted and can be word-split.

### Issue Context
This repo already documents this exact problem and solves it in `verify-conforma-konflux-ta` by passing `POLICY_CONFIGURATION` via environment variables.

### Fix Focus Areas
- tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml[253-310]

### Suggested approach
- Add env vars for values used in the script (at least `POLICY_CONFIGURATION`, `PUBLIC_KEY`, `CERTIFICATE_IDENTITY`, `CERTIFICATE_OIDC_ISSUER`, and any other user-controlled strings).
- In the script, reference variables (e.g. `"$POLICY_CONFIGURATION"`) and ensure any `--flag=value` entries are wrapped/constructed safely (e.g. `"--extra-rule-data=${EXTRA_RULE_DATA}"`).
- Avoid placing raw `$(params.X)` inside the script body wherever possible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
acceptance 55.58% <ø> (ø)
generative 18.49% <ø> (ø)
integration 27.50% <ø> (ø)
unit 68.44% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant