Skip to content

ci: integrate SafeDep PMG on all workflow jobs - #275

Open
vanshika-verma-rzp wants to merge 4 commits into
masterfrom
integrate-pmg-github-actions
Open

ci: integrate SafeDep PMG on all workflow jobs#275
vanshika-verma-rzp wants to merge 4 commits into
masterfrom
integrate-pmg-github-actions

Conversation

@vanshika-verma-rzp

@vanshika-verma-rzp vanshika-verma-rzp commented Aug 24, 2026

Copy link
Copy Markdown

Summary

Integrates SafeDep PMG (Package Manager Guard) into every job across both GitHub Actions workflows. PMG runs as a persistent proxy in CI, intercepts installs from supported package managers via standard proxy env vars, and auto-blocks any package flagged as malicious.

Reference: PMG in GitHub Actions

What changed

Each job received two steps, per the SafeDep docs:

  1. Start PMGsafedep/pmg@v1 in server-mode, placed after checkout and before any package setup.
  2. Enforce PMG policypmg proxy stop --fail-on-violation with if: always(), which fails the job on a block and flushes final events to the cloud (runs even if an earlier step fails).

Coverage

File Job Installs intercepted Notes
ci.yml build bundle install Start placed before ruby/setup-ruby
ci.yml test bundle install Start placed before ruby/setup-ruby
ci.yml publish apt-get / gem push Added for full coverage; harmless guard (PMG only intercepts supported package managers)
ruby.yml test bundle install (via bundler-cache: true) Start placed before ruby/setup-ruby so PMG intercepts the bundler install run inside the action

Why placement matters (ruby.yml)

ruby.yml's test job uses bundler-cache: true, which runs bundle install inside the ruby/setup-ruby action. The PMG start step is therefore placed before setup-ruby so the proxy env vars are active when that install runs.

Secrets

The steps reference secrets.SAFEDEP_API_KEY and secrets.SAFEDEP_TENANT_ID. These are optional — PMG blocks malicious packages using SafeDep's free community intelligence with or without credentials. If the secrets are unset, they resolve empty and PMG still enforces; only cloud event sync to Endpoint Hub is skipped. No job breaks either way.

Follow-up (optional): add SAFEDEP_API_KEY and SAFEDEP_TENANT_ID as repo secrets to enable SafeDep Cloud event sync.

Verification

  • ✅ Both workflow files validated as parseable YAML.
  • ✅ No existing steps modified, removed, or reordered — all original steps intact.
  • ✅ Diff is purely additive: +36 lines, 0 deletions. Nothing extra beyond the PMG integration.

Diff stat

.github/workflows/ci.yml   | 27 +++++++++++++++++++++++++++
.github/workflows/ruby.yml |  9 +++++++++
2 files changed, 36 insertions(+)

Discussion thread: Slack

Route package installs through SafeDep PMG's persistent proxy in
server-mode to auto-block malicious packages in CI.

Adds a 'Start PMG' step (safedep/pmg@v1) before package setup and an
'Enforce PMG policy' step (pmg proxy stop --fail-on-violation, if:
always()) to every job across both workflows:

- ci.yml: build, test, publish
- ruby.yml: test

In ruby.yml the PMG step is placed before ruby/setup-ruby so it
intercepts the bundle install run by bundler-cache: true.
steps:
- uses: actions/checkout@v3
- name: Start PMG
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 can silently change when the upstream tag is repointed. A compromised action could run in CI and expose the provided SafeDep API credentials.

More details about this

The Start PMG step runs safedep/pmg@v1, a mutable version tag rather than an immutable commit. The action owner—or an attacker who compromises that repository—can repoint v1 to a malicious commit; the next push or pull request would execute that code on ubuntu-latest before the Ruby tests run. Because the step receives secrets.SAFEDEP_API_KEY and secrets.SAFEDEP_TENANT_ID, the malicious action could read or exfiltrate those credentials and use them to access the SafeDep tenant. It could also alter the workspace or workflow outputs before bundle exec rake and the PMG policy check execute.

A plausible attack sequence:

  1. An attacker gains control of the safedep/pmg repository or its release process and moves the v1 tag to a malicious revision.
  2. A developer opens a pull request or pushes to master, triggering this workflow.
  3. GitHub resolves uses: safedep/pmg@v1 to the attacker-controlled revision and runs its code in the Start PMG step.
  4. That code accesses the step's api-key and tenant-id inputs, which contain ${{ secrets.SAFEDEP_API_KEY }} and ${{ secrets.SAFEDEP_TENANT_ID }}, then sends them to an attacker-controlled endpoint or uses them against the SafeDep service.

