In the previous tutorial, you learned how to protect your application with security headers. But even if your code is perfect, a single vulnerable dependency can compromise everything. In this article, you will learn how to scan dependencies for vulnerabilities, prevent supply chain attacks, and keep your software secure.

The Supply Chain Problem

Modern software depends on hundreds of third-party packages. A typical Node.js project has 500-1500 dependencies. A Go project has 50-200. Each dependency is code written by someone else — and any of them could contain a vulnerability.

Real-world supply chain attacks:

  • Log4Shell (2021) — a critical vulnerability in Log4j, a Java logging library used by millions of applications. It allowed remote code execution. Almost every Java application was affected.
  • SolarWinds (2020) — attackers compromised the build system and injected malware into a software update. 18,000 organizations installed the backdoored update, including US government agencies.
  • xz-utils (2024) — a maintainer spent years gaining trust, then injected a backdoor into the compression library used by SSH. Caught by a developer who noticed a 500ms slowdown.
  • event-stream (2018) — an attacker gained access to a popular npm package and added code to steal cryptocurrency wallets.

The OWASP Top 10 highlights software supply chain failures because these attacks are growing rapidly.

Level 1: Know Your Dependencies

The first step is knowing what you depend on. Most developers cannot list all their transitive dependencies.

List Dependencies

Go:

# List all dependencies (direct and transitive)
go list -m all

# Show the dependency graph
go mod graph

Python:

# List installed packages
pip freeze > requirements.txt

# Show dependency tree
pip install pipdeptree
pipdeptree

Node.js:

# List all dependencies
npm list --all

# Show only production dependencies
npm list --production

Kotlin/Java (Gradle):

# List all dependencies
./gradlew dependencies

# List only runtime dependencies
./gradlew dependencies --configuration runtimeClasspath

Software Bill of Materials (SBOM)

An SBOM is a complete list of every component in your software — including dependencies, versions, and licenses. Think of it as an ingredients list for software.

Generate an SBOM with Syft:

# Install Syft
brew install syft

# Generate SBOM from a directory
syft dir:. -o spdx-json > sbom.json

# Generate SBOM from a Docker image
syft myapp:latest -o cyclonedx-json > sbom.json

SBOMs are becoming required by regulation. The US Executive Order on Cybersecurity (2021) requires SBOMs for software sold to the government.

Level 2: Scan for Known Vulnerabilities

Vulnerability databases track known security issues in packages. The most important databases are:

  • CVE (Common Vulnerabilities and Exposures) — the global standard
  • NVD (National Vulnerability Database) — US government database
  • GitHub Advisory Database — GitHub’s curated database
  • OSV (Open Source Vulnerabilities) — Google’s database for open source

Scanning Tools

Go:

# Built-in vulnerability scanner (since Go 1.18)
govulncheck ./...

# Example output:
# Vulnerability #1: GO-2024-2687
#   A malicious HTTP redirect can cause sensitive headers to be
#   unexpectedly forwarded.
#   Found in: net/http@go1.21.0
#   Fixed in: net/http@go1.21.8

Python:

# pip-audit — scans Python dependencies
pip install pip-audit
pip-audit

# Safety — checks requirements.txt
pip install safety
safety check -r requirements.txt

Node.js:

# Built-in audit command
npm audit

# Fix automatically
npm audit fix

# Example output:
# found 3 vulnerabilities (1 low, 1 moderate, 1 high)

Multi-language: Trivy

# Install Trivy
brew install trivy

# Scan a project directory
trivy fs .

# Scan a Docker image
trivy image myapp:latest

# Scan with severity filter
trivy fs --severity HIGH,CRITICAL .

Multi-language: Snyk

# Install Snyk CLI
npm install -g snyk

# Authenticate
snyk auth

# Scan project
snyk test

# Monitor for new vulnerabilities
snyk monitor

Grype (Another Scanner)

# Install Grype
brew install grype

# Scan a directory
grype dir:.

