A CI dependency scan can miss a new advisory when it reuses cached vulnerability data, a cached result, or a database baked into an old runner image. The fix is to show data freshness in every report, set an expiry on caches, and re-run the scan when the data refreshes.
How data goes stale in CI
Scanners need two inputs: your dependency list and a vulnerability database. The list is fresh on every run. The database may not be.
- Database baked into a runner image. The image was built weeks ago, and every job uses that snapshot.
- Cache keyed on the lock file. The key says "same dependencies, so same result", and ignores the fact that the database changed.
- Result reuse. A pipeline skips the scan if nothing changed since the last green run.
- Offline or mirrored data. A mirror updated on a schedule that no one remembers.
Caching is good for speed. npm's docs even suggest keeping the npm cache in CI to speed up installs. The risk is applying the same idea to security data, where the result can legitimately change with no code change.
A table of cache types
| Cache | Safe to cache long? | Rule |
|---|---|---|
| Downloaded packages | Mostly yes | Verify integrity |
| Resolved dependency tree | Yes while the lock file is unchanged | Key on lock file hash |
| Vulnerability database | Only briefly | Expire, show timestamp |
| Scan result | No | Key on lock file hash plus data version |
How to check yours
- Find where your scanner gets its data: bundled, downloaded at run time, or mirrored.
- Look for a database timestamp or version in the scan output. If there is none, that is a gap to raise with the tool owner.
- Pick a recent advisory in a package you use and check whether your pipeline reports it.
- Look at your cache keys. If the scan result is cached, check whether the key includes a data version.
- Check the age of the runner image that holds any bundled data.
A safe policy
- Refresh the vulnerability database on every pipeline run or on a short fixed expiry, and fail the scan if the data is older than your limit.
- Print the data timestamp in the job summary.
- Include the data version in any result cache key.
- Rebuild runner images on a schedule if they carry data.
- Keep a scheduled full scan that does not depend on a pull request existing.
What not to do
Do not skip the scan for pull requests that do not change dependencies if that means a stale result is displayed as current. It is fine to skip work, but not to show an old pass as today's pass.
Where Safeguard fits
Safeguard provides dependency scanning, SBOM generation and continuous matching for new findings, so advisories published after a build are not missed. Check your own repository with the free scan at gold.safeguard.sh/scan.
Quick check
How can I tell whether my scan data is fresh?
Look for a timestamp or version of the vulnerability database in the output. If it is missing, test with a recent advisory you know about.
Is it ever fine to skip the scan?
Skip duplicate work, but do not present an old result as current. Tie any reuse to the data version.
Sources
- npm Docs, npm ci
Last reviewed: 19 October 2026.