From caa8e8a0e63b8f07c7db03f7cb881c4c8ea0b807 Mon Sep 17 00:00:00 2001 From: Vanshika Verma Date: Tue, 25 Aug 2026 12:33:49 +0530 Subject: [PATCH 1/3] ci: integrate SafeDep PMG into GitHub Actions workflows Route CI package installs through SafeDep PMG's persistent proxy to block malicious packages before they run. Per job, following the SafeDep PMG GitHub Actions doc: - add permissions: contents: read - start PMG in server-mode after checkout (safedep/pmg@v1) - enforce policy with 'pmg proxy stop --fail-on-violation' (if: always()) Uses secrets PMG_PUBLIC_REPOS_TOKEN (api-key) and PMG_TENANT_ID (tenant-id). Applied to: crud_app, ingressroute_configurator, secret_cloner, sqs_app, sqs_configurator, semgrep. Skipped genesis.yml (Analysis job) - it calls a reusable workflow, which cannot contain steps or a job-level permissions block. --- .github/workflows/crud_app.yaml | 11 +++++++++++ .github/workflows/ingressroute_configurator.yaml | 11 +++++++++++ .github/workflows/secret_cloner.yaml | 11 +++++++++++ .github/workflows/semgrep.yaml | 13 ++++++++++++- .github/workflows/sqs_app.yaml | 11 +++++++++++ .github/workflows/sqs_configurator.yaml | 11 +++++++++++ 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.github/workflows/crud_app.yaml b/.github/workflows/crud_app.yaml index c369cff..28377ec 100644 --- a/.github/workflows/crud_app.yaml +++ b/.github/workflows/crud_app.yaml @@ -8,12 +8,20 @@ jobs: irc: runs-on: ubuntu-latest name: Build CRUD demo app image + permissions: + contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers @@ -36,3 +44,6 @@ jobs: tags: razorpay/devstack:crud_demo_app push: true file: ./example/apps/webapp/Dockerfile + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation diff --git a/.github/workflows/ingressroute_configurator.yaml b/.github/workflows/ingressroute_configurator.yaml index 6236450..222ea64 100644 --- a/.github/workflows/ingressroute_configurator.yaml +++ b/.github/workflows/ingressroute_configurator.yaml @@ -8,12 +8,20 @@ jobs: irc: runs-on: ubuntu-latest name: Build Ingressroute configurator image + permissions: + contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers @@ -36,3 +44,6 @@ jobs: tags: razorpay/devstack:irc push: true file: ./hooks/ingressroute_configurator/Dockerfile + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation diff --git a/.github/workflows/secret_cloner.yaml b/.github/workflows/secret_cloner.yaml index 6f1cc71..21804a8 100644 --- a/.github/workflows/secret_cloner.yaml +++ b/.github/workflows/secret_cloner.yaml @@ -8,12 +8,20 @@ jobs: secc: runs-on: ubuntu-latest name: Build SQS configurator image + permissions: + contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers @@ -36,3 +44,6 @@ jobs: tags: razorpay/devstack:sec push: true file: ./hooks/secret_cloner/Dockerfile + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation diff --git a/.github/workflows/semgrep.yaml b/.github/workflows/semgrep.yaml index bccd149..5ff69e1 100644 --- a/.github/workflows/semgrep.yaml +++ b/.github/workflows/semgrep.yaml @@ -7,12 +7,23 @@ jobs: semgrep: name: Scan runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: razorpay/checkout-action@30aedaf6f35b3b7756b8095789c1e18674bcf2f6 + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - uses: returntocorp/semgrep-action@v1 with: auditOn: push publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} publishDeployment: 339 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation \ No newline at end of file diff --git a/.github/workflows/sqs_app.yaml b/.github/workflows/sqs_app.yaml index 2545266..72dc7c7 100644 --- a/.github/workflows/sqs_app.yaml +++ b/.github/workflows/sqs_app.yaml @@ -8,12 +8,20 @@ jobs: irc: runs-on: ubuntu-latest name: Build SQS demo app image + permissions: + contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers @@ -36,3 +44,6 @@ jobs: tags: razorpay/devstack:sqs_demo_app push: true file: ./example/apps/sqs/Dockerfile + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation diff --git a/.github/workflows/sqs_configurator.yaml b/.github/workflows/sqs_configurator.yaml index ffc93a4..7f4ead4 100644 --- a/.github/workflows/sqs_configurator.yaml +++ b/.github/workflows/sqs_configurator.yaml @@ -8,12 +8,20 @@ jobs: sqsc: runs-on: ubuntu-latest name: Build SQS configurator image + permissions: + contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true + - name: Start PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers @@ -36,3 +44,6 @@ jobs: tags: razorpay/devstack:sqsc push: true file: ./hooks/sqs_configurator/Dockerfile + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation From 696bbfa1ea06e52107a43f7c4e075471502e70aa Mon Sep 17 00:00:00 2001 From: Vanshika Verma Date: Wed, 26 Aug 2026 02:32:42 +0530 Subject: [PATCH 2/3] Align PMG integration with the agreed standard 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: 75 lines added, none removed or modified. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/crud_app.yaml | 12 ++-- .../workflows/ingressroute_configurator.yaml | 12 ++-- .github/workflows/pmg-test.yml | 60 +++++++++++++++++++ .github/workflows/secret_cloner.yaml | 12 ++-- .github/workflows/semgrep.yaml | 13 +--- .github/workflows/sqs_app.yaml | 12 ++-- .github/workflows/sqs_configurator.yaml | 12 ++-- 7 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/pmg-test.yml diff --git a/.github/workflows/crud_app.yaml b/.github/workflows/crud_app.yaml index 28377ec..b7535d7 100644 --- a/.github/workflows/crud_app.yaml +++ b/.github/workflows/crud_app.yaml @@ -8,15 +8,14 @@ jobs: irc: runs-on: ubuntu-latest name: Build CRUD demo app image - permissions: - contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true - - name: Start PMG proxy + - name: Setup PMG proxy + id: pmg-setup uses: safedep/pmg@v1 with: server-mode: true @@ -46,4 +45,9 @@ jobs: file: ./example/apps/webapp/Dockerfile - name: Enforce PMG policy if: always() - run: pmg proxy stop --fail-on-violation + run: | + if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then + pmg proxy stop --fail-on-violation + else + pmg proxy stop || true + fi diff --git a/.github/workflows/ingressroute_configurator.yaml b/.github/workflows/ingressroute_configurator.yaml index 222ea64..83b84cf 100644 --- a/.github/workflows/ingressroute_configurator.yaml +++ b/.github/workflows/ingressroute_configurator.yaml @@ -8,15 +8,14 @@ jobs: irc: runs-on: ubuntu-latest name: Build Ingressroute configurator image - permissions: - contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true - - name: Start PMG proxy + - name: Setup PMG proxy + id: pmg-setup uses: safedep/pmg@v1 with: server-mode: true @@ -46,4 +45,9 @@ jobs: file: ./hooks/ingressroute_configurator/Dockerfile - name: Enforce PMG policy if: always() - run: pmg proxy stop --fail-on-violation + run: | + if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then + pmg proxy stop --fail-on-violation + else + pmg proxy stop || true + fi diff --git a/.github/workflows/pmg-test.yml b/.github/workflows/pmg-test.yml new file mode 100644 index 0000000..664eebf --- /dev/null +++ b/.github/workflows/pmg-test.yml @@ -0,0 +1,60 @@ +name: PMG Proxy Test + +on: + workflow_dispatch: + pull_request: + push: + +jobs: + test-pmg-allows-clean-install: + name: PMG - Clean package should not be blocked + runs-on: ubuntu-latest + steps: + - name: Setup PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} + + - name: Install npm via nvm + 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 + + - name: Install clean package (should succeed) + run: npm install lodash + + - name: Enforce PMG policy + if: always() + run: pmg proxy stop --fail-on-violation + + test-pmg-blocks-malicious-package: + name: PMG - Malicious package should be blocked + runs-on: ubuntu-latest + steps: + - name: Setup PMG proxy + uses: safedep/pmg@v1 + with: + server-mode: true + api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} + tenant-id: ${{ secrets.PMG_TENANT_ID }} + + - name: Install npm via nvm + 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 + + - name: Install flagged test package (PMG should block this) + continue-on-error: true + run: npm install --no-cache --prefer-online safedep-test-pkg@0.1.3 + + - name: Enforce PMG policy (expect failure — violation recorded) + if: always() + run: pmg proxy stop --fail-on-violation diff --git a/.github/workflows/secret_cloner.yaml b/.github/workflows/secret_cloner.yaml index 21804a8..0514565 100644 --- a/.github/workflows/secret_cloner.yaml +++ b/.github/workflows/secret_cloner.yaml @@ -8,15 +8,14 @@ jobs: secc: runs-on: ubuntu-latest name: Build SQS configurator image - permissions: - contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true - - name: Start PMG proxy + - name: Setup PMG proxy + id: pmg-setup uses: safedep/pmg@v1 with: server-mode: true @@ -46,4 +45,9 @@ jobs: file: ./hooks/secret_cloner/Dockerfile - name: Enforce PMG policy if: always() - run: pmg proxy stop --fail-on-violation + run: | + if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then + pmg proxy stop --fail-on-violation + else + pmg proxy stop || true + fi diff --git a/.github/workflows/semgrep.yaml b/.github/workflows/semgrep.yaml index 5ff69e1..bccd149 100644 --- a/.github/workflows/semgrep.yaml +++ b/.github/workflows/semgrep.yaml @@ -7,23 +7,12 @@ jobs: semgrep: name: Scan runs-on: ubuntu-latest - permissions: - contents: read steps: - uses: razorpay/checkout-action@30aedaf6f35b3b7756b8095789c1e18674bcf2f6 - - name: Start PMG proxy - uses: safedep/pmg@v1 - with: - server-mode: true - api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} - tenant-id: ${{ secrets.PMG_TENANT_ID }} - uses: returntocorp/semgrep-action@v1 with: auditOn: push publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} publishDeployment: 339 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Enforce PMG policy - if: always() - run: pmg proxy stop --fail-on-violation \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/sqs_app.yaml b/.github/workflows/sqs_app.yaml index 72dc7c7..14d8f3b 100644 --- a/.github/workflows/sqs_app.yaml +++ b/.github/workflows/sqs_app.yaml @@ -8,15 +8,14 @@ jobs: irc: runs-on: ubuntu-latest name: Build SQS demo app image - permissions: - contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true - - name: Start PMG proxy + - name: Setup PMG proxy + id: pmg-setup uses: safedep/pmg@v1 with: server-mode: true @@ -46,4 +45,9 @@ jobs: file: ./example/apps/sqs/Dockerfile - name: Enforce PMG policy if: always() - run: pmg proxy stop --fail-on-violation + run: | + if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then + pmg proxy stop --fail-on-violation + else + pmg proxy stop || true + fi diff --git a/.github/workflows/sqs_configurator.yaml b/.github/workflows/sqs_configurator.yaml index 7f4ead4..68d963b 100644 --- a/.github/workflows/sqs_configurator.yaml +++ b/.github/workflows/sqs_configurator.yaml @@ -8,15 +8,14 @@ jobs: sqsc: runs-on: ubuntu-latest name: Build SQS configurator image - permissions: - contents: read steps: - name: checkout id: checkout uses: actions/checkout@v2 with: submodules: true - - name: Start PMG proxy + - name: Setup PMG proxy + id: pmg-setup uses: safedep/pmg@v1 with: server-mode: true @@ -46,4 +45,9 @@ jobs: file: ./hooks/sqs_configurator/Dockerfile - name: Enforce PMG policy if: always() - run: pmg proxy stop --fail-on-violation + run: | + if [ "${{ steps.pmg-setup.outcome }}" = "success" ]; then + pmg proxy stop --fail-on-violation + else + pmg proxy stop || true + fi From fcbd981dd6318a715542182b061f10d4828be327 Mon Sep 17 00:00:00 2001 From: Vanshika Verma Date: Fri, 28 Aug 2026 14:52:47 +0530 Subject: [PATCH 3/3] Remove pmg-test.yml PMG validation workflow 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 safedep-test-pkg@0.1.3 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) --- .github/workflows/pmg-test.yml | 60 ---------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 .github/workflows/pmg-test.yml diff --git a/.github/workflows/pmg-test.yml b/.github/workflows/pmg-test.yml deleted file mode 100644 index 664eebf..0000000 --- a/.github/workflows/pmg-test.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: PMG Proxy Test - -on: - workflow_dispatch: - pull_request: - push: - -jobs: - test-pmg-allows-clean-install: - name: PMG - Clean package should not be blocked - runs-on: ubuntu-latest - steps: - - name: Setup PMG proxy - uses: safedep/pmg@v1 - with: - server-mode: true - api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} - tenant-id: ${{ secrets.PMG_TENANT_ID }} - - - name: Install npm via nvm - 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 - - - name: Install clean package (should succeed) - run: npm install lodash - - - name: Enforce PMG policy - if: always() - run: pmg proxy stop --fail-on-violation - - test-pmg-blocks-malicious-package: - name: PMG - Malicious package should be blocked - runs-on: ubuntu-latest - steps: - - name: Setup PMG proxy - uses: safedep/pmg@v1 - with: - server-mode: true - api-key: ${{ secrets.PMG_PUBLIC_REPOS_TOKEN }} - tenant-id: ${{ secrets.PMG_TENANT_ID }} - - - name: Install npm via nvm - 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 - - - name: Install flagged test package (PMG should block this) - continue-on-error: true - run: npm install --no-cache --prefer-online safedep-test-pkg@0.1.3 - - - name: Enforce PMG policy (expect failure — violation recorded) - if: always() - run: pmg proxy stop --fail-on-violation