BlogCI/CD

CI dependency scanning: pip-audit, npm audit, fail fast

Lockfile scanners like pip-audit and npm audit are free and fast — the hard part is a CI failure policy strict enough to matter that your team keeps on.

Jun 12, 2025·4 min readBeginner·By the SecOpsLog team · command-tested

The fastest way to kill a security gate is to make it slow and noisy. A dependency scanner that fails the build on a decade-old, unfixable CVE buried in a transitive package teaches developers exactly one skill: how to add allow_failure: true. A scan survives only if it is fast, deduplicated, and fails only on things a human can actually act on. Lockfile scanners like pip-audit, npm audit, and Trivy fs are free and fast — the hard part is a failure policy strict enough to matter that your team keeps it on.

This note wires a filesystem scan into GitLab CI, gates on fixable HIGH and CRITICAL findings, and records suppressions with reasons and expiry. For the full pipeline hardening track, see Secure CI/CD with GitLab.

The dependency gate developers keep

If the gate has no clear fix path, developers route around it. --ignore-unfixed is the difference.

1Commit lockfilespackage-lock, poetry.lock2Resolve treescanner reads lockfiles3Match CVE feedsOS + language DBs4Filter severityHIGH, CRITICAL only5Filter fixable--ignore-unfixed6Fail or passexit code 1 on hits7Emit SBOManswer later under pressure
bash — trivy fslive
trivy fs --severity HIGH,CRITICAL --ignore-unfixed .
Detecting dependencies from package-lock.json ...
CRITICAL lodash 4.17.11 -> fixed in 4.17.21
HIGH axios 0.21.0 -> fixed in 0.21.2
2 actionable, all with a fix — the build fails, correctly

Fail only on what is actionable

Two flags do most of the work. --severity HIGH,CRITICAL drops the low-severity chatter, and --ignore-unfixed means the build only breaks when there is a version to bump to. A developer who hits this gate always has a clear next step — bump the dependency, rebuild, merge. That is what keeps them from routing around it with allow_failure: true.

.gitlab-ci.yml
dep-scan:
stage: test
image: aquasec/trivy:0.53.0
script:
- trivy fs --scanners vuln
--severity HIGH,CRITICAL
--ignore-unfixed
--exit-code 1 .
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

For Python-only repos, pip-audit -r requirements.txt or scanning against poetry.lock is equally valid and sometimes faster. The policy matters more than the tool — same severity filter, same fixable-only gate, same MR visibility.

Suppress with a reason, not a lower bar

There will be a genuine false positive, or a CVE you have assessed as not-exploitable in your context. Do not globally lower the severity — that blinds you to the next real one. Record a scoped, expiring exception with a reason, so the suppression is a reviewed decision instead of a silent hole.

.trivyignore
# CVE-2023-45853 — zlib, only reachable via a build-time tool.
# Reviewed 2025-06-10 by security. Re-check when base image bumps.
CVE-2023-45853
# Expired entries fail review — nothing hides here forever.
An unfixable CVE is a decision, not a pass
Ignoring a finding is a risk acceptance. Write down who accepted it and why, give it an expiry, and surface the list in review — the moment suppressions become invisible, the scanner is lying to you. Review .trivyignore in every security audit the same way you review firewall rules.

Generate an SBOM once, answer later

Have the pipeline emit a software bill of materials for every build — trivy fs --format cyclonedx or syft . works on the same checkout. When the next Log4Shell drops, you answer 'are we affected, and where' by querying stored SBOMs in minutes — instead of re-scanning every repo you own under pressure.

Where this goes next

CI scanning catches what you are about to ship; it does not catch what is already running. Pair it with registry scanning on push and runtime scanning in the cluster, so a CVE disclosed after a build still gets flagged against the images you have in production. The Secure CI/CD with GitLab course covers scanning, signing, SBOMs, and policy gates end to end.

Go deeper in a courseSecure CI/CD with GitLabBuild GitLab pipelines, then harden them with scanning and gates.View course

Related posts