DEV Community

ThankGod Chibugwum Obobo
ThankGod Chibugwum Obobo

Posted on • Originally published at actocodes.hashnode.dev

Dependency Auditing at Scale: How to Automate Supply Chain Security with Dependabot and Snyk

The average modern web application depends on hundreds, sometimes thousands, of transitive npm packages. Each one is a potential attack vector, a vulnerability waiting to be disclosed, a maintainer account waiting to be compromised, or a malicious update waiting to be published. Manually tracking the security posture of a dependency tree this large is not just impractical at any meaningful scale, it is impossible.

Supply chain security has moved from a niche compliance concern to a top-tier engineering priority, driven by high-profile incidents like the event-stream backdoor, the ua-parser-js compromise, and the XZ Utils backdoor that nearly compromised SSH across major Linux distributions. Attackers have learned that compromising one widely-used package is more efficient than attacking a thousand individual applications.

This guide covers how to build an automated dependency auditing pipeline using GitHub Dependabot and Snyk, two complementary tools that, used together, give you continuous vulnerability detection, automated remediation, and CI/CD gates that stop vulnerable code before it ships.

Why Manual Dependency Management Doesn't Scale

Consider a mid-sized application with 40 direct dependencies. Each of those typically pulls in 10–30 transitive dependencies of its own, meaning your actual dependency tree can easily exceed 800–1200 packages. Each package:

  • Has its own maintainers, release cadence, and security practices
  • Can be updated at any time, potentially introducing a vulnerability or malicious code
  • May depend on other packages with their own vulnerabilities
  • Often includes postinstall scripts that execute arbitrary code on npm install

Manually monitoring CVE databases, cross-referencing them against your package-lock.json, and tracking remediation status across dozens of services is not a sustainable practice. It requires automation, both for detection and for response.

Understanding the Two-Tool Strategy

Dependabot (native to GitHub) and Snyk (a dedicated security platform) overlap in purpose but differ in depth and capability. Understanding the distinction helps you use both effectively rather than redundantly.

Capability Dependabot Snyk
Vulnerability database GitHub Advisory Database Snyk's proprietary database (broader, faster disclosure)
Automated PRs for fixes Yes Yes
License compliance scanning No Yes
Container image scanning Limited Yes
Infrastructure as Code scanning No Yes
Code-level vulnerability scanning (SAST) No Yes (Snyk Code)
Native GitHub integration Built-in, free Requires app installation
Fix prioritization by exploitability Basic Advanced risk scoring
Cost Free for public & private repos Free tier is limited, pay for scale

Recommended approach: Use Dependabot as your baseline, always-on dependency update mechanism, it's free, native, and requires minimal setup. Layer Snyk on top for deeper vulnerability intelligence, license compliance, and broader scanning coverage (containers, IaC, code-level issues) across your most critical services.

Step 1 - Configure Dependabot

Dependabot is configured via a single YAML file per repository. Start with version updates and security updates enabled:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    open-pull-requests-limit: 10
    groups:
      production-dependencies:
        dependency-type: "production"
        update-types: ["minor", "patch"]
      development-dependencies:
        dependency-type: "development"
        update-types: ["minor", "patch", "major"]
    ignore:
      - dependency-name: "legacy-package"
        update-types: ["version-update:semver-major"]
    labels:
      - "dependencies"
      - "automated"
    reviewers:
      - "platform-team"
    commit-message:
      prefix: "chore(deps)"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "github-actions"
    directory: "/.github/workflows"
    schedule:
      interval: "weekly"
Enter fullscreen mode Exit fullscreen mode

Key configuration decisions:

  • groups consolidates multiple minor/patch updates into a single PR, dramatically reducing PR noise for large dependency trees.
  • open-pull-requests-limit prevents Dependabot from overwhelming your review queue.
  • Separate ecosystems (docker, github-actions) ensure your container base images and CI workflow actions are also kept current, both common and overlooked attack surfaces.

Enabling Security-Only Updates

