Skip to content

docs: harden recommended PR-check workflow against CodeQL alerts#141

Merged
theoephraim merged 1 commit into
mainfrom
claude/harden-codeql-workflow
Jun 25, 2026
Merged

docs: harden recommended PR-check workflow against CodeQL alerts#141
theoephraim merged 1 commit into
mainfrom
claude/harden-codeql-workflow

Conversation

@theoephraim

Copy link
Copy Markdown
Member

The recommended pull_request_target "Bumpy Check" workflow in docs/github-actions.md trips GitHub CodeQL's default Actions security queries, so anyone copying it verbatim gets a critical alert. Two findings — one fixed in the YAML, one documented.

1. actions/envvar-injection/critical — fixed

The version-resolution step wrote a file-derived value to $GITHUB_ENV:

echo "BUMPY_VERSION=$VERSION" >> "$GITHUB_ENV"   # flagged sink

In a privileged workflow CodeQL treats the file-derived value as attacker-controlled, and $GITHUB_ENV is an RCE-grade sink (a newline-bearing value could inject e.g. NODE_OPTIONS). Folded the version straight into the bunx call so nothing is written to $GITHUB_ENV — behavior identical, value still from the trusted base-branch package.json:

- name: Bumpy release-plan check
  run: |
    VERSION=$(jq -r '.devDependencies["@varlock/bumpy"] // .dependencies["@varlock/bumpy"]' package.json | sed 's/[\^~]//')
    bunx "@varlock/bumpy@$VERSION" ci check --cwd ./pr
  env:
    GH_TOKEN: ${{ github.token }}

2. actions/untrusted-checkout/critical — documented

Inherent to checking out PR head in a privileged workflow; CodeQL can't prove the code is never executed. Added a note that it's an expected false positive, safe to dismiss, with the reasoning: bunx runs from the trusted root (root bunfig.toml/lockfile), ci check --cwd ./pr only reads the PR files, and nothing under ./pr is installed/built/run.

3. Safety invariant (stated + verified)

Documented the rule the data-only guarantee depends on: bumpy ci check never executes code from its --cwd target — no build, no lifecycle scripts, no dynamic import/require; data parsing only.

Verified against the implementation:

  • ciCheckCommand only: parses JSONC config + package.json + .bumpy/*.md (js-yaml v4 safe load), globs the workspace via fs (no package-manager spawn), and runs git/gh via argv arrays.
  • The two code-loading sinks — loadFormatterimport(modulePath) and resolveCommitMessageimport(fnPath) — are reachable only from apply-release-plan / publish / version / ci release, never from ci check.

Scope notes

  • Mentioned the pull_requestworkflow_run artifact split as an optional "strict mode" (fully green, no dismissals) but kept the single-workflow data-only pattern as the recommendation, per the task.
  • No other copies of the workflow exist: README references ci check only conceptually, bumpy ci setup scaffolds release-token setup (not the check workflow), and there are no example/template repos.
  • The release workflow's $GITHUB_ENV write (line ~174) is left as-is — it runs on push (trusted), not pull_request_target, so the privileged-context query doesn't flag it.

Docs-only change; no managed package touched, so no bump file.

🤖 Generated with Claude Code

Consumers copying the pull_request_target check workflow tripped CodeQL's
default Actions queries with a critical alert. Two findings addressed:

- actions/envvar-injection/critical (fixed): fold the base-resolved bumpy
  version straight into the `bunx` invocation instead of writing it to
  $GITHUB_ENV (an RCE-grade sink in a privileged workflow). Behavior is
  identical — the value still comes from the trusted base-branch package.json.
- actions/untrusted-checkout/critical (documented): inherent to checking out
  PR head in a privileged workflow and not statically fixable. Added a note
  that it's an expected false positive, with the reasoning.

Also states the safety invariant the data-only guarantee depends on — `ci
check` never executes code from its --cwd target — and records that the
current implementation upholds it (the only code-loading paths, custom
changelog/commit-message modules, live in version/publish/release, not check).
Mentions the pull_request->workflow_run split as an optional strict mode.

No other copies of the workflow exist (README/ci-setup don't embed it).
@theoephraim theoephraim merged commit 4e8f1bd into main Jun 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant