Skip to content

ci: integrate SafeDep PMG package guard into all workflow jobs - #488

Open
vanshika-verma-rzp wants to merge 4 commits into
masterfrom
chore/integrate-pmg-package-guard
Open

ci: integrate SafeDep PMG package guard into all workflow jobs#488
vanshika-verma-rzp wants to merge 4 commits into
masterfrom
chore/integrate-pmg-package-guard

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 all CI jobs so that package installs are routed through PMG's persistent proxy, which auto-blocks any package flagged as malicious at install time.

In CI, PMG is non-interactive: flagged packages are always blocked (never prompted), and the run fails on a violation.

Changes

File Job Install intercepted
.github/workflows/node.js.yml build npm install
.github/workflows/ci.yml test npm install
.github/workflows/ci.yml publish-npm npm ci

Every job now follows the documented pattern:

  1. Start PMGsafedep/pmg@v1 in server-mode: true, placed right after actions/checkout and before language setup / installs so installs are intercepted automatically.
  2. Enforce policy — a final pmg proxy stop --fail-on-violation step with if: always(), which fails the job on a block and flushes events even if an earlier step failed.

Credentials are wired via repo secrets SAFEDEP_API_KEY and SAFEDEP_TENANT_ID to link runs to SafeDep Cloud.

Scope note

  • All 3 jobs were integrated — none needed skipping, since PMG sits transparently in front of the existing install steps and does not alter build/test/publish behavior.
  • The diff contains only PMG additions. No existing steps, matrix versions, triggers, or unrelated config were modified, reordered, or removed.

Prerequisites before merge

Two repository secrets should be configured for cloud event sync:

  • SAFEDEP_API_KEY
  • SAFEDEP_TENANT_ID

Note: these are optional for blocking. PMG blocks malicious packages using SafeDep's free community intelligence even without credentials, so jobs will not break if the secrets are unset — the secrets only enable syncing block events to SafeDep Cloud / Endpoint Hub.

Verification

  • Both workflow files validated as well-formed YAML.
  • git diff reviewed: additions are limited strictly to the PMG start/stop steps.

Discussion thread: Slack

Route package installs through PMG's persistent proxy to auto-block
malicious packages at install time across every CI job.

- node.js.yml: build job
- ci.yml: test job
- ci.yml: publish-npm job

Each job starts safedep/pmg@v1 in server-mode after checkout and ends
with 'pmg proxy stop --fail-on-violation' (if: always()) to enforce the
policy and flush events.
Comment thread .github/workflows/node.js.yml Outdated
steps:
- uses: actions/checkout@v4
# Start PMG in server mode to block malicious packages at install time
- 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 and can silently change between workflow runs. A repointed tag could execute code with access to the PMG secrets and modify dependencies or build artifacts.

More details about this

The workflow starts the safedep/pmg GitHub Action with the mutable v1 tag. The action owner can move v1 to a different commit without changing this workflow, so a future run may execute attacker-controlled action code before npm install and before the later build and test steps. Because this step receives secrets.SAFEDEP_API_KEY and secrets.SAFEDEP_TENANT_ID, that code could read those values, send them to an external server, modify the workspace or package.json, and tamper with dependencies or build artifacts.

A plausible attack is:

  1. An attacker compromises the safedep repository or obtains permission to update its v1 tag.
  2. They repoint v1 to a malicious commit and wait for a push or pull-request workflow run on this repository.
  3. The uses: safedep/pmg@v1 step runs the malicious code with the workflow's token and access to SAFEDEP_API_KEY and SAFEDEP_TENANT_ID.
  4. The malicious action exfiltrates those secrets, then alters files used by npm install, npm run build --if-present, or npm test to introduce a backdoor into the resulting package or build output.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
