Skip to content

Add two-phase release workflow#634

Open
Boomatang wants to merge 1 commit into
mainfrom
release_process
Open

Add two-phase release workflow#634
Boomatang wants to merge 1 commit into
mainfrom
release_process

Conversation

@Boomatang

@Boomatang Boomatang commented Jun 18, 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

Release Notes

  • Documentation

    • Updated release process documentation with a comprehensive two-phase workflow guide, including details on artefacts, version rules, and required secrets.
  • Chores

    • Enhanced release automation with new validation scripts and GitHub Actions workflows to streamline the pre-release and release phases, improving consistency and reducing manual steps.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a two-phase release automation system comprising a release.yaml version seed file, two Bash helper scripts (parse-version.sh, validate-release-yaml.sh), three new GitHub Actions workflows (pre-release.yaml, release.yaml, version-gate.yaml), extensions to build-images.yaml for workflow_call support, and a rewritten RELEASE.md.

Changes

Two-phase release automation

Layer / File(s) Summary
Version seed file and parse-version script
release.yaml, .github/scripts/parse-version.sh
release.yaml adds the authorino.version field initialised to 0.0.0. parse-version.sh reads the YAML, enforces semver format, derives major/minor/patch, computes the release-MAJOR.MINOR branch name, and writes all values to GITHUB_OUTPUT.
Release YAML validation script and Version Gate workflow
.github/scripts/validate-release-yaml.sh, .github/workflows/version-gate.yaml
validate-release-yaml.sh checks authorino.version rules per branch and verifies declared dependency releases exist via gh. version-gate.yaml installs yq, then executes the validation script on PRs targeting release-** branches when release.yaml is modified.
Pre-release workflow (Phase 1)
.github/workflows/pre-release.yaml
Three-job workflow triggered manually: setup validates semver input and creates the release branch; prepare-release creates a pre-release-v<version> branch, updates release.yaml via yq, runs make generate and make manifests, and pushes; open-pr opens a PR to the release branch via gh pr create.
build-images workflow_call extension
.github/workflows/build-images.yaml
Adds a workflow_call trigger with required version and ref inputs; the prepare job prioritises inputs.version over the existing ref-name derivation; checkout uses inputs.ref; the merge job always emits version_tag and conditions sha/latest tags on version_tag == 'latest'.
Release workflow (Phase 2)
.github/workflows/release.yaml
Five-job workflow triggered manually: read-version parses and validates the version; smoke-tests runs lint, unit tests, and CEL tests; tag creates and pushes an annotated git tag; build-image reuses build-images.yaml via workflow_call; create-release runs gh release create with generated notes.
RELEASE.md documentation update
RELEASE.md
Rewrites the file to document the two-phase workflow, release artefacts, release.yaml version rules, required GitHub Actions secrets, and updated automated-build notes.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant PreRelease as pre-release workflow
    participant VersionGate as Version Gate workflow
    participant Release as release workflow
    participant BuildImages as build-images workflow
    participant GitHub as GitHub Remote

    Dev->>PreRelease: manual dispatch (version, source-branch)
    PreRelease->>GitHub: create release-MAJOR.MINOR branch
    PreRelease->>GitHub: push pre-release-v branch (updated release.yaml + generated manifests)
    PreRelease->>GitHub: gh pr create → release branch
    GitHub->>VersionGate: PR event targeting release-** (release.yaml changed)
    VersionGate->>VersionGate: validate-release-yaml.sh (version rules + dependency releases)
    Dev->>Release: manual dispatch (release-branch) after PR merge
    Release->>Release: parse-version.sh, smoke tests (lint/unit/CEL)
    Release->>GitHub: git tag v{version}
    Release->>BuildImages: workflow_call (version, ref=tag)
    BuildImages->>GitHub: build and push multi-arch Docker image
    Release->>GitHub: gh release create v{version}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hop, hop, two phases to leap,
A release.yaml to keep,
Parse the version, validate the tag,
No release slips through the bag.
Gate the PR, build the image right,
Then publish the release into the night! 🚀

🚥 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 'Add two-phase release workflow' accurately captures the main objective of the changeset, which introduces a new two-phase release workflow system with supporting scripts and documentation.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 release_process

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.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>
@Boomatang Boomatang marked this pull request as ready for review June 23, 2026 10:29

