Patch what you can't upgrade
Remove a CVE from a dependency without changing its version, using Socket Certified Patches.
Some CVEs can't be fixed by upgrading. The maintainer hasn't released a fix, the fix is a breaking major version, or the package is abandoned. A Socket Certified Patch changes only the vulnerable code inside the installed package. The version in your manifest and lockfile stays the same, so nothing else in the tree moves. Patches are plain text, live in your repository under .socket/, and are reapplied after every install.
| Without a Socket token | With your organization's token |
|---|---|
| Free patches only, from Socket's public patch service | Free patches plus the paid patches your plan includes |
Before you start:
npm install -g socket. Thesocket patchcommands ship with it.- Dependencies installed. Patches apply to installed packages:
node_modulesfor npm, and for Python the.venvin the project folder or the virtual environment that's active. - For paid patches,
export SOCKET_CLI_API_TOKEN=...and add--org your-orgto each command. - Network access to
patches-api.socket.dev(no token) orapi.socket.dev(with a token).
1. Find CVEs that have a patch
Across every repository, list the reachable CVEs that have a patch available:
curl -s -H "Authorization: Bearer $SOCKET_CLI_API_TOKEN" \
"https://api.socket.dev/v0/orgs/your-org/historical/alerts?range=-30d&per_page=100&filters.cvePatchStatus=patch_available&filters.alertReachabilityType=reachable" \
| jq -r '[.items[]? | "\(.repoSlug) \(.artifact.name)@\(.artifact.version) \(.alert.props.cveId)"] | unique[]'Drop filters.alertReachabilityType=reachable to see every patchable CVE, not just reachable ones. The query returns up to 100 rows. A larger organization gets more on later pages: the response carries an endCursor for the next one, and without it the list is cut off silently.
For one project, list the patches available for what's installed:
cd /path/to/your-app
npm ci
socket patch scan --json > patches.json
jq '{scannedPackages, packagesWithPatches, freePatches, paidPatches}' patches.jsonThe packages list in patches.json names each package with a patch and the patch's UUID. Nothing in the project changes.
Always use--jsonwithscanWithout
--json,socket patch scanoffers to download and apply every patch it finds. When it isn't run in a terminal (CI, a script, piped output) it doesn't ask: it applies them all and writes.socket/manifest.json.
2. Apply a patch
Get the patch by package name, or by the UUID from patches.json. get downloads it, saves it under .socket/, and applies it to the installed version:
socket patch get minimist # by package name
socket patch get your-patch-uuid # or by patch UUID
socket patch list # shows the package, patch UUID, tier and the CVEs it fixes
npm testBoth forms save exactly one patch, for the version you have installed.
Getting a patch by CVE ID saves every version
socket patch get CVE-...also works, but it saves a patch for every affected version of the package, not just yours. In one test, a single CVE added 27 entries to.socket/manifest.jsonand applied only the installed one.socket patch listthen shows all 27, andsocket patch removetakes them out one version at a time. Use the package name or UUID unless you want that.
Review the change before you commit it. .socket/manifest.json lists each patched file with a beforeHash and an afterHash, and file contents are stored by hash in .socket/blobs/. The blob named by a file's afterHash is the patched version. To see exactly what the patch changes, diff it against the same file in an unpatched install (npm ci --ignore-scripts leaves packages unpatched).
3. Reapply it after every install
A fresh npm ci puts the original package back, so the patch has to be reapplied each time dependencies are installed. For npm, add it as a postinstall step:
socket patch setup --yesThis adds "postinstall": "socket patch apply --silent --ecosystems npm" to package.json. Commit package.json and the whole .socket/ folder. With the patched files committed, socket patch apply has nothing to download; add --offline to make it fail rather than fetch if a file is ever missing.
The postinstall step needs the Socket CLIEvery machine that runs
npm installornpm cifor this project, including CI runners and Docker builds, now needssocketon itsPATH, or the install fails. Installs run with--ignore-scriptsskip postinstall, so the patch isn't applied there; runsocket patch applyexplicitly after those.
For Python there's no postinstall hook. Run the apply step after installing, from the project folder that holds .venv, or with the virtual environment you install into activated:
uv sync # or: pip install -r requirements.txt
socket patch apply --ecosystems pypi4. Check it in CI
socket patch apply --json reports "status": "success" even when it patched nothing, for example when node_modules is missing or the installed version no longer matches the patch. So the gate checks the counts as well as the status:
out=$(socket patch apply --json)
echo "$out" | jq -e '.status == "success" and (.patchesApplied + .alreadyPatched) > 0' >/dev/null \
|| { echo "Socket patch apply: $(echo "$out" | jq -c '{status, patchesApplied, alreadyPatched, failed}')"; exit 1; }alreadyPatched counts patches the postinstall step already applied. Other status values are partial_failure, no_manifest and error.
Undo a patch
socket patch rollback "pkg:npm/[email protected]" # restore the original files, keep the patch saved
socket patch remove "pkg:npm/[email protected]" # roll back and delete it from .socket/Omit the identifier on rollback to roll back every patch.
Verify
-
socket patch listshows the patch and the CVE it fixes - Your tests pass with the patch applied
- After
rm -rf node_modules && npm ci,socket patch apply --jsonshowsalreadyPatchedof 1 or more, because postinstall reapplied the patch -
socket patch rollbackrestores the original files, andsocket patch applypatches them again
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
scan reports 0 patches everywhere | The patch service can't be reached. scan doesn't report connection errors | Check access to patches-api.socket.dev or api.socket.dev. socket patch get with a package name does show the error |
| Python packages aren't found, or the wrong ones are | No .venv in the folder and none active, so scan reads the system Python's packages | Run from the folder that holds .venv, or activate the venv you install into |
| Only free patches are offered | No token, or no org | Set SOCKET_CLI_API_TOKEN and pass --org your-org |
The patch silently stops applying (patchesApplied 0, status still success) | The installed version isn't the one the patch was made for, usually after an upgrade | Each patch is for one exact version. socket patch remove it and check whether the new version still needs one. The step 4 gate catches this |
socket patch list shows versions you don't have | The patch was fetched by CVE ID | Remove the extra versions with socket patch remove, or fetch by package name next time |
npm install or npm ci fails with npm error code 127 and npm error command sh -c socket patch apply --silent --ecosystems npm | The postinstall step ran where the Socket CLI isn't installed | Install socket on that machine or image |
Reference
- Socket Certified Patches
- Fix what matters for CVEs that do have an upgrade
Updated 8 days ago