Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/create-release-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Create release tag
run-name: Create ${{ inputs.release_type }} release tag

on:
workflow_dispatch:
inputs:
release_type:
description: 'Type of version bump'
required: true
default: 'patch'
type: choice
options:
- patch
- minor

permissions:
contents: write
Comment thread
cursor[bot] marked this conversation as resolved.

# Two overlapping runs would bump from the same tip and race on the same tag.
concurrency:
group: create-release-tag
cancel-in-progress: false

jobs:
create-release-tag:
Comment thread
cursor[bot] marked this conversation as resolved.
name: Bump version and push tag
runs-on: ubuntu-latest
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- name: 🛑 Ensure workflow runs on master
if: github.ref != 'refs/heads/master'
env:
REF_NAME: ${{ github.ref_name }}
run: |
echo "::error::This workflow can only be run on master, got ${REF_NAME}."
exit 1
Comment on lines +31 to +37

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

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

echo "== target workflow =="
if [ -f .github/workflows/create-release-tag.yml ]; then
  cat -n .github/workflows/create-release-tag.yml
else
  echo "target file missing"
fi

echo "== package lifecycle scripts / npm version config =="
if [ -f package.json ]; then
  python3 - <<'PY'
import json
p=json.load(open('package.json'))
for k in ['scripts','version']: print(k, p.get(k))
PY
fi

Repository: GladysAssistant/Gladys

Length of output: 4472


🌐 Web query:

GitHub Actions run workflow button select branch workflow_dispatch manual runs on branch security bash injection github.ref_name

💡 Result:

Using ${{ github.ref_name }} or workflow inputs directly within a bash run block is a significant security risk, classified as template or shell command injection [1][2][3]. When you place these expressions directly into a run block, the GitHub Actions runner replaces the placeholders with their raw string values before the shell executes the command [4][2][5]. An attacker can craft a branch name or workflow input containing shell meta-characters (such as ;, &, |, or backticks) to break out of your intended command and execute arbitrary code on the runner [1][6][3]. To secure your workflows, you must never interpolate untrusted values directly into run scripts [1][2][5]. Instead, follow these industry-standard defensive practices: 1. Use Environment Variables: Map all untrusted inputs (including github.ref_name and workflow_dispatch inputs) to environment variables at the step level [4][6][7]. This ensures the values are treated as data, not code, by the shell [2][6][8]. Example of secure implementation: - name: Process branch name env: BRANCH_NAME: ${{ github.ref_name }} run: | # Use the variable in quotes to prevent shell interpretation echo "Processing branch: $BRANCH_NAME" 2. Input Validation: For workflow_dispatch inputs, implement strict validation using regular expressions or allowlists before the input is used in any downstream logic [4][9][6]. For example, ensure a version input matches a known pattern (e.g., ^[a-zA-Z0-9._-]+$) [4][6]. 3. Static Analysis: Enable code scanning with CodeQL in your repository [2][5]. CodeQL includes specialized queries designed to detect unsafe interpolation of untrusted inputs into GitHub Actions workflows, helping you catch these vulnerabilities before they are exploited [2][5]. By treating all inputs—including branch names, issue titles, and workflow_dispatch fields—as untrusted, you significantly reduce the risk of supply chain compromise and credential exfiltration [1][4][6].

Citations:


🌐 Web query:

npm version lifecycle scripts runs lifecycle scripts prepublish preversion version postversion

💡 Result:

In the npm lifecycle, scripts are triggered by specific commands to manage package preparation, versioning, and publishing [1][2]. Version Lifecycle Scripts When running the npm version command, the lifecycle scripts execute in the following fixed order [1][2]: 1. preversion: Runs before the package version is bumped [3][4]. 2. version: Runs after the version is bumped, but before the commit [3][4]. 3. postversion: Runs after the version is bumped and after the commit [3][4]. Prepublish and Related Scripts The prepublish script is considered deprecated because its behavior can be confusing; it runs both before the package is packed/published and during a local npm install without arguments [3][1]. To replace this, npm introduced other lifecycle events [3][1]: - prepublishOnly: Runs only before the package is prepared and packed, specifically during npm publish [3][1]. - prepare: Runs after prepublish, but before prepublishOnly. It executes during both npm publish and local npm install, serving as a reliable alternative for build steps [3][1]. General Note For any script (e.g., myscript), npm automatically looks for pre- and post- counterparts (premyscript and postmyscript) if they are defined in your package.json [1][2]. Additionally, the environment variable npm_lifecycle_event is always set to the name of the script currently being executed, allowing you to use a single script to handle multiple lifecycle stages [3][1].