@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: 8

🤖 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 20-24: The yq query on the line that assigns dep_version uses dot
notation with `.dependencies.${dep}`, which fails when dependency names contain
hyphens because yq interprets hyphens as subtraction operators rather than
literal characters. Replace the dot notation syntax with bracket notation by
changing `.dependencies.${dep}` to `.dependencies["${dep}"]` to properly handle
dependency names with special characters like hyphens.

In @.github/workflows/build-images.yaml:
- Around line 39-42: The conditional block starting at line 39 directly
interpolates the workflow input `${{ inputs.version }}` into shell code, which
creates a shell injection vulnerability if a caller passes values containing
shell metacharacters like `$(command)`. Instead of directly embedding the input
in the bash conditional and echo commands, set the input value as an environment
variable first, then reference that variable in the conditional check and the
subsequent echo statements that write to GITHUB_OUTPUT. This prevents the shell
from interpreting any special characters in the input value.

In @.github/workflows/pre-release.yaml:
- Line 27: The workflow file uses mutable version tags (such as `@v4` and `@v5`) for
GitHub Actions references on lines 27, 62, 82, and 108, which creates security
risks since these tags can be retargeted upstream. Replace each mutable version
tag with a specific immutable commit SHA. For the actions/checkout action on
line 27 and any other action references throughout the file, determine the
correct commit SHA for each action version (for example, `@v4` should be replaced
with the full commit SHA like `@e2f20f853a2a19803ed141a6f266e17681910f6d`) and
update all four occurrences to use the pinned commit hash instead of the version
tag.

In @.github/workflows/release.yaml:
- Around line 11-13: The `contents: write` permission is currently set at the
workflow level, granting write access to all jobs. Move this permission to only
the specific job(s) that create tags or releases by removing `contents: write`
from the workflow-level permissions block and adding it as a job-level
permission to only the job(s) that perform release operations. This ensures
other jobs in the workflow operate with minimal necessary privileges.
- Around line 91-94: The tag existence check in the release workflow is not
idempotent - it fails unconditionally whenever the tag already exists, blocking
recovery from partial failures. Modify the git tag validation logic to check if
the tag exists at the current HEAD commit rather than just checking if the tag
exists. If the tag already points to HEAD, allow the workflow to proceed as
success. Only exit with an error if the tag exists but points to a different
commit, indicating a genuine conflict.
- Around line 22-24: Replace all mutable GitHub Actions version tags in the
release.yaml workflow with pinned commit SHAs. Specifically, replace the `@v4`
tag in the actions/checkout action on line 22, and locate the other GitHub
Actions usages at lines 60, 65, 82, and 112, then replace their mutable version
tags (such as `@v4` or `@v5`) with their full commit SHAs. You can find the correct
commit SHAs by looking up each action's releases on GitHub or using the GitHub
CLI. This ensures that the workflow uses immutable action references and
prevents silent upstream changes that could affect supply-chain security.
- Around line 38-43: The step "Validate branch matches version" directly
interpolates the workflow input `inputs.release-branch` into the bash script,
creating a shell injection vulnerability. Move `inputs.release-branch` to an
environment variable by adding an `env` block to the step that sets a variable
(e.g., `RELEASE_BRANCH: ${{ inputs.release-branch }}`), then update the bash
script to reference this environment variable using native shell syntax (e.g.,
`$RELEASE_BRANCH`) instead of the direct `${{ inputs.release-branch }}`
interpolation on lines 41-42.

In @.github/workflows/version-gate.yaml:
- Around line 28-30: The validate-release-yaml.sh script is currently receiving
the source branch via github.head_ref, but it should receive the target branch
via github.base_ref to properly validate against the correct release branch.
Additionally, to prevent shell-template injection attacks through crafted branch
names, pass the branch reference as an environment variable rather than directly
interpolating it in the shell command. Modify the run block to export
github.base_ref as an environment variable and pass that variable to the
validate-release-yaml.sh script instead of the direct interpolation.
🪄 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

Run ID: 9c0cdb3d-3870-4fdc-ae44-d790d6b496b1

📥 Commits