Security updates are enabled by default once Dependabot alerts are turned on for the repository, and they bypass the schedule, firing immediately when a new vulnerability is disclosed for a dependency you use:

Repository → Settings → Code security and analysis
Dependabot alerts
Dependabot security updates
Dependabot version updates
Enter fullscreen mode Exit fullscreen mode

Step 2 - Configure Snyk for Deeper Scanning

Install the Snyk GitHub integration and add a .snyk policy file to your repository:

npm install -g snyk
snyk auth
snyk test
Enter fullscreen mode Exit fullscreen mode

Generate a baseline .snyk policy file to manage known, accepted-risk vulnerabilities with documented justification:

# .snyk
version: v1.5.0
ignore:
  SNYK-JS-LODASH-1040724:
    - '*':
        reason: >
          Vulnerability is in a dev-only build tool, not present in
          production bundle. Tracked in JIRA-4521 for upgrade in Q3.
        expires: 2026-09-30T00:00:00.000Z
patch: {}
Enter fullscreen mode Exit fullscreen mode

Never use .snyk to silently suppress vulnerabilities. Every ignored entry requires a documented reason and an expiration date, forcing periodic re-evaluation rather than permanent suppression.

Snyk CI Integration

# .github/workflows/snyk-security.yml
name: Snyk Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  snyk-dependencies:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high --fail-on=upgradable

      - name: Run Snyk Code (SAST)
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          command: code test
          args: --severity-threshold=high

  snyk-container:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build container image
        run: docker build -t app:${{ github.sha }} .

      - name: Run Snyk Container scan
        uses: snyk/actions/docker@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          image: app:${{ github.sha }}
          args: --severity-threshold=high
Enter fullscreen mode Exit fullscreen mode

The --fail-on=upgradable flag is important, it only fails the build for vulnerabilities that have an available fix, preventing your pipeline from blocking on issues you currently have no way to remediate.

Step 3 - License Compliance Scanning

Open source licenses carry legal obligations, some permissive (MIT, Apache 2.0), others restrictive (GPL, AGPL) in ways that can create compliance risk for commercial products. Snyk's license scanning catches these automatically:

# Add license policy enforcement
- name: Snyk License Compliance
  uses: snyk/actions/node@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    command: test
    args: --severity-threshold=high --fail-on=all
            --policy-path=.snyk-license-policy.json
Enter fullscreen mode Exit fullscreen mode

Configure license policies in the Snyk dashboard to flag or block specific license types:

Disallow:  GPL-3.0, AGPL-3.0, SSPL-1.0
Warn:      LGPL-2.1, MPL-2.0
Allow:     MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC
Enter fullscreen mode Exit fullscreen mode

This is particularly critical for companies building commercial or closed-source products, an AGPL-licensed transitive dependency can create unexpected obligations to release your own source code.

Step 4 - Prioritizing Remediation with Risk Scoring

Not every vulnerability deserves the same urgency. A critical CVE in a dependency that's only used in your test suite is lower risk than a medium-severity CVE in a package that processes user input in production.

Snyk's risk scoring considers exploit maturity, whether the vulnerable code path is actually reachable, and the dependency's position in your tree (direct vs. transitive) to prioritize remediation:

# View prioritized vulnerabilities sorted by risk score
snyk test --json | snyk-to-html -o report.html

# Or query via API for custom dashboards
curl -H "Authorization: token $SNYK_TOKEN" \
  "https://api.snyk.io/v1/org/$ORG_ID/projects/$PROJECT_ID/issues"
Enter fullscreen mode Exit fullscreen mode

Establish a remediation SLA based on severity and reachability:

Severity Reachable in Production Code Remediation SLA
Critical Yes 24–48 hours
Critical No (dev/test only) 1 week
High Yes 1 week
High No 2 weeks
Medium Yes 2 weeks
Medium No Next regular update cycle
Low Any Tracked, no SLA

Step 5 - Automating Patch PRs and Auto-Merge

For low-risk updates, patch and minor version bumps with passing tests, automate the entire remediation loop:

# .github/workflows/automerge-dependabot.yml
name: Auto-merge Dependabot PRs