The risk is specific to the mutable v1 reference; the other-party action can change without any change to this workflow.

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. Identify the full 40-character commit SHA that corresponds to the trusted v1 release of safedep/pmg.

  2. Replace the mutable tag with that commit SHA and retain the version comment for maintainability:
    uses: safedep/pmg@<40-character-commit-sha> # v1

  3. Confirm that the pinned commit is the intended safedep/pmg release before allowing the action to access SAFEDEP_API_KEY and SAFEDEP_TENANT_ID. A commit SHA cannot be silently repointed like a tag or branch.

💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 30 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.

Comment thread .github/workflows/ci.yml
steps:
- uses: actions/checkout@v2
- name: Start PMG
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 workflow runs safedep/pmg from the mutable v1 tag, allowing a repointed action to execute in CI. A compromised action could steal PMG credentials and the publish job’s GEM_API_KEY, then publish malicious Ruby gems.

More details about this

uses: safedep/pmg@v1 follows the mutable v1 tag rather than an immutable commit. The owner of safedep/pmg (or an attacker who compromises that repository) can move v1 to a different commit without changing this workflow, causing future runs to execute attacker-controlled code.

A plausible attack is:

  1. An attacker gains control of the safedep/pmg repository and repoints the v1 tag to a malicious action.
  2. A subsequent build or publish run starts the Start PMG step and executes that malicious action before the Ruby tests or gem publication.
  3. The action reads the job's SAFEDEP_API_KEY, SAFEDEP_TENANT_ID, and—especially in publishGEM_API_KEY from the runner environment or process context.
  4. Using GEM_API_KEY, the attacker can publish unauthorized Ruby gems or tampered versions; they can also alter files or artifacts used by later steps. The same mutable safedep/pmg@v1 reference appears in both jobs, so both execution paths are exposed.

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. Identify the trusted 40-character commit SHA for the safedep/pmg release corresponding to v1 from the action’s official repository or release metadata.

  2. Replace both occurrences of safedep/pmg@v1 with the full commit SHA, retaining the version as a comment. For example: uses: safedep/pmg@<40-character-commit-sha> # v1

  3. Confirm that the uses: values in both the build/test job and the publish job use the same immutable SHA and do not use a branch or version tag.

💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 80 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.

Comment thread .github/workflows/ci.yml
- name: Checkout
uses: actions/checkout@v2
- name: Start PMG
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 workflow executes safedep/pmg@v1, a mutable action reference that can silently be repointed to attacker-controlled code. A compromised action could access PMG secrets and tamper with or exfiltrate Ruby build and release artifacts.

More details about this

The Start PMG step runs safedep/pmg@v1, where v1 is a mutable branch or tag rather than an immutable commit. The action owner—or someone who compromises that repository—can move v1 to a malicious commit without changing this workflow, causing future build, test, or publish jobs to execute attacker-controlled code.

A plausible attack is:

  1. An attacker gains control of the safedep/pmg repository and repoints v1 to a malicious release.
  2. The next workflow run downloads that release when it reaches uses: safedep/pmg@v1.
  3. The malicious action executes with the job's permissions and can read ${{ secrets.SAFEDEP_API_KEY }} and ${{ secrets.SAFEDEP_TENANT_ID }} supplied to the PMG step.
  4. It can exfiltrate those credentials, inspect the checked-out Ruby source and built gems, or tamper with artifacts before actions/upload-artifact@v3 uploads them. In publish, the same action runs in the tag-triggered release path, so a compromised v1 can affect released packages without a workflow-file change.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1; replace with the verified 40-character commit SHA for the trusted v1 release
View step-by-step instructions
  1. Replace the mutable tag in the PMG step with the full 40-character commit SHA for the intended safedep/pmg v1 release: uses: safedep/pmg@<40-character-commit-sha>.
  2. Confirm that the SHA belongs to the trusted safedep/pmg repository and corresponds to the v1 release, then optionally retain the version as a comment: uses: safedep/pmg@<40-character-commit-sha> # v1.
  3. Apply the same pin to each safedep/pmg@v1 reference in this workflow. A commit SHA prevents the action owner from silently repointing the tag to different code.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 51 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.

Comment thread .github/workflows/ci.yml
steps:
- uses: actions/checkout@v3
- name: Start PMG
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 workflow runs safedep/pmg@v1, so the action can change silently when its tag is repointed. A compromised release could execute with SafeDep secrets and exfiltrate credentials or alter the build.

More details about this

The Start PMG step runs safedep/pmg@v1, where v1 is a mutable tag rather than an immutable commit. The action owner—or an attacker who compromises that repository—can repoint v1 to a malicious commit without changing this workflow.

