Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .github/workflows/build-demo-website.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Build and publish demo website

on:
# Dispatched on the new tag by the "Create release tag" workflow: a tag pushed
# with the default GITHUB_TOKEN does not trigger the `push` event below.
workflow_dispatch:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Soft (parity with Docker): workflow_dispatch here has no tag/ref guard, so anyone with Actions write can publish the demo site from an arbitrary branch via the UI. docker-release-build.yml correctly skips publishing unless github.ref is a refs/tags/v* ref. Apidoc already had the same ungated dispatch before this PR, so this is not a merge blocker — consider mirroring the Docker-style if: on the deploy job if you want demo publishes limited to release tags.

push:
tags:
- 'v*.*.*'
Expand Down
91 changes: 91 additions & 0 deletions .github/workflows/create-release-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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.
# Needed to dispatch the release workflows on the new tag.
actions: write

# 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: ⬇️ 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
- 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 }}
run: |
git push --atomic origin master "refs/tags/${NEW_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.

Ops reminder (not a merge blocker): this push now uses the default GITHUB_TOKEN (contents: write) instead of a PAT. Confirm branch protection / rulesets allow GitHub Actions to push commits and tags to master; if not, the job fails closed (atomic push), but the workflow will not ship until that is allowed (or an Environment with a bypass actor is configured).

Optional hardening still stands: pin this job to a GitHub Environment with required reviewers so “Create release tag” is not a one-click production ship for every collaborator with write access.

- name: 🎬 Trigger the release workflows
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
NEW_VERSION: ${{ steps.bump.outputs.version }}
run: |
# A tag pushed with the default GITHUB_TOKEN does not trigger the
# workflows listening on `push: tags`, so they are dispatched explicitly
# on the new tag. workflow_dispatch is one of the two events GitHub does
# start from a GITHUB_TOKEN, which is why no PAT is needed here.
for workflow in docker-release-build.yml build-demo-website.yml build-apidoc-documentation.yml; do
echo "Triggering ${workflow} on ${NEW_VERSION}"
gh workflow run "${workflow}" --ref "${NEW_VERSION}"
done
- 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})"
echo "- Release workflows dispatched on the tag: production images, demo website, apidoc"
} >> "$GITHUB_STEP_SUMMARY"
6 changes: 6 additions & 0 deletions .github/workflows/docker-release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: Release Gladys Production Images
run-name: Release Gladys ${{ github.ref_name }} Production Image

on:
# Dispatched on the new tag by the "Create release tag" workflow: a tag pushed
# with the default GITHUB_TOKEN does not trigger the `push` event below.
workflow_dispatch:
push:
tags:
- 'v*.*.*'
Expand Down Expand Up @@ -121,6 +124,9 @@ jobs:
docker:
needs: build-front
name: Docker magic !
# Production images are only published from a release tag, never from a
# branch a manual dispatch could have been started on.
if: startsWith(github.ref, 'refs/tags/v')
Comment on lines +127 to +129

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 | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate workflow =="
fd -a 'docker-release-build\.yml$' . || true

echo "== workflow relevant sections =="
if [ -f .github/workflows/docker-release-build.yml ]; then
  wc -l .github/workflows/docker-release-build.yml
  sed -n '1,220p' .github/workflows/docker-release-build.yml
fi

echo "== occurrences of docker job and ref usage =="
rg -n "name: docker|docker-run|docker release|github\.ref|prerelease|on:|workflow_dispatch|pull_request|push:" .github/workflows/docker-release-build.yml || true

Repository: GladysAssistant/Gladys

Length of output: 7939


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== inspect release workflow context =="
python3 - <<'PY'
from pathlib import Path
p=Path('.github/workflows/docker-release-build.yml')
if not p.exists():
    print('missing')
    raise SystemExit
lines=p.read_text().splitlines()
for i,l in enumerate(lines,1):
    if 'workflow_dispatch' in l or 'push:' in l or 'pull_request:' in l or any(kw in l for kw in ['if:', 'refs/tags', 'docker', 'needs:']):
        print(f"{i}: {l}")
PY

Repository: GladysAssistant/Gladys

Length of output: 1652


🌐 Web query:

GitHub Actions workflow_dispatch workflow ref input github.ref tags values examples

💡 Result:

In GitHub Actions, the workflow_dispatch event allows you to manually trigger a workflow [1][2]. While you can define custom inputs for this event, the ref (reference) is a distinct parameter used to specify which branch, tag, or commit SHA the workflow should run against [1][3]. Key Concepts regarding Ref and Inputs: 1. The Ref Parameter: The ref is not defined as an input within your YAML workflow file's inputs section [4]. Instead, it is a required parameter provided when triggering the workflow via the GitHub UI, GitHub CLI, or REST API [1][2][3]. - GitHub UI: You select the branch or tag from a dropdown menu after clicking the Run workflow button [2]. - GitHub CLI: You use the --ref flag (e.g., gh workflow run WORKFLOW --ref BRANCH_OR_TAG) [1][2]. - REST API: You include it as a mandatory field in the request body (e.g., "ref": "main") [5][3]. 2. Inputs: You can define up to 25 custom inputs in your workflow YAML file under the on.workflow_dispatch.inputs key [1][4][3]. These inputs are separate from the Git ref [4][3]. Example of triggering via REST API: You must provide the ref as a top-level key in the JSON payload, while custom inputs reside within their own inputs object [3]. curl -L -X POST https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW_ID/dispatches \ -d '{ "ref": "main", "inputs": { "logLevel": "warning", "tags": "production-release" } }' If you need to pass a tag or branch dynamically as an input, you must define a string input in your YAML (e.g., named target_ref) and manually handle that value within your workflow steps, as the system-level ref parameter used by the trigger mechanism is not automatically mapped to custom input fields [4].

Citations:


Require an exact release-tag format before publishing images.

The Docker job runs when workflow_dispatch is invoked with any refs/tags/v* ref because startsWith(github.ref, 'refs/tags/v') only checks the prefix. Gate docker on a prerelease job that validates github.ref against the release tag grammar, such as vMAJOR.MINOR.PATCH, and let prerelease jobs publish only vMAJOR.MINOR.PATCH-next or another supported prerelease form.

🤖 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/docker-release-build.yml around lines 127 - 129, Update
the Docker publish condition around the docker job to require the prerelease
validation job and an exact supported release-tag format, rather than only the
refs/tags/v prefix. Allow production publishing only for vMAJOR.MINOR.PATCH
tags, while permitting prerelease jobs to publish supported forms such as
vMAJOR.MINOR.PATCH-next.

runs-on: ubuntu-22.04
env:
DOCKERHUB_USER: ${{secrets.DOCKERHUB_USER}}
Expand Down
Loading