Skip to content

PyPI publish GitHub Action vulnerable to injectable expression expansions in action steps

Low severity GitHub Reviewed Published Sep 4, 2025 in pypa/gh-action-pypi-publish • Updated Sep 4, 2025

Package

actions pypa/gh-action-pypi-publish (GitHub Actions)

Affected versions

< 1.13.0

Patched versions

1.13.0

Description

Summary

gh-action-pypi-publish makes use of GitHub Actions expression expansions (i.e. ${{ ... }}) in contexts that are potentially attacker controllable. Depending on the trigger used to invoke gh-action-pypi-publish, this may allow an attacker to execute arbitrary code within the context of a workflow step that invokes gh-action-pypi-publish.

Details

gh-action-pypi-publish contains a composite action step, set-repo-and-ref, that makes use of expression expansions:

  - name: Set repo and ref from which to run Docker container action
    id: set-repo-and-ref
    run: |
      # Set repo and ref from which to run Docker container action
      # to handle cases in which `github.action_` context is not set
      # https://github.com/actions/runner/issues/2473
      REF=${{ env.ACTION_REF || env.PR_REF || github.ref_name }}
      REPO=${{ env.ACTION_REPO || env.PR_REPO || github.repository }}
      REPO_ID=${{ env.PR_REPO_ID || github.repository_id }}
      echo "ref=$REF" >>"$GITHUB_OUTPUT"
      echo "repo=$REPO" >>"$GITHUB_OUTPUT"
      echo "repo-id=$REPO_ID" >>"$GITHUB_OUTPUT"
    shell: bash
    env:
      ACTION_REF: ${{ github.action_ref }}
      ACTION_REPO: ${{ github.action_repository }}
      PR_REF: ${{ github.event.pull_request.head.ref }}
      PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
      PR_REPO_ID: ${{ github.event.pull_request.base.repo.id }}

Permalink: https://github.com/pypa/gh-action-pypi-publish/blob/db8f07d3871a0a180efa06b95d467625c19d5d5f/action.yml#L114-L125

In normal intended operation, these expansions are used to establish a correct priority for outputs like ref and repo-id.

However, these expansions have a side effect: because they're done with ${{ ... }} and not with ${...} (i.e. normal shell interpolation), they can bypass normal shell quoting rules. In particular, if both env.ACTION_REF and env.PR_REF evaluate to empty strings, then the expression falls back to github.ref_name, which can be an attacker controlled string via a branch or tag name.

For example, if the attacker is able to set a branch name to something like innocent;cat${IFS}/etc/passwd, then the REF line may expand as:

REF=innocent;cat${IFS}/etc/passwd

which would set REF to innocent and then run the attacker's code.

Additional information about dangerous expansions can be found in zizmor's template-injection rule documentation.

Impact

The impact of this vulnerability is very low: the expression in question is unlikely to be evaluated in normal operation, since env.ACTION_REF should always take precedence.

In particular, the action is not vulnerable in many popular configurations, i.e. those where pull_request or release or a push: tags event is used to call the action.

References

@webknjaz webknjaz published to pypa/gh-action-pypi-publish Sep 4, 2025
Published to the GitHub Advisory Database Sep 4, 2025
Reviewed Sep 4, 2025
Last updated Sep 4, 2025

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N

EPSS score

Weaknesses

Improper Neutralization of Special Elements used in a Command ('Command Injection')

The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component. Learn more on MITRE.

CVE ID

No known CVE

GHSA ID

GHSA-vxmw-7h4f-hqxh

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.