Back to glossary

Open Source Security

Open source security is managing the risk in third-party open source code: vulnerabilities, malicious packages, license obligations, and maintainer health.

Also known as OSS security, open source software security, open-source security

Open source security, also called OSS security, is the practice of managing the risk in open source code your software runs but your team did not write. Every install trusts a published version rather than a reviewed project, so risk arrives four ways: vulnerabilities, malicious packages, license obligations, and the health of the accounts that publish.

The mechanism is ordinary package installation. A manifest such as package.json declares version ranges, not code. On every fresh install, the package manager resolves those ranges against whatever the registry holds at that moment, downloads an artifact that never had to match the source repository, and, on npm 11 and earlier, runs the package's install scripts. Open source security is the part of software supply chain security that governs that path.

The most important fact is that the four risks run on different clocks. A vulnerability can sit in a release for years before a CVE (Common Vulnerabilities and Exposures) entry names it. The malicious releases in the axios compromise were live for about three hours on March 31, 2026, according to SANS's timeline. Most tooling is built for the slow clock: it matches installed versions against advisory databases. Our position is that advisory matching is an inventory, not a defense. Malware does not get a CVE, an advisory range can silently miss the version you run, and the update that clears a vulnerability backlog is the same command that installs a hijacked release. The control that matters most is inspecting what a package contains before it runs.

What is open source security?

#

Open source security is the discipline of deciding which third-party open source versions may enter your software, and keeping that decision correct as those versions age.

Start with what open source software is. Open source software is code distributed under a license that meets the Open Source Initiative's Open Source Definition, a list of 10 criteria. The second criterion says the program "must include source code." Licenses grant rights. They do not grant assurances. The MIT license, which covers 151 of the 170 packages in the sample application we analyze below, ends with this clause:

Plain text
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

That clause is the first principle of open source security. You get the right to run, read, and change the code. The authors keep no obligation to you. Any assurance you need, you build yourself.

The risk divides into four classes. Each has a different cause, a different tracking system, and a different clock.

Risk classWhat goes wrongWho causes itHow it is trackedClock
VulnerabilitiesA bug in honest code is exploitableMaintainer mistakesCVE, GitHub Security Advisories (GHSA), Open Source Vulnerabilities (OSV)Months to years from introduction to fix
Malicious packagesA published version runs attacker codeTyposquatters, hijacked maintainer accountsMalware feeds; advisories, if any, arrive after the damageMinutes to hours
LicenseAn obligation you cannot meet, or a relicenseAuthors and copyright holdersSPDX identifiers in package metadata, often incompleteChanges with releases
Maintainer and project healthAbandonment, a single point of failure, account takeoverCircumstance, and attackers who target peopleWeak signals: release history, deprecation flags, scorecardsYears of drift, then one bad publish

A malicious package is the class most programs underweight, because nothing in a vulnerability workflow is designed to see it. License compliance is the class most programs over-automate, because license metadata looks authoritative and is not. In our sample tree, two packages, busboy and streamsearch, have no license field in package-lock.json because they use the deprecated licenses array. Both are MIT.

The classes also feed each other. A maintainer account takeover is a project-health failure that becomes a malware incident in one publish. An abandoned package is a vulnerability problem waiting for its first unfixed CVE. We treat them as one pipeline with four kinds of failure, not four tools with four dashboards.

What an install actually trusts

#

Take a real manifest. We wrote this one the way a small Node.js API might have looked in October 2023. Then we resolved it twice with npm 12.2.1: once with --before=2023-10-26, which installs only versions published before that date, and once against the registry on October 26, 2026.

