Skip to content

ci: pass the release tag through env in the docs-sync dispatch - #2294

Open
WAHIB-EL-KHADIRI wants to merge 1 commit into
TEN-framework:mainfrom
WAHIB-EL-KHADIRI:ci/harden-sync-docs-dispatch
Open

ci: pass the release tag through env in the docs-sync dispatch#2294
WAHIB-EL-KHADIRI wants to merge 1 commit into
TEN-framework:mainfrom
WAHIB-EL-KHADIRI:ci/harden-sync-docs-dispatch

Conversation

@WAHIB-EL-KHADIRI

Copy link
Copy Markdown

What

.github/workflows/trigger_sync_remote_docs.yml interpolates the release tag directly into an actions/github-script body:

target_tag: '${{ github.event.release.tag_name || github.event.inputs.tag_name }}'

The script: block is JavaScript source, and ${{ ... }} is substituted as text before the script is parsed — it is not a runtime variable. A tag name containing a single quote closes the string literal and everything after it is evaluated as JavaScript.

A tag such as:

v1.0'});console.log(process.env);//

ends the call and runs attacker-chosen code in the step.

Why it matters here

This particular step carries TEN_FRAMEWORK_PORTAL_ACTION_PAT — a token with repo + workflow permissions on TEN-framework/portal. Code execution in this step reaches that token, so the blast radius extends to a second repository.

To be clear about severity: both triggers (release: created and workflow_dispatch) require write access, so this is defense in depth rather than an externally reachable vulnerability. It matters because it removes a path from "can push a tag" to "controls the portal PAT" — a meaningful step up in privilege, and one that release automation or a compromised maintainer account could take.

The fix

Pass the value through env: and read it with process.env.TARGET_TAG. The environment variable is only ever data, never source text. Behaviour is unchanged.

Also adds permissions: contents: read, which the file had never declared — the job only needs to dispatch through the PAT, not to use the default GITHUB_TOKEN.

Verification

The workflow parses cleanly, and the env block resolves as expected. The diff is three lines plus the permissions block; no logic changed.

🤖 Generated with Claude Code

The tag is interpolated into the github-script body, which is JavaScript
source. A ${{ }} expansion is substituted as text before the script runs,
so a tag containing a single quote terminates the string literal and the
rest executes as code -- in a step holding a cross-repo PAT.

Reading it from process.env keeps it data. Also adds the least-privilege
permissions block the file had never declared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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