Reviewing files that changed from the base of the PR and between b6bd487 and f17c4d8.

📒 Files selected for processing (8)
  • .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 +20 to +24
DEPS=$(yq '.dependencies | keys | .[]' "$RELEASE_YAML" 2>/dev/null || true)
for dep in $DEPS; do
dep_version=$(yq ".dependencies.${dep}" "$RELEASE_YAML")
if [[ "$dep_version" != "0.0.0" && "$dep_version" != "null" && -n "$dep_version" ]]; then
if ! gh release view "v${dep_version}" --repo "${ORG}/${dep}" &>/dev/null; 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.

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the validate-release-yaml.sh file
git ls-files | grep -i "validate-release-yaml"

Repository: Kuadrant/authorino

Length of output: 197


🏁 Script executed:

# Read the file to see the current state
cat -n .github/scripts/validate-release-yaml.sh | head -40

Repository: Kuadrant/authorino

Length of output: 1386


🏁 Script executed:

# Test yq behaviour with keys containing hyphens
# Create a test YAML to verify the concern
cat > /tmp/test-release.yaml <<'EOF'
dependencies:
  my-service: "1.0.0"
  another-component: "2.0.0"
  simple: "3.0.0"
EOF

# Test current approach (without quotes)
echo "=== Testing current approach (unquoted key) ==="
yq '.dependencies | keys | .[]' /tmp/test-release.yaml

echo ""
echo "=== Testing current approach with unquoted variable ==="
dep="my-service"
yq ".dependencies.${dep}" /tmp/test-release.yaml 2>&1 || echo "FAILED"

echo ""
echo "=== Testing proposed fix with bracket notation ==="
yq -r ".dependencies[\"${dep}\"]" /tmp/test-release.yaml

Repository: Kuadrant/authorino

Length of output: 435


🏁 Script executed:

# Check if there are any RELEASE_YAML files in the repo and their content
find . -name "*release*.yaml" -o -name "*release*.yml" | head -10

Repository: Kuadrant/authorino

Length of output: 241


🌐 Web query:

yq key quoting hyphenated keys bracket notation syntax

💡 Result:

To access keys containing hyphens in yq, you must use bracket notation with quoted strings [1][2]. Because hyphenated keys can be misinterpreted by the parser as subtraction operations, wrapping the key name in quotes (either double or single) is necessary to ensure it is treated as a literal string [1][2]. The standard syntax for accessing such a key is: yq '.["your-hyphenated-key"]' file.yaml Alternatively, you can use the syntax: yq '."your-hyphenated-key"' file.yaml It is critical to use shell-level quoting (the outer single quotes) to prevent your shell from interpreting the brackets or quotes before they are passed to yq [1]. Common pitfalls: 1. Shell Interpretation: If you do not wrap the entire expression in single quotes (e.g., yq.["key-name"]), your shell may attempt to interpret the brackets or quotes, leading to errors. 2. Operator Confusion: If you omit quotes around the key name, yq may treat the hyphen as a subtraction operator rather than a character in the key name [1]. 3. Leading Dashes: If the key name itself starts with a hyphen, you may also need to use the flag terminator (--) to stop the command from misinterpreting the key as a command-line flag [3].

Citations:


🌐 Web query:

yq .dependencies.key-name vs .dependencies["key-name"] parsing difference

💡 Result:

In yq, the primary difference between dot notation (e.g.,.dependencies.key-name) and bracket notation (e.g.,.dependencies["key-name"]) is how they handle key names containing special characters, such as dots, hyphens, or spaces [1][2][3]. Dot notation is designed for simple, alphanumeric key names [4]. When a key contains special characters like a period, hyphen, or space, yq may misinterpret these characters as part of the expression's syntax—for example, treating a dot as a nested object separator rather than part of the key name [2][5][3]. Bracket notation with quotes (e.g.,.dependencies["key-name"]) acts as a robust mechanism to treat the contents within the brackets as a literal string [1][3]. This allows you to access keys that would otherwise be syntactically ambiguous or invalid in dot notation [2][6]. Key takeaways: 1. Dot notation: Suitable for standard, simple key names without special characters [4]. It cannot correctly reference keys containing dots (e.g.,.a.b.c interprets b.c as a nested structure) or other reserved symbols [2][5]. 2. Bracket notation: Required for keys containing dots, spaces, or leading dashes [1][2][3][7]. It is generally safer to use bracket notation whenever you are uncertain if a key name might conflict with yq expression syntax [6]. 3. Shell considerations: When using bracket notation in a terminal, it is important to wrap the entire expression in single quotes (e.g., '.dependencies["key-name"]') to prevent the shell from interpreting the double quotes or brackets prematurely [2][5][8]. For example, if you have a key named b.c, you must use.["b.c"] or."b.c" to access it, as.b.c would attempt to look for a key named b containing a sub-key named c [2][5].

