# 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

Source: https://socket.dev/glossary/open-source-security

Last updated: 2026-10-26

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](https://socket.dev/glossary/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](https://socket.dev/glossary/common-vulnerabilities-and-exposures-cve) (Common Vulnerabilities and Exposures) entry names it. The malicious releases in [the axios compromise](https://socket.dev/blog/axios-npm-package-compromised) were live for about three hours on March 31, 2026, according to [SANS's timeline](https://sans.org/blog/axios-npm-supply-chain-compromise-malicious-packages-remote-access-trojan). 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](https://opensource.org/osd), 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:

```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 class | What goes wrong | Who causes it | How it is tracked | Clock |
| --- | --- | --- | --- | --- |
| Vulnerabilities | A bug in honest code is exploitable | Maintainer mistakes | CVE, GitHub Security Advisories (GHSA), Open Source Vulnerabilities (OSV) | Months to years from introduction to fix |
| Malicious packages | A published version runs attacker code | Typosquatters, hijacked maintainer accounts | Malware feeds; advisories, if any, arrive after the damage | Minutes to hours |
| License | An obligation you cannot meet, or a relicense | Authors and copyright holders | SPDX identifiers in package metadata, often incomplete | Changes with releases |
| Maintainer and project health | Abandonment, a single point of failure, account takeover | Circumstance, and attackers who target people | Weak signals: release history, deprecation flags, scorecards | Years of drift, then one bad publish |

A [malicious package](https://socket.dev/glossary/malicious-package) is the class most programs underweight, because nothing in a vulnerability workflow is designed to see it. [License compliance](https://socket.dev/glossary/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](https://socket.dev/glossary/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](https://socket.dev/glossary/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](https://socket.dev/blog/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](https://socket.dev/blog/how-to-use-socket-to-find-out-if-you-were-affected-by-the-backdoored-xz-package) (CVE-2024-3094) lived in release tarballs. The [XZ Utils project's own notice](https://tukaani.org/xz-backdoor/) 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](https://socket.dev/glossary/typosquatting) module called [boltdb-go/bolt](https://socket.dev/blog/malicious-package-exploits-go-module-proxy-caching-for-persistence) 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](https://socket.dev/glossary/postinstall-scripts) from every dependency by default until [npm v12](https://socket.dev/blog/npm-12) shipped on July 8, 2026. The [`plain-crypto-js@4.2.1`](https://socket.dev/blog/axios-npm-package-compromised) 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](https://socket.dev/blog/pnpm-10-0-0-blocks-lifecycle-scripts-by-default) 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.

> Five stages of an npm install, what each stage trusts, and a real incident that exploited each stage.

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

| Stage | What it trusts | Incident |
| --- | --- | --- |
| 1. Manifest declares a range | ^1.5.1 accepts every future 1.x release at or above 1.5.1 | Accepts versions that do not exist yet |
| 2. Resolve against the live registry | Whatever version is newest at install time | axios@1.14.1, published 2026-03-31 00:21:58 UTC from a hijacked account |
| 3. Fetch the published artifact | Tarball or cached module bytes, not the git history reviewers read | xz 5.6.0 and 5.6.1 tarballs; boltdb-go/bolt cached for more than three years |
| 4. Run install scripts | Arbitrary 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 production | The code's behavior under attacker-controlled input | 11 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](https://socket.dev/blog/the-unpaid-backbone-of-open-source) 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](https://socket.dev/blog/axios-maintainer-confirms-social-engineering-behind-npm-compromise) 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](https://github.com/advisories/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](https://github.com/advisories/GHSA-4pg4-qvpc-4q3h), 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](https://socket.dev/blog/cve-volume-surges-past-48k-in-2025), up 20.6% from 2024. In April 2026, [NIST stopped routinely enriching most CVEs](https://socket.dev/blog/nist-nvd-ai-automation) 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.

> Timeline of one Node.js manifest from October 2023 to October 2026: security debt accumulates, a malicious axios version briefly matches its range, npm 12 changes defaults, and an in-range update clears 19 audit findings.

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](https://socket.dev/glossary/dependency-cooldown) refuses versions younger than a set age. pnpm 11 [defaults minimumReleaseAge to 1,440 minutes](https://socket.dev/blog/pnpm-11-adds-new-supply-chain-protection-defaults), 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](https://socket.dev/blog/npm-introduces-minimumreleaseage-and-bulk-oidc-configuration) 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](https://docs.socket.dev/docs/socket-firewall-overview). 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](https://socket.dev/glossary/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](https://socket.dev/blog/introducing-tier-1-reachability). 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](https://socket.dev/blog/inside-lodash-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.

| Control | Vulnerabilities | Malicious packages | License | Maintainer health | What it misses |
| --- | --- | --- | --- | --- | --- |
| Advisory scanning (npm audit, software composition analysis tools) | Yes, when ranges match | Only after an advisory exists | Some tools | No | Unmatched ranges, malware without advisories |
| Lockfile plus npm ci | Freezes versions, so debt grows | Protects existing installs | No | No | New resolution: updates, npx, global installs |
| Release cooldown | Delays fixes | Skips short-lived malicious versions | No | No | Malware that outlasts the delay |
| Install scripts off | No | Stops install-time payloads | No | No | Import-time and runtime payloads |
| Provenance and trusted publishing | No | Proves which pipeline built a version | No | Partly | A hijacked maintainer publishing through a legitimate pipeline |
| Reachability analysis | Prioritizes | No | No | No | Malware; over-approximation leaves some noise |
| Install-time package analysis (firewall) | Partly, by policy | Blocks known and detected malware before download | By policy | Through signals | Malware not yet detected |
| License scanning | No | No | Yes | No | Missing or legacy license metadata |
| Health scores (OpenSSF Scorecard) | No | No | No | Yes | Point-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](https://socket.dev/blog/anthropic-oss-scanner). 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.
