---
title: "How to audit dependencies"
slug: "audit-dependencies"
category: "howto"
tags: ["howto", "security", "npm", "python", "ci", "tutorial", "dependencies"]
status: "stable"
last_updated: 2026-05-14
summary: "Run npm audit or pip-audit, classify findings by severity, patch or accept risk, re-test, then add a scheduled CI job to catch new CVEs."
related: ["[[coding/general-principles]]", "[[coding/python-security]]", "[[tooling/github-actions]]", "[[tooling/github]]", "[[cheatsheets/bash-one-liners]]", "[[coding/testing]]", "[[ops/github-pages]]"]
---

## Overview

Dependency audits find known CVEs before they reach production. Run `npm audit` or `pip-audit` to surface findings, classify each as patch-now, accept, or ignore, apply fixes, re-run to confirm the count is zero, and wire a scheduled CI job so new vulnerabilities surface automatically. See [[coding/python-security]] for deeper Python hardening.

## Prerequisites

- Node 18+ for `npm audit`, or Python 3.9+ and `pip install pip-audit` for Python projects.
- A lockfile present (`package-lock.json`, `yarn.lock`, or `poetry.lock`). Without a lockfile, audit results are imprecise.
- Write access to the repo; you will patch dependencies and open a PR.

## Steps

### 1. Run the audit

For Node projects:

```bash
npm audit --json > audit-report.json
npm audit            # human-readable summary
```

For Python projects using pip:

```bash
pip-audit --format json -o audit-report.json
pip-audit             # human-readable summary
```

For Python projects using Poetry:

```bash
pip-audit --requirement <(poetry export -f requirements.txt --without-hashes)
```

The output lists each vulnerability with a CVE identifier, severity (critical, high, moderate, low), and the affected package range.

### 2. Classify findings

Not every finding warrants an immediate patch. Apply this triage:

- **Critical or high with a reachable code path**: patch immediately.
- **Moderate**: patch before the next release; accept short-term if a fix requires a major version bump that breaks the build.
- **Low**: log and schedule; rarely worth the disruption of a large upgrade.
- **Dev-only**: a vulnerability in a test tool that never ships to production can be accepted with a documented reason.

Create an `audit-exceptions.json` if your tooling supports it (npm does with `npm audit fix --audit-level`), or document accepted findings in a comment in `package.json` under `"auditExemptions"`.

### 3. Patch automatically where safe

For Node, apply non-breaking patches in one command:

```bash
npm audit fix
```

For breaking patches (major version bumps), review the changelog before running:

```bash
npm audit fix --force   # use with caution; may introduce breaking changes
```

For Python, upgrade the specific package:

```bash
pip install --upgrade cryptography
pip-audit                         # confirm count drops
pip freeze > requirements.txt     # or update pyproject.toml manually
```

### 4. Re-test after patching

A dependency upgrade that passes `audit` can still break the application. Run the full test suite before committing.

```bash
# Node
npm test

# Python
pytest -x
```

If tests fail, check the package's changelog for breaking changes, then either pin to the last safe version or update your code. See [[coding/testing]] for test setup patterns.

### 5. Set up scheduled CI

A one-time audit is not enough; new CVEs are published daily. Add a scheduled workflow to [[tooling/github-actions]] that runs the audit and fails the job if critical findings appear.

```yaml
# .github/workflows/audit.yml
name: Dependency audit
on:
  schedule:
    - cron: "0 8 * * 1"   # every Monday at 08:00 UTC
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: "22" }
      - run: npm ci
      - run: npm audit --audit-level=high
```

For Python, substitute:

```yaml
      - run: pip install pip-audit
      - run: pip-audit --requirement requirements.txt --fail-on CRITICAL
```

The job emails the repository owner on failure via GitHub's default notification settings.

## Verify it worked

```bash
# Node: zero high/critical findings
npm audit --audit-level=high
# Expected: "found 0 vulnerabilities"

# Python
pip-audit --requirement requirements.txt
# Expected: "No known vulnerabilities found"

# Confirm CI workflow is scheduled
git log --oneline .github/workflows/audit.yml
```

## Common errors

- **`npm audit` reports vulnerabilities in `devDependencies`**. Pass `--omit=dev` to ignore them: `npm audit --omit=dev --audit-level=high`.
- **`pip-audit` fails with "no candidates found"**. The package is yanked from PyPI; pin to the previous release or replace with an actively maintained fork.
- **Audit is clean but CI still fails**. The `--audit-level` flag is set lower than expected; check the workflow file for `--audit-level=moderate`.
- **`npm audit fix` downgrades packages**. The lockfile has conflicting peer dependency constraints. Resolve with `npm install <pkg>@<version>` explicitly.
- **Poetry `export` fails in CI**. Add `poetry export` to the CI environment with `pip install poetry` before running pip-audit.

## Related

- [[coding/general-principles]]
- [[coding/python-security]]
- [[tooling/github-actions]]
- [[tooling/github]]
- [[cheatsheets/bash-one-liners]]
- [[coding/testing]]
- [[ops/github-pages]]