# Scan a Docker image
grype myapp:latest

Level 3: Automate Scanning in CI/CD

Manual scanning is not enough. Automate it so every commit and pull request is scanned.

GitHub Actions with Trivy

# .github/workflows/security.yml
name: Security Scan
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

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

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@v0.20.0
        with:
          scan-type: 'fs'
          scan-ref: '.'
          severity: 'HIGH,CRITICAL'
          exit-code: '1'  # Fail the build on high/critical vulnerabilities

GitHub Dependabot

Dependabot automatically creates pull requests to update vulnerable dependencies.

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

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

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

Renovate (Alternative to Dependabot)

Renovate is more configurable than Dependabot and supports more ecosystems.

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"]
  },
  "packageRules": [
    {
      "matchUpdateTypes": ["patch"],
      "automerge": true
    }
  ]
}

Level 4: Prevent Supply Chain Attacks

Pin Dependencies

Always pin exact versions. Do not use version ranges.

# BAD — allows any minor/patch update
requests>=2.28.0
lodash^4.17.0

# GOOD — exact version
requests==2.31.0
lodash@4.17.21

Use lockfiles:

  • Go: go.sum (automatically generated)
  • Python: requirements.txt with pinned versions, or use poetry.lock
  • Node.js: package-lock.json or yarn.lock
  • Kotlin/Java: Gradle lockfiles or version catalogs

Verify Package Integrity

Go:

Go modules use go.sum to verify checksums. The Go checksum database (sum.golang.org) provides a global ledger of module hashes.

# Verify all modules
go mod verify

Node.js:

npm uses package-lock.json with integrity hashes.

"lodash": {
  "version": "4.17.21",
  "integrity": "sha512-v2kDE..."
}

Use Private Registries

For sensitive projects, mirror dependencies in a private registry. This protects against:

  • Package deletion (left-pad incident)
  • Account takeover of package maintainers
  • Typosquatting attacks

Options: Artifactory, Nexus, GitHub Packages, AWS CodeArtifact.

Evaluate Dependencies Before Adding

Before adding a new dependency, check:

QuestionWhere to Check
Is it actively maintained?GitHub: last commit date, open issues
How many downloads?npm, PyPI, pkg.go.dev stats
How many dependencies does it have?npm info <pkg> dependencies
Are there known vulnerabilities?snyk test <pkg> or npm audit
What license does it use?license field in package metadata
Can I write this myself?If it is 20 lines of code, do not add a dependency

Vulnerability Management Process

When a vulnerability is found:

  1. Assess severity. CVSS score: Critical (9.0-10.0), High (7.0-8.9), Medium (4.0-6.9), Low (0.1-3.9)
  2. Check exploitability. Is the vulnerable code actually reachable in your application?
  3. Update or mitigate. Upgrade to a patched version if available. If not, apply a workaround.
  4. Set deadlines. Critical: 24 hours. High: 7 days. Medium: 30 days. Low: 90 days.
  5. Document. Track the vulnerability, the fix, and the timeline.

Prevention Checklist

DefensePriorityNotes
Use lockfiles for all package managersHighEnsures reproducible builds
Run npm audit / govulncheck / pip-audit in CIHighCatch vulnerabilities before merge
Enable Dependabot or RenovateHighAutomated dependency updates
Pin exact dependency versionsHighPrevent unexpected updates
Scan Docker images with TrivyHighCatch vulnerabilities in containers
Generate SBOMs for releasesMediumRequired by some regulations
Use a private registry for productionMediumProtect against supply chain attacks
Review new dependencies before addingMediumMinimize your attack surface
Set vulnerability fix deadlines (SLA)MediumCritical: 24h, High: 7d, Medium: 30d
Verify package integrity (checksums)LowDetect tampered packages

What is Next?

In the next tutorial, you will learn about Security Logging and Monitoring — how to detect attacks in progress, what to log, and how to set up alerts that actually matter.