For: anyone running several web apps (mixed frameworks) with a small team, who wants to fix dependency vulnerabilities (CVEs) for real, once. No attack steps — only the practice of fixing, making fixes stick, and keeping watch. For the kind of incident that often kicks this off, see a neglected public RCE billed for fraud.
This site's view: small teams stay safe with two disciplines
What works on a small team isn't flashy tooling — it's two disciplines: 1) automated change-detection (alert only on new vulnerabilities) and 2) local→push→deploy (production receives, never edits). This site runs the same way: a dependency audit (pnpm audit) before every deploy plus a daily cron, with push-to-deploy delivering to production. Setting up alerts that keep running, instead of treating it as a one-time fix, is the cheapest approach that lasts.
Decide the four conditions for "done" first
This keeps you from calling things "done" too early. Decide up front: until all four line up, it's incomplete.
1. Scan
put the state into numbers
2. Fix
fix the non-dev crit/high at the source
3. Isolate/hand off
make the un-fixable explicit
4. Monitor
put daily change-detection in
1. Scan: count what you have now
First, see what's where, by machine. Manual ad-hoc checks always lapse.
Auto-discover lockfiles and scan
composer.lock / package-lock.json / pnpm-lock.yaml and run them daily through an open-source scanner (osv-scanner or pnpm audit). It only reads lockfiles, so the load is tiny.Know that severity isn't one number
Exclude noise WITH a recorded reason, not by ignoring
2. Fix: update the vulnerable version itself
A temporary measure that hides the symptom and a fix of the actual cause are different jobs. Do both to finish.
Symptom-hiding only (the cause remains)
- block only "suspicious-looking" requests at a reverse proxy
- the symptom stopped, so call it "handled"
- the vulnerable dependency stays — the RCE is still exploitable
Fixing the cause (correct)
- upgrade the vulnerable dependency to the patched version
- after upgrading, confirm the signature is gone in your logs
- treat the temporary measure and the actual fix as two separate jobs, do both
Build-verify major bumps BEFORE production
Unlike a patch, a major-version jump (framework 14→15, a UI kit 4→6, etc.) carries breaking changes. Upgrading without checking and breaking the production build takes the service down. Put the edited source in a prod-identical runtime (same Node/PHP version container) and get the build/type-check fully green before you push. Clear errors one by one (sync→async codemods, multi-line CSS class strings, a retired global type namespace, etc.). If the old container keeps running, a failure rolls back with zero downtime.
Check that the next deploy won't undo the fix
A perfect fix is worth zero if the next deploy erases it. This is the most common mistake.
Don't commit on the production working tree
Standardize on one direction: local→push→deploy
Don't trust HTTP 200 as 'healthy'
curl | grep), are there new errors in the logs, and walk the dynamic routes (DB-backed, parameterized), not just the homepage. Caches can delay the change, so wait or clear first.3. Isolate/hand off: write down what you can't fix
You can't always fix everything now. The trick is to never leave "can't fix / not my area / unused" ambiguous.
Orphaned/EOL code: isolate BEFORE deleting
Confirm it's truly unreferenced first
Explicitly hand off what you can't fix
4. Monitor: set up daily change detection
Now, finally, put the alerting mechanism in. Without it, a fix you made won't tell you when it recurs.
Alert on what's NEW, not everything every day
Diff scan results against the previous run (a state file) and notify only when a new critical/high appears. The same content every day gets ignored (alert fatigue). Summarize into one email (new / resolved / current) on a daily cron. Even shared hosting handles one scanner binary + cron fine (the load is milliseconds; add nice for peace of mind).
API keys and SSH keys an inventory also finds
Fixing CVEs for real tends to surface non-dependency problems too. Two common ones: a secret file left in a public directory (an old token sitting in the webroot; if it came from a shared template, every site has the same problem) and a root key handed to an environment that can be compromised. Both create the "one leak, everything" pattern, so check them in the same sweep (each deserves its own deep dive). For secrets basics, see storing secrets safely and the baseline checklist.
Read next
- Example: Jenkins security advisory (October 28, 2026, 15 issues)
- Dependency monitoring: installing and using osv-scanner · not falling behind on CVEs
- Defend by pattern: file upload vulnerabilities — designing out web shells / RCE
- Incident: a neglected public RCE billed for fraud
- Operations: production receives only / self-hosted Git vs GitHub
- Baseline: the security baseline checklist
- Incident: the Capcom ransomware attack (2020) (a neglected old VPN device was the way in)
- Tool: CVE / KEV lookup (CVSS, EPSS and active-exploitation status on one screen)
- Prioritisation: deciding what to patch first (CVSS, EPSS, KEV and deadlines)