Automate the updates
Dependabot on GitHub, Renovate anywhere. Both watch your manifest, notice new versions, and open pull requests.
Renovate is the more configurable of the two, grouped updates, scheduling, automerge rules for patch versions with passing tests. Dependabot needs no setup beyond enabling it.
Either is dramatically better than manual updates, because the cost of updating is mostly the review, and small frequent updates review faster than a year's worth at once.
Commit the lockfile
package-lock.json, poetry.lock, Gemfile.lock, composer.lock. Without one, builds resolve versions at install time and what you tested isn't necessarily what you deployed.
Lockfiles also make dependency scanning meaningful, since the scanner can see exact versions rather than ranges.
The transitive problem
Most of what you ship isn't what you installed. A handful of direct dependencies pulls in hundreds of others, and vulnerabilities are usually several levels down.
You often can't update the vulnerable package directly. Options: update the direct dependency that depends on it, use an override (overrides in npm, resolutions in yarn) to force a version, or accept the risk and document why.
Overrides are a stopgap. They can break things silently and they need removing once the real fix lands upstream.
Not every alert matters
This is the part people get wrong in both directions. Either fixing everything and burning weeks, or ignoring everything and missing the one that mattered.
A CVE in a package you depend on is only exploitable if your code reaches the vulnerable function with input an attacker controls. Frequently it doesn't. A vulnerability in a CLI parsing routine of a library you use programmatically isn't reachable from your application.
Triage roughly in this order:
Is it reachable? Does your code call the affected path at all
Is it exposed? Does untrusted input reach it
What's the impact? RCE and auth bypass first
Is there a fix? An update available now beats a severe issue with no patch
Reachability analysis is what separates useful dependency tooling from noise generation. A tool telling you about 400 CVEs without saying which are reachable has given you a list, not information.
Where nothing is reachable and no patch exists, document the decision. That's a defensible position and an auditor will accept it; silence isn't.
Supply chain
Distinct from known CVEs and worth its own attention. Typosquatted package names, compromised maintainer accounts, malicious postinstall scripts.
Pin versions rather than tracking ranges, review new dependencies before adding them, and consider disabling install scripts by default (npm ci --ignore-scripts) where your build doesn't need them.
Cadence
Regularly, and small. Weekly automated pull requests for patches, monthly review for minors, majors as planned work.
The failure mode is a team that ignores updates for a year, then faces a major version jump across the whole tree at the moment a critical CVE lands. The emergency is the accumulated debt, not the CVE.