Citations:


🏁 Script executed:

# Let me check the release.yaml file in the repo to see what dependencies actually exist
cat release.yaml 2>/dev/null | head -50

Repository: Kuadrant/authorino

Length of output: 186


🏁 Script executed:

# Also check if there are any dependencies with hyphens in their names
# by examining any release YAML files
if [ -f release.yaml ]; then
  echo "=== Content of release.yaml ===" 
  cat release.yaml
fi

Repository: Kuadrant/authorino

Length of output: 218


Use bracket notation with quoted keys when querying yq for dynamic dependency names.

Line 22 currently builds .dependencies.${dep} using dot notation, which fails when dependency names contain hyphens. The yq parser misinterprets hyphens as subtraction operators rather than literal characters. This causes dependency version checks to fail silently or be skipped entirely for dependencies like my-service.

🔧 Proposed fix
-DEPS=$(yq '.dependencies | keys | .[]' "$RELEASE_YAML" 2>/dev/null || true)
-for dep in $DEPS; do
-  dep_version=$(yq ".dependencies.${dep}" "$RELEASE_YAML")
+mapfile -t deps < <(yq -r '.dependencies // {} | keys[]' "$RELEASE_YAML")
+for dep in "${deps[@]}"; do
+  dep_version=$(yq -r ".dependencies[\"${dep}\"]" "$RELEASE_YAML")
🤖 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 - 24, The yq query
on the line that assigns dep_version uses dot notation with
`.dependencies.${dep}`, which fails when dependency names contain hyphens
because yq interprets hyphens as subtraction operators rather than literal
characters. Replace the dot notation syntax with bracket notation by changing
`.dependencies.${dep}` to `.dependencies["${dep}"]` to properly handle
dependency names with special characters like hyphens.

Comment on lines +39 to +42
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/build-images.yaml | sed -n '1,50p'

Repository: Kuadrant/authorino

Length of output: 2070


🏁 Script executed:

cat -n .github/workflows/build-images.yaml | sed -n '9,18p;75,76p;130,132p'

Repository: Kuadrant/authorino

Length of output: 837


🏁 Script executed:

# Check if there are any uses of inputs.version elsewhere in the workflow
rg 'inputs\.version' .github/workflows/build-images.yaml

Repository: Kuadrant/authorino

Length of output: 347


Harden workflow_call input handling before using it in bash.

Lines 39–41 interpolate ${{ inputs.version }} directly into shell code. If a caller passes a crafted value containing shell metacharacters (e.g. $(command)), the shell will expand it and execute arbitrary code, poisoning the workflow outputs. Pass the input via an environment variable instead to prevent expansion.

Suggested fix
       - name: Set Authorino build info
         id: build-info
+        env:
+          INPUT_VERSION: ${{ inputs.version }}
         run: |