Citations:


🌐 Web query:

GitHub Actions workflow_dispatch manual run select workflow branch if condition github.ref jobs job if

💡 Result:

When using workflow_dispatch to run a GitHub Actions workflow manually, the GitHub UI allows you to select any branch in the repository as the target for the workflow execution [1][2]. For the manual branch selection to function, the workflow file must exist in the default branch of the repository [1][3]. Once triggered, the workflow execution context identifies the selected branch through standard GitHub context variables [2]. You can use the github.ref or github.ref_name context variables within your workflow to dynamically handle the selected branch [2]. To restrict or control job execution based on the branch selected during the manual run, you can use the jobs.<job_id>.if condition [4][3]. Example implementation using a conditional job: jobs: deploy: runs-on: ubuntu-latest # This job will only run if the branch selected in the manual UI is 'main' if: ${{ github.ref_name == 'main' }} steps: - uses: actions/checkout@v4 - run: echo "Running on ${{ github.ref_name }}" If you need to control the branch used by the actions/checkout step itself (for example, to support scheduled runs alongside manual runs), you can use a ternary expression to dynamically set the ref [2]: jobs: checkout-job: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: # Use 'main' for scheduled runs, or the selected branch for manual runs ref: ${{ github.event_name == 'schedule' && 'main' || github.ref_name }}

Citations:


Move the branch restriction and checkout target outside untrusted output.

.github/workflows/create-release-tag.yml allows manual dispatch on any branch. Interpolating github.ref_name into the shell can allow command injection, and npm version can run lifecycle scripts from the selected repository state before the guard exits. Apply the check at job level and force checkout to refs/heads/master.

🧰 Tools
🪛 zizmor (1.28.0)

[error] 27-27: 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/create-release-tag.yml around lines 24 - 28, Move the
master-branch restriction from the “🛑 Ensure workflow runs on master” step to
the job-level if condition, preventing the job from starting for other refs.
Update the checkout action to explicitly target refs/heads/master, and remove
the shell-based github.ref_name interpolation and late branch guard.

Source: Linters/SAST tools

- name: 🔑 Ensure the RELEASE_PAT secret is set
env:
RELEASE_PAT: ${{ secrets.RELEASE_PAT }}
run: |
if [ -z "${RELEASE_PAT}" ]; then
echo "::error::The RELEASE_PAT secret is missing. It is required because a push made"
echo "::error::with the default GITHUB_TOKEN does not trigger the release workflows,"
echo "::error::which would leave a tag on master without any production image built."
exit 1
fi
- name: ⬇️ Checkout Gladys code
uses: actions/checkout@v4
with:
# Always release from the current tip of master, not from the commit
# master pointed at when the workflow was dispatched.
ref: master
fetch-depth: 0
# Checkout runs with the default GITHUB_TOKEN, and nothing is written to
# the local git config: RELEASE_PAT is only handed to the final push,
# through a one-shot authentication header.
persist-credentials: false
- name: 💽 Setup nodejs
uses: actions/setup-node@v4
with:
node-version-file: './package.json'
- name: 🔧 Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: 🔖 Bump version and create tag
id: bump
env:
RELEASE_TYPE: ${{ inputs.release_type }}
run: |
NEW_VERSION=$(npm version "${RELEASE_TYPE}")
echo "version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
echo "Created tag ${NEW_VERSION}"
- name: 🚀 Push commit and tag
env:
NEW_VERSION: ${{ steps.bump.outputs.version }}
RELEASE_PAT: ${{ secrets.RELEASE_PAT }}
run: |
AUTH_HEADER=$(printf 'x-access-token:%s' "${RELEASE_PAT}" | base64 -w0)
echo "::add-mask::${AUTH_HEADER}"
git -c http.extraheader="AUTHORIZATION: basic ${AUTH_HEADER}" \
push --atomic origin master "refs/tags/${NEW_VERSION}"
- name: 📝 Job summary
env:
NEW_VERSION: ${{ steps.bump.outputs.version }}
RELEASE_TYPE: ${{ inputs.release_type }}
run: |
{
echo "### 🚀 Release ${NEW_VERSION} created"
echo ""
echo "- Bump type: \`${RELEASE_TYPE}\`"
echo "- Tag: [\`${NEW_VERSION}\`](${{ github.server_url }}/${{ github.repository }}/releases/tag/${NEW_VERSION})"
} >> "$GITHUB_STEP_SUMMARY"
Loading