A plausible attack is:

  1. An attacker gains control of the safedep/pmg repository and moves the v1 tag to a malicious release.
  2. A push to master or a matching version tag starts this CI workflow and downloads the newly tagged code for safedep/pmg.
  3. The malicious action executes during Start PMG with server-mode: true and access to secrets.SAFEDEP_API_KEY and secrets.SAFEDEP_TENANT_ID.
  4. It can send those credentials, repository contents, or build data to an attacker-controlled server before the later bundle install and gem build steps run, potentially allowing unauthorized access to the SafeDep tenant or tampering with the build.

Because the workflow text remains unchanged, ordinary code review may not reveal when the action's behavior changes.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
uses: safedep/pmg@v1
uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
# Human intervention required: replace the placeholder with the verified 40-character commit SHA for the trusted v1 release.
View step-by-step instructions
  1. Resolve the trusted commit SHA for the safedep/pmg v1 release from the action’s official repository or release metadata.
  2. Replace the mutable tag with the full 40-character commit SHA, keeping the version comment for readability: uses: safedep/pmg@<40-character-commit-sha> # v1
  3. Verify that the selected commit belongs to the intended v1 release and is not a branch or abbreviated SHA.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 19 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.

Comment thread .github/workflows/pmg-test.yml Outdated
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}

- uses: actions/setup-node@v4

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608.

🥳 Fixed in commit 67bea21 🥳

Comment thread .github/workflows/pmg-test.yml Outdated
runs-on: ubuntu-latest
steps:
- name: Setup PMG proxy
uses: safedep/pmg@v1

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608.

Removed in commit 0c6e9ea

Comment thread .github/workflows/pmg-test.yml Outdated
api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }}
tenant-id: ${{ secrets.PMG_TENANT_ID }}

- uses: ruby/setup-ruby@v1

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608.

🧼 Fixed in commit 67bea21 🧼

Comment thread .github/workflows/pmg-test.yml Outdated
runs-on: ubuntu-latest
steps:
- name: Setup PMG proxy
uses: safedep/pmg@v1

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608.

🎈 Removed in commit 0c6e9ea 🎈

Brings this repo onto the same integration used in ai-playbook and i18nify,
with the enforcement fix from blade.

- Setup step renamed to "Setup PMG proxy" and given `id: pmg-setup`, so the
  enforce step can tell whether setup actually ran.
- Enforce step runs `--fail-on-violation` only when setup succeeded. With a
  bare `if: always()`, any failure before the PMG step makes GitHub skip
  setup while still running enforce, which then dies with
  `pmg: command not found` (exit 127) and buries the real error.
- Removed additions that are not part of the reference integration:
  `permissions:` blocks, workflow comments, pinned action SHAs and
  non-standard step names.
- Added pmg-test.yml, byte-identical to the copy in ai-playbook and i18nify,
  which demonstrates the proxy blocking a known-malicious package and
  syncing the event to SafeDep Cloud.

The workflow files are now the master versions plus the two PMG steps and
nothing else: 60 lines added, none removed or modified.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Comment thread .github/workflows/pmg-test.yml Outdated
Comment on lines +47 to +53
run: |
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 20
echo "$NVM_DIR/versions/node/$(nvm version 20)/bin" >> $GITHUB_PATH

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A run: step pipes the output of curl or wget directly into a shell interpreter. This is the "curl | bash" install pattern — if the remote server is compromised or the URL is hijacked, an attacker can execute arbitrary code in your CI runner. Consider downloading the file first, verifying its checksum or signature, and then executing it.

🧼 Removed in commit 0c6e9ea 🧼

Comment thread .github/workflows/pmg-test.yml Outdated
Comment on lines +21 to +27
run: |
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm install 20
echo "$NVM_DIR/versions/node/$(nvm version 20)/bin" >> $GITHUB_PATH

@semgrep-code-razorpay semgrep-code-razorpay Bot Aug 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A run: step pipes the output of curl or wget directly into a shell interpreter. This is the "curl | bash" install pattern — if the remote server is compromised or the URL is hijacked, an attacker can execute arbitrary code in your CI runner. Consider downloading the file first, verifying its checksum or signature, and then executing it.

Removed in commit 0c6e9ea

pmg-test.yml was added alongside the PMG integration purely to prove the proxy
behaves correctly inside this repository's own CI environment. It ran two jobs:
one installing a known-clean package to confirm PMG does not block legitimate
traffic, and one installing the deliberately-flagged [email protected] to
confirm the block is caught and `pmg proxy stop --fail-on-violation` fails the
job as intended.

That validation is now complete across every repository in this rollout, so the
workflow has served its purpose. Leaving it in place would mean a permanent CI
job that installs a deliberately-flagged package on every push and pull request
- burning runner time and producing a red check that is expected-to-fail, which
is exactly the kind of noise that trains people to ignore CI signal.

The PMG integration itself is untouched. The safedep/pmg setup steps and the
`pmg proxy stop --fail-on-violation` enforcement steps in this repository's real
build and test workflows remain exactly as they were.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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