on: pull_request

permissions:
  pull-requests: write
  contents: write

jobs:
  automerge:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    steps:
      - name: Fetch Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v2
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Auto-approve patch and minor updates
        if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
        run: gh pr review --approve "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Enable auto-merge
        if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
        run: gh pr merge --auto --squash "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Auto-merge should only apply to patch/minor updates and require your full CI suite (tests, linting, security scans) to pass first. gh pr merge --auto waits for all required status checks before merging. Major version updates should always require human review, since they're more likely to introduce breaking changes.

Step 6 - Software Bill of Materials (SBOM) Generation

Beyond vulnerability scanning, regulatory and enterprise procurement requirements increasingly demand a Software Bill of Materials, a complete inventory of every component in your software supply chain:

# Generate an SBOM in CycloneDX format using Snyk
snyk sbom --format=cyclonedx1.5+json --org=$ORG_ID > sbom.json
Enter fullscreen mode Exit fullscreen mode
# .github/workflows/sbom.yml
- name: Generate SBOM
  run: snyk sbom --format=cyclonedx1.5+json > sbom.json
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

- name: Upload SBOM as release artifact
  uses: actions/upload-artifact@v4
  with:
    name: sbom
    path: sbom.json
Enter fullscreen mode Exit fullscreen mode

Store SBOMs alongside each release artifact, they provide auditable proof of exactly what shipped, which is increasingly required for enterprise customers and government contracts (e.g., U.S. Executive Order 14028 compliance).

Step 7 - Blocking Malicious Packages Proactively

Beyond known CVEs, supply chain attacks often involve packages with no disclosed vulnerability, because the malicious behavior was intentionally introduced by a compromised maintainer account. Snyk and GitHub both maintain databases of known-malicious packages distinct from standard CVE tracking:

# Block installation of known-malicious packages at install time
- name: Check for malicious packages
  run: |
    npx socket-security audit  # cross-references known malicious package database
Enter fullscreen mode Exit fullscreen mode

Combine this with npm ci --ignore-scripts in CI to prevent postinstall scripts from executing arbitrary code during dependency installation, a common vector for credential exfiltration:

npm ci --ignore-scripts
# Run necessary build scripts explicitly and individually instead of trusting postinstall hooks
npm run build
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls to Avoid

Treating Dependabot PR volume as noise to ignore. A repository with 60 open Dependabot PRs signals the automation is not being acted on, defeating its purpose. Use grouping and auto-merge for low-risk updates to keep the queue manageable.

Scanning only direct dependencies. The majority of real-world vulnerabilities live in transitive dependencies your team has never directly chosen. Ensure your scanning tools traverse the full dependency tree, not just package.json.

No reachability analysis. Flagging every CVE in every dependency, even ones whose vulnerable code path is never executed, creates alert fatigue. Use tools with reachability analysis (Snyk Code, Socket) to prioritize what's actually exploitable in your application.

Ignoring container and IaC layers. Application dependencies are only one layer of your supply chain. Base container images and Terraform providers carry their own vulnerabilities and deserve the same scanning rigor.

Conclusion

Supply chain security at scale cannot be a manual process, the sheer size of modern dependency trees makes that mathematically impossible to sustain. Dependabot provides a free, always-on baseline for dependency updates and security patches. Snyk adds depth: license compliance, container and IaC scanning reachability-aware risk scoring, and code-level SAST.

Used together, with auto-merge for low-risk updates, documented exception policies for accepted risk, and SBOM generation for auditability, you transform dependency management from a reactive scramble after each disclosed CVE into a continuous, automated engineering practice.

The goal isn't zero vulnerabilities, that's not achievable in any non-trivial dependency tree. The goal is continuous visibility, fast remediation for what's exploitable, and an audit trail for everything else.

Running a polyglot stack with Python, Go, and Java services alongside Node.js? Both Dependabot and Snyk support multi-ecosystem scanning natively — the configuration patterns in this guide apply with ecosystem-specific adjustments. Drop your stack in the comments.

Top comments (0)