Visitar URL original
Security (Medium): release notes run as shell commands in the create-version workflow by jasverix · Pull Request #450 · bcc-code/bcc-design · GitHub
Skip to content

Security (Medium): release notes run as shell commands in the create-version workflow - #450

Merged
jasverix merged 1 commit into
mainfrom
security/checkin-2026-11-13-version-workflow-injection
Nov 13, 2026
Merged

jasverix merged 1 commit into
mainfrom
security/checkin-2026-11-13-version-workflow-injection

Conversation

@jasverix

Copy link
Copy Markdown
Collaborator

Found by the BCC security check-in on 2026-11-13 (code review).

Finding

The free-text whats_new input of Create New Component Library Version (.github/workflows/design-library-create-version.yml) is expanded by a shell twice:

  1. In the workflow: it is written straight into the run: step as "${{ github.event.inputs.whats_new }}".
  2. In component-library/scripts/version.cjs: the script passes it to child_process.exec inside git tag -am ${JSON.stringify(tagMessage)} …. Inside double quotes, the shell still expands `, $(…) and $VAR.

The job checks out with secrets.BCC_BOT_ACCESS_TOKEN, which stays in the git config, so injected commands can read the bot token.

  • Deliberate abuse: only someone who can dispatch the workflow, i.e. who has write access, can control the input.
  • Accidental breakage: this is the practical risk. Ordinary Markdown notes like Fixed `BccButton` hover run BccButton as a command and silently drop that text, and a " in the notes breaks the step.
  • Same text later goes to $GITHUB_OUTPUT: design-library-release.yml and design-library-prepare-release.yml write it with a fixed EOF heredoc delimiter. A line EOF in the notes ends the value early and can inject other step outputs.

Fix

  • Workflow: the inputs go through env: (VERSION, CHANNEL, WHATS_NEW), and the step calls node ./scripts/version.cjs "$VERSION" "$CHANNEL" "$WHATS_NEW" directly, which removes the extra shell layer that pnpm run adds.
  • version.cjs: git add, git commit and git tag -a -m <message> <name> now run through execFileSync, with no shell. The pnpm version call is unchanged; it only receives the choice inputs.
  • Release workflows: the RELEASE_DESCRIPTION heredoc uses a random delimiter (EOF_$(openssl rand -hex 16)).

A side effect: multi-line release notes now keep real line breaks in the tag message. Before, JSON.stringify put a literal \n into the annotated tag.

Verification

I ran the old and new version.cjs in a scratch git repo with the real package.json and pnpm-lock.yaml (pnpm 12.16.1, Node 24). The notes were Fixed `BccButton` hover $(touch /tmp/pwned) and "quotes".

Tag message touch ran
main component-library v1.5.6\n\nFixed hover and "quotes" yes
this PR Fixed BccButton hover $(touch /tmp/pwned) and "quotes" (as typed) no

All three workflow files parse as YAML, and the edited run: blocks pass bash -n. I did not run the workflows on GitHub.

For the reviewer

  • Check the next release: confirm that the version commit and tag are still created and pushed. The steps around them are unchanged.
  • Related, not fixed here (low): icons/scripts/build.js compiles every SVG as a Vue template, so a malicious icon PR could ship code in @bcc-code/icons-vue. It is listed in the check-in report.

Generated by Claude Code

The "What is new" input of the create-version workflow was interpolated
into a run step, and version.cjs passed it to a shell again through exec.
Backticks and $(...) in release notes ran as commands with the bot token
available, and quotes broke the step. The inputs now go through env
variables, version.cjs calls git with execFileSync, and the release
workflows use a random heredoc delimiter for the release description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011A1M8h7FjdeuJFcE7nmdSa
@jasverix
jasverix merged commit ba1a19b into main Nov 13, 2026
7 checks passed
@jasverix
jasverix deleted the security/checkin-2026-11-13-version-workflow-injection branch November 13, 2026 14:20
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.

2 participants