-          if [[ -n "${{ inputs.version }}" ]]; then
-            echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
-            echo "version_tag=v${{ inputs.version }}" >> $GITHUB_OUTPUT
+          if [[ -n "$INPUT_VERSION" ]]; then
+            if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
+              echo "::error::Invalid version input: $INPUT_VERSION"
+              exit 1
+            fi
+            printf 'version=%s\n' "$INPUT_VERSION" >> "$GITHUB_OUTPUT"
+            printf 'version_tag=v%s\n' "$INPUT_VERSION" >> "$GITHUB_OUTPUT"
           elif [[ ${GITHUB_REF_NAME/\/,-} =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then
📝 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
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
- name: Set Authorino build info
id: build-info
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
if [[ -n "$INPUT_VERSION" ]]; then
if ! [[ "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "::error::Invalid version input: $INPUT_VERSION"
exit 1
fi
printf 'version=%s\n' "$INPUT_VERSION" >> "$GITHUB_OUTPUT"
printf 'version_tag=v%s\n' "$INPUT_VERSION" >> "$GITHUB_OUTPUT"
elif [[ ${GITHUB_REF_NAME/\/,-} =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.+)?$ ]]; then
🧰 Tools
🪛 zizmor (1.26.1)

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

(template-injection)


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

(template-injection)


[error] 41-41: 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 39 - 42, The conditional
block starting at line 39 directly interpolates the workflow input `${{
inputs.version }}` into shell code, which creates a shell injection
vulnerability if a caller passes values containing shell metacharacters like
`$(command)`. Instead of directly embedding the input in the bash conditional
and echo commands, set the input value as an environment variable first, then
reference that variable in the conditional check and the subsequent echo
statements that write to GITHUB_OUTPUT. This prevents the shell from
interpreting any special characters in the input value.

Source: Linters/SAST tools

version: ${{ steps.validate.outputs.version }}
release-branch: ${{ steps.validate.outputs.release-branch }}
steps:
- uses: actions/checkout@v4

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

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/pre-release.yaml | sed -n '20,115p'

Repository: Kuadrant/authorino

Length of output: 4142


Pin all action references to immutable commit SHAs.

Lines 27, 62, 82, and 108 use mutable version tags (@v4/@v5), leaving the workflow vulnerable to upstream tag retargeting. Replace with specific commit SHAs (e.g., actions/checkout@e2f20f853a2a19803ed141a6f266e17681910f6d).

🧰 Tools
🪛 zizmor (1.26.1)

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

(artipacked)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 at line 27, The workflow file uses
mutable version tags (such as `@v4` and `@v5`) for GitHub Actions references on
lines 27, 62, 82, and 108, which creates security risks since these tags can be
retargeted upstream. Replace each mutable version tag with a specific immutable
commit SHA. For the actions/checkout action on line 27 and any other action
references throughout the file, determine the correct commit SHA for each action
version (for example, `@v4` should be replaced with the full commit SHA like
`@e2f20f853a2a19803ed141a6f266e17681910f6d`) and update all four occurrences to
use the pinned commit hash instead of the version tag.

Source: Linters/SAST tools

Comment on lines +11 to +13
permissions:
contents: write

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

Reduce workflow token scope to least privilege.

contents: write at workflow scope grants write access to jobs that do not need it. Scope write permission only to jobs that create tags/releases.

Suggested fix
 permissions:
-  contents: write
+  contents: read

 jobs:
+  tag:
+    permissions:
+      contents: write
+    needs: [read-version, smoke-tests]
+    runs-on: ubuntu-latest
+    steps:
+      ...
+
+  create-release:
+    permissions:
+      contents: write
+    needs: [read-version, build-image]
+    runs-on: ubuntu-latest
+    steps:
+      ...
🧰 Tools
🪛 zizmor (1.26.1)

[error] 12-12: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level

(excessive-permissions)

🤖 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 11 - 13, The `contents: write`
permission is currently set at the workflow level, granting write access to all
jobs. Move this permission to only the specific job(s) that create tags or
releases by removing `contents: write` from the workflow-level permissions block
and adding it as a job-level permission to only the job(s) that perform release
operations. This ensures other jobs in the workflow operate with minimal
necessary privileges.

Source: Linters/SAST tools

Comment on lines +22 to +24
- uses: actions/checkout@v4
with:
ref: ${{ inputs.release-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

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/release.yaml

Repository: Kuadrant/authorino

Length of output: 4881


Pin GitHub Actions to commit SHAs instead of mutable version tags.

Using @v4/@v5 allows silent upstream changes and weakens supply-chain security guarantees. Pin all GitHub Actions to their full commit SHAs: actions/checkout@<commit-sha> and actions/setup-go@<commit-sha>.

This applies to lines 22, 60, 65, 82, and 112 in this workflow file.

🧰 Tools
🪛 zizmor (1.26.1)

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

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 - 24, Replace all mutable
GitHub Actions version tags in the release.yaml workflow with pinned commit
SHAs. Specifically, replace the `@v4` tag in the actions/checkout action on line
22, and locate the other GitHub Actions usages at lines 60, 65, 82, and 112,
then replace their mutable version tags (such as `@v4` or `@v5`) with their full
commit SHAs. You can find the correct commit SHAs by looking up each action's
releases on GitHub or using the GitHub CLI. This ensures that the workflow uses
immutable action references and prevents silent upstream changes that could
affect supply-chain security.

Source: Linters/SAST tools

Comment on lines +38 to +43
- name: Validate branch matches version
run: |
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)"
exit 1

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

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the release.yaml file
git ls-files | grep -E 'release\.yaml|release\.yml'

Repository: Kuadrant/authorino

Length of output: 235


🏁 Script executed:

# Check the content around lines 38-43 of the release.yaml file
cat -n .github/workflows/release.yaml | sed -n '30,50p'

Repository: Kuadrant/authorino

Length of output: 1127


🌐 Web query:

GitHub Actions security best practices context interpolation shell script injection

💡 Result:

The primary risk of context interpolation in GitHub Actions arises because workflow expressions (the ${{ ... }} syntax) are evaluated by the GitHub Actions runner before the shell executes your script [1][2][3]. When you directly interpolate untrusted context values—such as github.event.issue.title or github.event.pull_request.head_ref—into a run step, you inadvertently allow an attacker to inject arbitrary shell commands [1][4][3]. To secure your workflows against this script injection vulnerability, follow these best practices: 1. Use Intermediate Environment Variables (Recommended) Never place ${{ ... }} expressions directly inside your shell command [5][6][3]. Instead, map the untrusted value to an environment variable in the env block of the step, and then reference that variable using the native shell syntax (e.g., $VAR or "$VAR") [7][5][3]. Vulnerable Example: run: echo "Processing PR: ${{ github.event.pull_request.title }}" Secure Example: env: PR_TITLE: ${{ github.event.pull_request.title }} run: echo "Processing PR: $PR_TITLE" 2. Avoid Re-interpolation Do not use ${{ env.VAR }} inside a run step to access an environment variable [5][6][3]. This re-introduces the interpolation vulnerability because the expression is evaluated before the shell executes the command [6][3]. Always use native shell syntax (e.g., $VAR) to access variables created in the env block [5][3]. 3. Use Actions for Complex Logic When possible, replace inline shell scripts with custom actions or existing community actions that process data as arguments rather than generating shell scripts [7][8]. Because arguments are passed directly to the action's code rather than being concatenated into a shell command string, this approach is inherently resistant to script injection [7][8]. 4. Treat All Contexts as Untrusted Any context that can be influenced by external users, such as PR titles, issue bodies, branch names, or author names, must be treated as untrusted input [1][4]. Be especially cautious with workflows triggered by events like pull_request, issue_comment, or workflow_run [8][3]. 5. Enable Security Scanning Use tools like CodeQL or ESLint plugins (e.g., eslint-plugin-github-actions) to automatically detect and flag unsafe interpolation of context values in your workflow YAML files [9][10]. Enabling code scanning helps prevent these vulnerabilities from being introduced in future changes [9].

Citations:


🏁 Script executed:

# Check the workflow trigger and input definitions
cat -n .github/workflows/release.yaml | sed -n '1,30p'

Repository: Kuadrant/authorino

Length of output: 1092


Pass inputs.release-branch via an environment variable to prevent shell injection.

Line 41–42 directly interpolates the workflow dispatch input into a bash script, which allows shell command injection if the input contains metacharacters. GitHub Actions evaluates ${{ ... }} expressions before the shell executes, making the input available for expansion. Pass the input through the env block and reference it with native shell syntax instead.

Suggested fix
       - name: Validate branch matches version
+        env:
+          INPUT_RELEASE_BRANCH: ${{ inputs.release-branch }}
         run: |
           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)"
+          if [[ "$INPUT_RELEASE_BRANCH" != "$EXPECTED_BRANCH" ]]; then
+            echo "::error::Branch '$INPUT_RELEASE_BRANCH' does not match version ${{ steps.parse.outputs.version }} (expected branch: $EXPECTED_BRANCH)"
             exit 1
           fi
🧰 Tools
🪛 zizmor (1.26.1)

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

(template-injection)


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

(template-injection)


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

(template-injection)


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

(template-injection)


[info] 42-42: 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 38 - 43, The step "Validate
branch matches version" directly interpolates the workflow input
`inputs.release-branch` into the bash script, creating a shell injection
vulnerability. Move `inputs.release-branch` to an environment variable by adding
an `env` block to the step that sets a variable (e.g., `RELEASE_BRANCH: ${{
inputs.release-branch }}`), then update the bash script to reference this
environment variable using native shell syntax (e.g., `$RELEASE_BRANCH`) instead
of the direct `${{ inputs.release-branch }}` interpolation on lines 41-42.

Source: Linters/SAST tools

Comment on lines +91 to +94
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make tag creation idempotent for safe reruns.

If a previous run pushed the tag but failed later, reruns will always fail here and block recovery. Treat “tag already exists at HEAD” as success.

Suggested fix
           if git rev-parse "$TAG" >/dev/null 2>&1; then
-            echo "::error::Tag $TAG already exists"
-            exit 1
+            TAG_SHA="$(git rev-list -n1 "$TAG")"
+            HEAD_SHA="$(git rev-parse HEAD)"
+            if [[ "$TAG_SHA" != "$HEAD_SHA" ]]; then
+              echo "::error::Tag $TAG already exists and points to $TAG_SHA, not $HEAD_SHA"
+              exit 1
+            fi
+            echo "Tag $TAG already exists at HEAD; continuing"
+            exit 0
           fi
📝 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
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
exit 1
fi
if git rev-parse "$TAG" >/dev/null 2>&1; then
TAG_SHA="$(git rev-list -n1 "$TAG")"
HEAD_SHA="$(git rev-parse HEAD)"
if [[ "$TAG_SHA" != "$HEAD_SHA" ]]; then
echo "::error::Tag $TAG already exists and points to $TAG_SHA, not $HEAD_SHA"
exit 1
fi
echo "Tag $TAG already exists at HEAD; continuing"
exit 0
fi
🤖 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 91 - 94, The tag existence check
in the release workflow is not idempotent - it fails unconditionally whenever
the tag already exists, blocking recovery from partial failures. Modify the git
tag validation logic to check if the tag exists at the current HEAD commit
rather than just checking if the tag exists. If the tag already points to HEAD,
allow the workflow to proceed as success. Only exit with an error if the tag
exists but points to a different commit, indicating a genuine conflict.

Comment on lines +28 to +30
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

Use github.base_ref via env to avoid injection and enforce the correct branch rule.

Lines 28-30 interpolate github.head_ref directly in the shell and pass the source branch, but the gate logic should evaluate the target release branch. This can both weaken validation semantics and allow shell-template injection through crafted PR branch names.

🔧 Proposed fix
       - name: Validate release.yaml
         env:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          BASE_REF: ${{ github.base_ref }}
         run: |
           chmod +x .github/scripts/validate-release-yaml.sh
-          .github/scripts/validate-release-yaml.sh "${{ github.head_ref }}" "Kuadrant"
+          .github/scripts/validate-release-yaml.sh "$BASE_REF" "Kuadrant"
📝 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
run: |
chmod +x .github/scripts/validate-release-yaml.sh
.github/scripts/validate-release-yaml.sh "${{ github.head_ref }}" "Kuadrant"
- name: Validate release.yaml
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BASE_REF: ${{ github.base_ref }}
run: |
chmod +x .github/scripts/validate-release-yaml.sh
.github/scripts/validate-release-yaml.sh "$BASE_REF" "Kuadrant"
🧰 Tools
🪛 actionlint (1.7.12)

[error] 28-28: "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.26.1)

[error] 30-30: 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 28 - 30, The
validate-release-yaml.sh script is currently receiving the source branch via
github.head_ref, but it should receive the target branch via github.base_ref to
properly validate against the correct release branch. Additionally, to prevent
shell-template injection attacks through crafted branch names, pass the branch
reference as an environment variable rather than directly interpolating it in
the shell command. Modify the run block to export github.base_ref as an
environment variable and pass that variable to the validate-release-yaml.sh
script instead of the direct interpolation.

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.

2 participants