- uses: safedep/pmg@v1
# Replace the placeholder with the verified 40-character commit SHA for the v1 release.
- uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
View step-by-step instructions
  1. Resolve the trusted commit SHA for the v1 release of safedep/pmg.
  2. Replace the mutable tag with that exact 40-character commit SHA, while keeping the version comment for readability:
    uses: safedep/pmg@<40-character-commit-sha> # v1
  3. Keep the existing with configuration unchanged. Pinning the action prevents the referenced code from changing silently if the v1 tag is moved.
💬 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.

Comment thread .github/workflows/ci.yml Outdated
steps:
- uses: actions/checkout@v4
# Start PMG in server mode to block malicious packages at install time
- 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:

Both jobs execute safedep/pmg@v1, whose tag can be silently repointed to attacker-controlled code. The action runs with SafeDep credentials and, in publish-npm, npm publishing secrets, allowing secret theft or unauthorized package releases.

More details about this

The test and publish-npm jobs run safedep/pmg@v1, where v1 is a mutable tag rather than an immutable action revision. The action executes with SAFEDEP_API_KEY and SAFEDEP_TENANT_ID; in publish-npm, it also runs before npm publish with access to NPM_TOKEN and NPM_SECRET_KEY.

A plausible attack is:

  1. An attacker compromises the safedep/pmg repository or its release process and moves the v1 tag to a malicious commit.
  2. A normal test or version-tag push starts either job, and GitHub fetches the new code behind safedep/pmg@v1.
  3. The malicious action runs inside the workflow and can read the SAFEDEP_* secrets; during publish-npm, it can also exfiltrate NPM_TOKEN and NPM_SECRET_KEY or alter the workspace before npm publish.
  4. Using the stolen npm credentials, the attacker can publish unauthorized package versions or otherwise abuse the package's release identity.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
- uses: safedep/pmg@v1
- uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
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 both mutable references with the same 40-character commit SHA, keeping the version comment for readability:
    uses: safedep/pmg@<40-character-commit-sha> # v1
  3. Verify that the pinned commit provides the required server-mode, api-key, and tenant-id inputs before applying it to both the test and publish-npm jobs.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 55 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 Outdated
steps:
- uses: actions/checkout@v4
# Start PMG in server mode to block malicious packages at install time
- 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 CI workflow runs safedep/pmg from the mutable v1 tag, so a retag or compromised action release can execute attacker-controlled code. The action receives SafeDep secrets and runs before dependency installation and other build steps, creating a path to credential theft and build tampering.

More details about this

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

A plausible attack is:

  1. An attacker compromises the safedep/pmg release account and moves v1 to a malicious commit.
  2. A push to master or a pull request starts the test job, and GitHub resolves safedep/pmg@v1 to that malicious commit.
  3. The action runs before npm install, with access to secrets.SAFEDEP_API_KEY and secrets.SAFEDEP_TENANT_ID through its with inputs. Malicious action code could exfiltrate those values and alter the runner environment or dependencies used by the later npm install, tests, and coverage steps.
  4. The stolen SafeDep credentials could then be used to access or abuse the associated tenant outside this workflow, while the compromised runner could tamper with build or test artifacts.

To resolve this comment:

✨ Commit fix suggestion

Suggested change
- uses: safedep/pmg@v1
- uses: safedep/pmg@<VERIFIED_VALUE_REQUIRED> # v1
View step-by-step instructions
  1. Resolve safedep/pmg version v1 to its trusted 40-character commit SHA from the official repository or release history.
  2. Replace the mutable tag in the workflow with that commit SHA and retain the version as a comment, for example: uses: safedep/pmg@<40-character-commit-sha> # v1
  3. Apply the same full-SHA pinning to any other third-party actions in this workflow that use tags such as @v4 or @v3, including actions/checkout, actions/setup-node, and codecov/codecov-action.
💬 Ignore this finding

Leave a nosemgrep comment directly above or at the end of line 22 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 9427ddf 🧹

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 d54d503

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 9427ddf

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 d54d503 🧁

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: 45 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 25, 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 d54d503 🧼

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 25, 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 d54d503 🚀

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