JSON
{
  "name": "api-2023",
  "private": true,
  "dependencies": {
    "axios": "^1.5.1",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.2",
    "mongoose": "^7.6.3",
    "multer": "^1.4.5-lts.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}

The resolved versions depend on the date, not on the file. Resolved as of October 26, 2023, the manifest installs axios@1.5.1 and 160 packages. Resolved on October 26, 2026, the same file installs axios@1.20.0 and 169 packages. Nothing in the repository changed between those runs. Only the registry did.

A lockfile freezes one resolution. Here is the entry npm wrote for axios:

JSON
"node_modules/axios": {
  "version": "1.20.0",
  "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz",
  "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==",
  "license": "MIT"
}

The integrity hash proves the bytes match what was resolved. It does not prove the bytes are safe. It also only applies to installs that honor the lockfile. Between the manifest and running code there are three more places where trust is extended.

The version range is a standing order

#

A caret range like ^1.5.1 accepts every 1.x release at or above 1.5.1, including releases that do not exist yet. When axios@1.14.1 was published at 00:21:58 UTC on March 31, 2026, it became the answer to ^1.5.1 for every fresh install, npx run, and dependency update. Our analysis of the hidden blast radius of the axios compromise found ranges like that across continuous integration (CI) tools, SDKs, and Model Context Protocol (MCP) servers, often several layers deep. Lockfiles make existing installs deterministic. They do not make new installs safe.

The artifact is not the repository

#

Registries serve tarballs and cached modules, not git history, and the two can differ. The xz-utils backdoor (CVE-2024-3094) lived in release tarballs. The XZ Utils project's own notice is blunt: "XZ Utils 5.6.0 and 5.6.1 release tarballs contain a backdoor." The trigger code was never in the Git repository, and the tarballs were signed by the attacker's maintainer identity. In Go, a typosquatting module called boltdb-go/bolt kept serving a backdoored version from the Go Module Mirror's cache for more than three years, while its GitHub tag pointed to clean code. Anyone who reviewed either repository saw nothing.

Install scripts run before anyone looks

#

npm ran preinstall, install, and postinstall install scripts from every dependency by default until npm v12 shipped on July 8, 2026. The plain-crypto-js@4.2.1 dropper pulled in by the hijacked axios used exactly that hook. Its postinstall ran node setup.js, fetched a platform-specific remote access trojan, then swapped its own package.json for a clean copy. pnpm 10 turned dependency scripts off in January 2025, and npm v12 followed. Default-on install scripts were a design mistake, and both package managers have now corrected it. The fix removes install time as a free execution point. It does nothing for malicious code that runs when your application imports the package.

The figure above shows the five stages of one npm install, what each stage trusts, and an incident that exploited it:

StageWhat it trustsIncident
1. Manifest declares a range^1.5.1 accepts every future 1.x release at or above 1.5.1Accepts versions that do not exist yet
2. Resolve against the live registryWhatever version is newest at install timeaxios@1.14.1, published 2026-03-31 00:21:58 UTC from a hijacked account
3. Fetch the published artifactTarball or cached module bytes, not the git history reviewers readxz 5.6.0 and 5.6.1 tarballs; boltdb-go/bolt cached for more than three years
4. Run install scriptsArbitrary code with the installing user's privileges; off by default in pnpm 10+ and npm 12+plain-crypto-js@4.2.1 postinstall dropper
5. Import and run in productionThe code's behavior under attacker-controlled input11 multer advisories that npm audit does not match on 1.4.5-lts.2

Malicious code enters at stages 2 to 4. Vulnerabilities do their damage at stage 5. Advisory scanners are built for stage 5.

Is open source software safe?

#

Open source software is as safe as the specific versions you install and the process that admitted them. Neither the license nor the visibility of the source makes a promise either way.

Open source code lets anyone audit it, but obliges no one to. Review concentrates on popular repositories, while the bytes you install come from a registry artifact. That artifact is the layer xz and boltdb-go poisoned.

Most of that code is maintained by very few people. Tidelift's 2024 maintainer survey of 437 maintainers found 60% are unpaid and 60% have quit or considered quitting. In the same coverage, Josh Bressers is quoted counting, in 2022, 16 million of 28 million npm releases with a single maintainer.

Our sample API is typical. Its 157 unique packages are published by 114 distinct npm accounts, and 68 of those packages list a single maintainer, per registry metadata on October 26, 2026. Any one of those 114 accounts can publish a version that the next npm update will install.

The axios maintainer's account shows why that matters. The lead maintainer was socially engineered through a cloned company identity and a staged meeting, and the attacker lifted his browser sessions. He said the attacker's access would likely have been "complete irrespective of what was setup." Two-factor authentication and trusted publishing authenticate the account, not the intent. Provenance and signatures are necessary but not sufficient, because a compromised maintainer publishes through a legitimate pipeline.

Proprietary code is not safer by default. It has the same bug classes and fewer people able to find them. The real difference is agency. With open source you can inspect, pin, patch, or fork every component, and nobody else will do it for you. Our verdict: open source software is safe to use when you treat each new version as untrusted input, and unsafe when you treat a project's reputation as a property of its next release.

Why does CVE scanning miss most open source attacks?

#

CVE scanning misses most open source attacks because it can only match installed versions against advisories that already exist. Three gaps follow from that.

Malware does not get a CVE. GitHub published an advisory for the hijacked release, GHSA-fw8c-xr5c-95f9, on March 31, 2026, the same day. It lists no CVE. Its guidance reads: "Any computer that has this package installed or running should be considered fully compromised." Any machine that installed the release before the advisory existed was already compromised. The advisory describes the damage. It cannot prevent it. Socket's scanner flagged plain-crypto-js@4.2.1 six minutes after it was published, 16 minutes before the axios version that pulled it in existed.

Advisory ranges can silently miss the version you run. Multer is the Express project's multipart upload middleware. In 2022 its maintainers fixed CVE-2022-24434 on a prerelease-tagged long-term support (LTS) line and deprecated the older 1.x releases with instructions to move to 1.4.4-lts.1. The LTS line, 1.4.4-lts.1 through 1.4.5-lts.2, now carries its own deprecation: "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x." Yet npm audit reports nothing for the LTS versions:

Bash
$ npm install multer@1.4.4 --save-exact --silent && npm audit | grep vulnerabilities
3 high severity vulnerabilities
$ npm install multer@1.4.5-lts.2 --save-exact --silent && npm audit | grep vulnerabilities
found 0 vulnerabilities

npm counts vulnerable packages, not advisories. The 1.4.4 report covers dicer, busboy, and multer, with 11 advisories against multer alone.

The cause is a semver rule, not a missing advisory. CVE-2025-47944, published May 19, 2025, declares its affected range as >=1.4.4-lts.1 <2.0.0, which plainly intends to cover the LTS line. But node-semver, which npm uses to match ranges, lets a prerelease version satisfy a range only if a comparator with the same major, minor, and patch numbers also carries a prerelease tag. 1.4.5-lts.2 fails that test against all 11 multer advisories in npm's database, 10 of them high severity. Under prerelease-inclusive matching, all 11 cover it. We verified both results against npm's bulk advisory endpoint and node-semver 7.8.5 on October 26, 2026:

JavaScript
const semver = require('semver') // 7.8.5, the version bundled with npm 12.2.1
semver.satisfies('1.4.5-lts.2', '>=1.4.4-lts.1 <2.0.0')                              // false
semver.satisfies('1.4.5-lts.2', '>=1.4.4-lts.1 <2.0.0', { includePrerelease: true }) // true

The CVE pipeline itself is under strain. CVE volume reached 48,185 in 2025, up 20.6% from 2024. In April 2026, NIST stopped routinely enriching most CVEs in the National Vulnerability Database. As of August 17, 2026, 42,353 CVEs sat in its "Not Scheduled" status. The NVD cannot be the sole source of truth for anything, least of all for open source packages that change daily.

How do you reduce open source security debt?

#

Open source security debt is the set of known-vulnerable dependency versions you still ship because your lockfile stopped moving. It grows without a single commit, because new advisories keep landing on old versions.

Our sample manifest makes the debt measurable. We installed it from the October 2023 lockfile and ran npm audit against the advisory database on October 26, 2026. The untouched lockfile reports 19 vulnerable packages and 73 distinct advisories: 3 critical, 11 high, 2 moderate, and 3 low. Three of the vulnerable packages are direct dependencies: axios, express, and mongoose.

Paying most of it down took one command:

Bash
$ npm update
added 20 packages, removed 11 packages, and changed 54 packages in 2s

$ npm audit
found 0 vulnerabilities

Every fix was inside the ranges the manifest already allowed. Most open source security debt is not hard engineering. It is deferred routine work. The exception is the one npm audit cannot see: multer stays on the deprecated 1.x line, and leaving it requires a breaking upgrade to 2.x that no in-range update will make.

The timeline above tracks the same package.json from October 26, 2023 to October 26, 2026. On October 26, 2023, the lockfile resolves axios@1.5.1, express@4.18.2, mongoose@7.6.3, multer@1.4.5-lts.1, and 160 packages. On May 19, 2025, CVE-2025-47944 is published for multer >=1.4.4-lts.1 <2.0.0, and npm audit never matches the 1.4.5-lts versions. Between 00:21 and about 03:15 UTC on March 31, 2026, the malicious axios@1.14.1 satisfies ^1.5.1, so any npm update in that window installs plain-crypto-js@4.2.1 and, on npm 11, runs its postinstall. On July 8, 2026, npm v12 ships with dependency install scripts off by default. On October 26, 2026, the untouched lockfile shows 19 vulnerable packages and 73 advisories (3 critical, 11 high, 2 moderate, 3 low), and after a 2.5-second npm update it shows 0, with multer@1.4.5-lts.2 still deprecated and still unmatched.

That is the central tension of open source security. The command that pays down vulnerability debt is the delivery channel for the fastest attacks. Run that same npm update during the axios window and npm 11 would have installed the hijacked release and executed its dropper. Reducing debt safely means updating often and gating every new resolution:

  • Update in small, frequent steps. Weekly automated update pull requests keep each lockfile diff small enough to review. Our three-year catch-up added 543 lines to package-lock.json and removed 353 in a single diff. The cost is review load, and every bot-opened pull request is a fresh resolution against the live registry.
  • Delay routine updates with a cooldown. A dependency cooldown refuses versions younger than a set age. pnpm 11 defaults minimumReleaseAge to 1,440 minutes, which would have skipped axios@1.14.1 entirely. npm 12.2.1 offers min-release-age, measured in days and off by default. The cost is that a cooldown delays security patches too, and npm's first implementation had no exclusion mechanism for urgent fixes.
  • Keep install scripts off. In our sample tree, exactly one of 170 lockfile entries declares an install script: fsevents, a macOS-only optional dependency. For most applications, npm 12's default costs nothing. It does not stop payloads that run at import time.
  • Inspect packages at the moment they resolve. Socket Firewall is a proxy between the package manager and the registry. Wrapping a command, as in sfw npm update, checks each requested package version against malware intelligence and blocks it before download, at any dependency depth, per Socket's documentation. It cannot block malware nobody has detected yet, which is why it pairs with a cooldown rather than replacing one.
  • Triage the remaining CVEs by reachability. Reachability analysis asks whether your code can call the vulnerable function at all. Socket reports that its dependency-graph-only analysis marks about 60% of CVEs unreachable, and full application analysis typically about 80%, in its Tier 1 Reachability announcement. The analysis over-approximates by design and says nothing about malware.
  • Retire what is dead. Deprecation flags such as multer's are the cheapest health signal in the registry, and most teams ignore them. Sometimes the project revives instead: Lodash's security reset shipped its first security release in years only after a funded steering committee rebuilt its CI and vulnerability process.

Which controls cover which open source risks

#

No single control covers more than one risk class well. This is the comparison we use, with what each control misses.

ControlVulnerabilitiesMalicious packagesLicenseMaintainer healthWhat it misses
Advisory scanning (npm audit, software composition analysis tools)Yes, when ranges matchOnly after an advisory existsSome toolsNoUnmatched ranges, malware without advisories
Lockfile plus npm ciFreezes versions, so debt growsProtects existing installsNoNoNew resolution: updates, npx, global installs
Release cooldownDelays fixesSkips short-lived malicious versionsNoNoMalware that outlasts the delay
Install scripts offNoStops install-time payloadsNoNoImport-time and runtime payloads
Provenance and trusted publishingNoProves which pipeline built a versionNoPartlyA hijacked maintainer publishing through a legitimate pipeline
Reachability analysisPrioritizesNoNoNoMalware; over-approximation leaves some noise
Install-time package analysis (firewall)Partly, by policyBlocks known and detected malware before downloadBy policyThrough signalsMalware not yet detected
License scanningNoNoYesNoMissing or legacy license metadata
Health scores (OpenSSF Scorecard)NoNoNoYesPoint-in-time; cannot see a session hijack

Our verdict: advisory scanning is your inventory and lockfiles are your reproducibility, but neither is a defense against the attacks that execute. The minimum stack for open source security is deterministic installs from a committed lockfile, install scripts off, a cooldown on routine updates, install-time analysis of every package that resolves fresh, and reachability to make the remaining CVE queue finite. Each piece fails somewhere, which is why the stack has five pieces.

The update path is the new bottleneck

#

Vulnerability discovery is accelerating faster than maintainers can absorb it. In six months, Anthropic's AI-driven scanning found more than 29,000 candidate vulnerabilities in open source projects, of which its researchers manually reviewed about 6,000, according to our coverage of Anthropic's OSS Scanner launch. curl maintainer Daniel Stenberg pointed to the next delay in that chain: users still have to install the fixed versions.

More findings mean more upgrades, and every upgrade is a fresh resolution against a registry that attackers can also publish to. The open problem in open source security is no longer finding the bugs. It is making the move to the fixed version as safe as the fix itself.