Skip to content

Harden L10n workflows against branch-name script injection#99

Open
arpitjain099 wants to merge 2 commits into
ossf:mainfrom
arpitjain099:chore/harden-expression-injection
Open

Harden L10n workflows against branch-name script injection#99
arpitjain099 wants to merge 2 commits into
ossf:mainfrom
arpitjain099:chore/harden-expression-injection

Conversation

@arpitjain099

Copy link
Copy Markdown

What

The two localization workflows reference branch-ref context values directly inside run: blocks:

  • check-outdated-content.yaml: ${{ github.base_ref }} and ${{ github.head_ref }} appear in the bash steps, including OLD_BRANCH="origin/${{github.base_ref}}", which is then used in git diff ${OLD_BRANCH}..${LATEST_BRANCH}.
  • post-outdated-content-report.yaml: the debug step echoes ${{ github.head_ref }}, ${{ github.base_ref }}, and the raw ${{ github }} context.

Why this matters

${{ }} expressions are expanded into the script text by the Actions runner before bash runs, so a value an attacker can influence (a pull-request branch name) is concatenated straight into the command. A branch named something like $(...) can therefore execute arbitrary commands when the workflow runs. This is the script-injection pattern described in GitHub's security hardening guide, and it is what CodeQL's actions/expression-injection query and zizmor flag.

The change

Each affected branch-ref value is now passed through a step-level env: block and referenced as a quoted shell variable ("$HEAD_REF") inside run:. Environment variables are not re-parsed by the shell, so the untrusted text can no longer alter the script. I also routed the report workflow's context dump through toJSON(github) in env: rather than echoing the raw ${{ github }} object.

No behavior changes: the same values are logged and the same git refs are compared.

The localization workflows interpolate github.head_ref and
github.base_ref directly into run blocks. Because the Actions runner
substitutes ${{ }} into the script text before bash executes it, a
crafted branch name on a pull request could inject shell commands
(for example in the OLD_BRANCH assignment that feeds git diff).

Pass the branch-ref and other github-context values through step-level
env: variables and reference them as quoted shell variables instead.
Runtime behavior is unchanged; the values are no longer evaluated as
part of the script. This follows GitHub's security-hardening guidance
for avoiding script injection.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
@kusari-inspector

kusari-inspector Bot commented May 28, 2026

Copy link
Copy Markdown

Kusari Inspector

Kusari Analysis Results:

Proceed with these changes

✅ No Flagged Issues Detected
All values appear to be within acceptable risk parameters.

No pinned version dependency changes, code issues or exposed secrets detected!

Note

View full detailed analysis result for more information on the output and the checks that were run.


@kusari-inspector rerun - Trigger a re-analysis of this PR
@kusari-inspector feedback [your message] - Send feedback to our AI and team
See Kusari's documentation for setup and configuration.
Commit: e781b80, performed at: 2026-06-02T01:28:02Z

Found this helpful? Give it a 👍 or 👎 reaction!

echo "(DEBUG) github.actor: ${ACTOR}"
echo "(DEBUG) github.base_ref: ${BASE_REF}"
echo "(DEBUG) github.event_name: ${EVENT_NAME}"
echo "(DEBUG) github.event_path: ${EVENT_PATH}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Issue: The run block directly uses ${{ }} template expressions for github.token, github.workflow, github.repository, github.repository_owner, github.run_id, github.run_number, github.sha, and github.workspace. All of these should be assigned to environment variables at the step or job level and referenced via $ENV_VAR syntax. The github.token value should NEVER be echoed in logs under any circumstances. Remove the echo for github.token entirely and move all remaining ${{ }} references to the env block.

Recommended Code Changes:

env:
  REPOSITORY: ${{ github.repository }}
  REPOSITORY_OWNER: ${{ github.repository_owner }}
  RUN_ID: ${{ github.run_id }}
  RUN_NUMBER: ${{ github.run_number }}
  SHA: ${{ github.sha }}
  WORKFLOW: ${{ github.workflow }}
  WORKSPACE: ${{ github.workspace }}
run: |
  echo "(DEBUG) github.repository: ${REPOSITORY}"
  echo "(DEBUG) github.repository_owner: ${REPOSITORY_OWNER}"
  echo "(DEBUG) github.run_id: ${RUN_ID}"
  echo "(DEBUG) github.run_number: ${RUN_NUMBER}"
  echo "(DEBUG) github.sha: ${SHA}"
  echo "(DEBUG) github.workflow: ${WORKFLOW}"
  echo "(DEBUG) github.workspace: ${WORKSPACE}"
  # Remove the github.token echo entirely - never log tokens

…step

The 'Show the github context' debug step echoed ${{ github.token }} straight
into the workflow log and still interpolated several github.* values directly
inside the run block. Drop the token echo entirely (a token should never be
written to logs) and pass the remaining context values
(repository, repository_owner, run_id, run_number, sha, workflow, workspace)
through the env block, referenced as shell variables. This finishes the
expression-injection hardening for this step.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
@arpitjain099

Copy link
Copy Markdown
Author

Good catch from the Kusari scan. I pushed a follow-up commit that finishes hardening the Show the github context debug step:

  • Removed the echo "(DEBUG) github.token: ..." line entirely. A token should never be written to the log, and it served no purpose in a debug dump.
  • Moved the remaining direct ${{ github.* }} interpolations (repository, repository_owner, run_id, run_number, sha, workflow, workspace) into the step env: block and reference them as $VAR, matching the pattern already used for head_ref/base_ref/etc. in this PR.

That keeps every context value out of the shell-parsed command text, so there's no expression-injection surface left in that step.

@kusari-inspector

Copy link
Copy Markdown

Kusari PR Analysis rerun based on - e781b80 performed at: 2026-06-02T01:28:03Z - link to updated analysis

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