DevOpsWeb Development

Defending Against CI/CD Dependency Supply Chain Attacks

Defending software supply chain dependencies with package integrity verification in CI

Modern software applications are assemblies of thousands of open-source packages. A standard web application repository might contain 50,000 lines of proprietary code, but pull in more than 1,200 open-source dependencies totaling millions of lines of external code.

Attackers know this. Instead of trying to breach a hardened web application firewall, adversaries target the dependencies that run inside your build pipeline through package typosquatting, dependency confusion, and maintainer account takeovers.

Here is how senior engineering teams lock down their delivery pipelines against supply chain threats without crippling developer velocity.

Common Supply Chain Attack Vectors

Understanding how malicious code enters a pipeline is key to designing effective defenses:

  1. Typosquatting: An attacker publishes cross-envv or loadash, hoping a developer mistypes a package name during development.
  2. Lockfile Desynchronization: A pipeline running npm install rather than npm ci, which allows newer patch versions containing malicious code to be fetched dynamically during builds.
  3. Malicious Lifecycle Scripts: An npm or PyPI package executing an arbitrary post-install bash script (postinstall) that steals environment variables and CI credentials upon installation.

Supply Chain Defense Checklist

Security ControlImplementationAttack Vector PreventedFriction Level
Deterministic CI Installnpm ci / pip --require-hashesUnexpected floating patch updatesZero
Ignore Install Scriptsnpm ci --ignore-scriptsArbitrary post-install shell executionLow
Automated Dependency Auditingnpm audit / Grype in CIKnown CVE injectionLow
Internal Package Scoping@company/package-nameDependency confusion attacksLow
Artifact Provenance ValidationSLSA / Sigstore cosign verificationTampered third-party binariesMedium

Hardening Package Installation in GitHub Actions

The simplest and most effective improvement you can make to your CI workflow is disabling untrusted execution during dependency installation:

# .github/workflows/ci.yml
name: Secure Build and Test
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies securely
        # --ignore-scripts prevents malicious postinstall execution
        run: npm ci --ignore-scripts

      - name: Run security vulnerability scan
        run: npm audit --audit-level=high

      - name: Rebuild trusted native binaries if required
        # Explicitly build trusted native modules only
        run: npm rebuild sharp

Eliminating Dependency Confusion

If your organization uses internal packages (e.g. company-auth-token), configure your .npmrc to strictly resolve your private registry for your organizational scope:

# .npmrc
@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

This prevents the public npm registry from being queried for proprietary internal package names, neutralizing dependency confusion attacks.

To build security into every stage of your delivery cycle, explore our DevOps engineering services and read our checklist on securing software delivery pipelines.

Related Articles

Let's grow your business together.

Book a free consultation — no obligation, just honest advice.

Get a Free Quote