Safeguard
Security Guides•

Why do fixed vulnerabilities keep coming back in my scans?

A finding you fixed shows up again. This guide explains the seven usual causes, how to tell which one you have, and how to make a fix stay fixed.

Safeguard Security Team
9 min read

Fixed findings come back for a few common reasons: an old branch or build still has the old version, another package pulls the old version back in, the lockfile was regenerated, the fix lived in only one place, or a new flaw was published. Find which one applies, then fix the cause, not the symptom.

TL;DR

  • A returning finding is not always the same finding. Check the exact package, version, and project.
  • Old branches, old images, and extra manifest files are the usual hiding places.
  • A parent package can pull the old version back in after an update.
  • A lockfile that is regenerated or ignored can undo your fix.
  • Rescan on a schedule and make the fix part of the code review, not a side step.

What does it mean when a fixed finding returns?

Definition: A recurring finding is a vulnerability that a scan reports again after someone marked it fixed. It is a sign that the fix did not reach everywhere the package lives, or that the package arrived again by another route.

Think of weeding a garden. You pull a weed and the next week it is back. Sometimes the root stayed in the soil. Sometimes a seed blew in from the neighbor's yard. Sometimes it is a different weed that looks similar. The cure depends on which one it is.

Software works the same way. A package version is just a number written in a file. If any file, branch, or image still says the old number, a scan will find it. So before you assume the tool is wrong, look at where the old number lives.

What are the usual causes?

Here are the seven causes teams run into most.

CauseWhat happenedHow to check
Old branchThe fix is on one branch, but the scan reads anotherCheck which branch the scan reads
Old image or buildA container or package built before the fix is still deployedCompare build dates to fix dates
Parent pulls it backA package you updated needs the old version of a childLook at the dependency path
Lockfile undoneThe lockfile was regenerated or not committedCompare lockfile in the repository to the one in the build
Second manifestA project has more than one dependency file, and you fixed oneSearch the whole folder for dependency files
Version range driftA loose range allowed a different version to installCheck the range and the installed version
New advisoryA different flaw was published for the same packageRead the finding id carefully

Let us take them one at a time.

Old branches and old builds

Most scanners read one branch, usually the default one. If your fix went into a feature branch that was never merged, the default branch still has the old version. Likewise, an application already running in production might have been built last month, so it has last month's packages until it is rebuilt and redeployed.

The parent pulls it back

You upgraded a package. But a different package in your tree needs the old version of the same library. Package managers pick versions by matching ranges, so two packages with different needs can leave the old version in the tree. The fix is often to upgrade the parent that wants the old child, as covered in direct versus transitive vulnerabilities.

The lockfile was undone

The npm docs describe the lockfile as recording the exact tree so that later installs produce identical trees, and say it should be committed to the repository. If someone deletes it, or if a build runs without it, versions can change. The same docs say that when the lockfile is bypassed for the audit command, results may differ every run because the tree is rebuilt each time. If your fix was in the lockfile and the lockfile is not what the build uses, the old version can return. See best practices for npm lockfile security.

A second manifest

Large projects often have several dependency files in different folders, for example one for the front end and one for a background service. If you fixed one and not the other, the scan will keep reporting the one you missed.

Version range drift

A manifest does not always name an exact version. It often names a range. The npm semver documentation explains that a caret range, written with a ^ sign, allows changes that do not modify the left most non zero number, which in practice means patch and minor updates for versions 1.0.0 and above. That means a fresh install can pick a newer version than before. That usually helps, but it can also mean two machines get different versions if the lockfile does not pin them. Our upgrade guide explains the numbering.

A new advisory

Sometimes the finding looks the same but is not. Packages can have several flaws over time. The scan may be reporting a second, different flaw in the same package, which needs a newer fix. Always compare the finding identifier, not just the package name. The what is a known vulnerability post explains the identifiers.

How do I track down which cause I have?

Use this short routine.

  1. Write down the exact finding. Package name, version found, finding id, and project.
  2. Check the finding id against your earlier one. If it differs, it is a new flaw, not a return.
  3. Find the branch and build the scan read. Was it the one you fixed?
  4. Look at the version in the lockfile. Is it the fixed one?
  5. Look at the dependency path. Does a parent bring in the old child?
  6. Search the folder for other dependency files. Fix them all.
  7. Check what is deployed. If the fixed code has not been released, the running app is still old.

A worked example with invented numbers

The numbers below are invented for teaching.

On Monday a scan of the "billing" project shows 14 high findings. The team upgrades packages and marks 14 as fixed. On Friday the scan shows 5 of those 14 are back.

The team investigates:

  • 2 of the 5 are on the default branch. The fix was merged to a release branch only. Cause: wrong branch. Fix: merge to the default branch.
  • 1 of the 5 comes from a second manifest in a "tools" folder. Cause: second manifest. Fix: update that file too.
  • 1 of the 5 is the same library pulled in by a parent package that wants the old version. Cause: parent pulls it back. Fix: upgrade the parent.
  • 1 of the 5 has a different finding id. Cause: new advisory. Fix: a newer version.

So 5 returns became 5 different small actions. None of them was "the scanner is broken." That is typical. Almost every recurrence has a specific, findable cause.

How do I make fixes stay fixed?

  1. Fix on the default branch first, then copy to others if needed.
  2. Commit the lockfile and make the build use it.
  3. Add a gate to pull requests. The OWASP guidance says organizations need an ongoing plan for monitoring, triaging, and applying updates. A gate that flags a known bad version before it merges is part of that plan.
  4. Rescan on a schedule, not only when code changes, because new flaws appear after you ship.
  5. Record the fix with the finding id, the version, and the date. If it returns, you can compare.
  6. Rebuild and redeploy so running systems match the fixed code.

Try it yourself

The demo below shows a vulnerability queue where each finding has a status in a sample workspace. Click through and look for how each finding carries a clear status, which is the record you compare against when a finding seems to return.

Click through it at your own pace, or open it full screen.

Quick check

Does a returning finding mean the scanner is wrong?

Rarely. Usually the old version still lives on another branch, in another file, in a build, or comes back through a parent package. Check the exact package, version, and finding id first.

Why does the lockfile matter for this?

The lockfile records the exact versions installed. If it is not committed or not used by the build, the tree can be rebuilt and old versions can return.

How can I tell a recurrence from a new flaw?

Compare the finding identifier. The same package can have several different flaws over time, and each has its own identifier.

How often should I rescan?

On a regular schedule and whenever dependencies change. New flaws are published after you ship, so a past clean result cannot cover a flaw announced later.

How can Safeguard help with this?

Safeguard's dependency scanning rechecks your projects against fresh vulnerability data, so a flaw announced after your last build can still show up. Policies and gates can block a pull request that brings back a known bad version, which keeps a fix from slipping away. If you turn on autonomous remediation, fix pull requests are opened for review. It is off by default, and every category starts on Manual.

Run a free scan at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.