Detecting NPM Package Owner Changes in CI: Supply Chain Defense (2025)

Package ownership and maintainer changes are high-signal events in software supply chains. A trustworthy package today can become risky tomorrow after a maintainer handoff or account compromise. This guide shows how to detect owner changes in CI and block merges until humans review them.
What to watch for
- New maintainer accounts added to a package (especially recently created accounts).
- Ownership transfer to a new scope/org.
- Sudden spike of releases after a long dormant period.
- New lifecycle scripts or native bindings introduced along with ownership change.
Approach: compare before vs after in CI
For each package that changed in the lockfile, get the previous maintainers/owner and the new set, then fail CI if the delta contains unknown or untrusted accounts. A simple baseline can use the public registry API, with a local allowlist for your org’s vetted packages.
GitHub Actions: detect maintainer changes
name: owner-change-guard
on: [pull_request]
jobs:
  check-owners:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: corepack enable
      - name: Compute changed packages from lockfile
        run: |
          git fetch origin ${{ github.base_ref }} --depth=1
          git checkout ${{ github.base_ref }}
          cp pnpm-lock.yaml pnpm-lock.base.yaml || true
          git checkout ${{ github.head_ref }}
          cp pnpm-lock.yaml pnpm-lock.head.yaml || true
          node -e '
            const fs=require("fs");
            const base=fs.readFileSync("pnpm-lock.base.yaml","utf8").split("
");
            const head=fs.readFileSync("pnpm-lock.head.yaml","utf8").split("
");
            const pkgre=/^s{2}([^s][^:]+):$/; // section headers like "  registry.npmjs.org/foo/1.2.3:"
            const get=set=>{const s=new Set(); for(const l of set){const m=l.match(pkgre); if(m){const id=m[1]; const name=id.split("/").slice(-2,-1)[0]; s.add(name);} } return s};
            const b=get(base), h=get(head);
            const added=[...h].filter(x=>!b.has(x));
            const changed=[...added];
            console.log(JSON.stringify({changed}));
          ' > changed.json
          cat changed.json
      - name: Fetch maintainers and compare
        env:
          ALLOWLIST: "^(esbuild|sharp|@your-scope/)"
        run: |
          CHANGED=$(jq -r '.changed[]' changed.json || true)
          FAIL=0
          for PKG in $CHANGED; do
            echo "Checking $PKG";
            BASE=$(curl -sL https://registry.npmjs.org/$PKG | jq -r '.maintainers[]?.name' | sort -u | tr '
' ' ')
            # Optionally compare previous version maintainers by pinning a specific version from the base lockfile
            echo "Maintainers: $BASE";
            echo "$PKG" | grep -Eq "$ALLOWLIST" && { echo "allowlisted"; continue; }
            # Organization policy could require manual approval for any owner change event
          done
          exit $FAIL
      - name: Fail if unexpected owner changes detected
        if: failure()
        run: exit 1
Notes: npm maintains an owner/maintainer list per package via the registry API; results may vary per package. For critical packages, mirror metadata to your own database and require a manual approval workflow on any owner changes.
Policy refinements
- Enrich with account age, publish cadence, and 2FA signals when available.
- Require second reviewer approval if an owner change coincides with lifecycle scripts.
- Auto-open a security review task when a high-risk package changes owners.
Related internal reading
- NPM Supply Chain Attack: What Happened and How to Protect Your CI/CD (2025)
- Supply Chain Security in Code Review: Dependency Analysis Best Practices
- Security Code Review That Stops 99% of Vulnerabilities (2025)
References
- NPM CLI — Owner and team management: https://docs.npmjs.com/cli/v10/commands/npm-owner
- NPM Registry API — Package metadata: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md
Stop Supply Chain Surprises in PRs
Propel flags risky dependency diffs, owner changes, and lifecycle scripts directly in code review.




