Automating Code Quality and Dependency Audits in CI/CD
Manual code review catches logic bugs and style issues. It almost never catches a dependency that quietly became vulnerable last week - that requires checking against a database that changes daily, which is exactly the kind of check that belongs in automation, not a human's memory.
Why Dependency Health Is a Security & Velocity Issue
Modern apps are mostly other people's code - a typical project pulls in hundreds of transitive dependencies most contributors have never looked at. Three things go wrong quietly over time:
- A dependency gets a CVE disclosed after you already shipped it, and nothing tells you unless you're watching for it.
- A package gets abandoned - no more patches, ever - and you don't find out until you need one.
- Version drift accumulates until an upgrade that should've been a patch release becomes a multi-day migration.
None of this shows up in a normal PR review. It only shows up if something is actively checking for it, continuously.
What to Check Automatically
- Vulnerable dependencies - known CVEs in direct and transitive dependencies, not just the top-level package.json.
- Outdated packages - how far behind latest, and whether the gap includes a security fix.
- License issues - a copyleft license slipping into a proprietary codebase through a transitive dependency is a real, recurring problem.
- Code quality signals - lint violations, obvious anti-patterns, dead code - the things a reviewer would flag if they had time to look everywhere.
- Repo hygiene - committed secrets, overly permissive CI tokens, missing branch protection.
Where This Fits in Your Pipeline
Two complementary cadences, not one or the other:
- Pre-merge, on every PR - catches new problems before they reach
main, when they're cheapest to fix and the author still has context loaded. - Scheduled, e.g. nightly - catches problems in code that already merged, when a new CVE gets disclosed for a dependency you added months ago and never touched again.
Pre-merge alone misses the second case entirely - most vulnerable dependencies weren't vulnerable when they were added.
A Minimal GitHub Actions Example
A scan step on every push and pull request, so results show up before code merges:
1name: Repo Audit
2on:
3 pull_request:
4 push:
5 branches: [main]
6
7jobs:
8 audit:
9 runs-on: ubuntu-latest
10 steps:
11 - uses: actions/checkout@v4
12 - name: Run dependency & quality scan
13 run: |
14 curl -X POST https://scanverra.com/api/repo/scan \
15 -H "Authorization: Bearer ${{ secrets.SCANVERRA_API_KEY }}" \
16 -d '{"owner":"your-org","repo":"your-repo","ref":"${{ github.sha }}"}'Turning Findings Into Action
The failure mode that kills these pipelines isn't missing checks - it's checks nobody acts on. A few things that keep it useful instead of ignored:
- Fail the build only on high-severity, actionable findings - a pipeline that blocks on every low-severity notice trains people to ignore it entirely.
- Warn (don't block) on everything else, surfaced somewhere visible, so it's not invisible either.
- Triage false positives once and suppress them explicitly, rather than re-litigating the same finding every run.
- Where possible, let AI-suggested fixes turn a finding directly into a draft PR - the gap between "flagged" and "fixed" is where most findings quietly die.
A Simple CI Checklist
- Dependency/CVE scan runs on every PR, not just manually before releases.
- A scheduled scan also runs against
main, independent of new PRs. - Severity threshold for failing the build is deliberately set, not left at whatever the tool defaults to.
- Someone is actually assigned to triage new findings - a check nobody owns is a check nobody fixes.
Automate this in your next PR
Run a free repo scan and see outdated dependencies, CVEs, and code quality issues before they ship.
Run a free repo scan