Back to glossary

Security Advisory

A security advisory is a published record that specific package versions are vulnerable or malicious, naming affected versions, the fix, and severity.

Also known as Security advisories, Security bulletin, Vulnerability advisory, Malware advisory, Advisory database

A security advisory, also called a security bulletin, is a published notice that specific versions of a software package are vulnerable or malicious. In open source it is a structured record naming the package, the affected version range, the fixed version, and a severity, which scanners like npm audit match against your lockfile. Advisories only cover threats someone already found.

The matching step is the whole mechanism. A scanner never reads the vulnerable code. It compares the package names and versions in your dependency tree against the version ranges in advisory records, and it raises an alert on every overlap. Everything a security advisory catches, and everything it misses, follows from that design.

The cost of that design is measured in hours. In the axios compromise, the malicious dependency plain-crypto-js went live on npm at 23:59:12 UTC on March 30, 2026. Our scanner flagged it 6 minutes and 29 seconds later. The first advisory record with a public timestamp, OSV MAL-2026-2306, was published 2 hours and 8 minutes after the upload. Until a record exists, npm audit has nothing to match malware against.

Our position: security advisories are the right mechanism for known vulnerabilities and the wrong primary defense against malware. An advisory is a record of what someone already noticed, written after the fact and matched by name and version. Use advisories as the floor of your vulnerability data. Do not let a clean advisory scan stand in for analyzing what a package actually does.

What is a security advisory?

#

A security advisory is a publisher's formal statement about a flaw: which versions are affected, how serious it is, and which version fixes it. Maintainers, software vendors, Linux distributions, ecosystem security teams, and national CERTs all publish them. In open source, most advisories end up as JSON records in the Open Source Vulnerability (OSV) format, collected by aggregators such as the GitHub Advisory Database and OSV.dev.

A machine-readable advisory carries a small set of fields that do the work:

  • Identifier and aliases. A database-native ID such as GHSA-78xj-cgh5-2h22, plus the same vulnerability's IDs elsewhere, usually a CVE ID.
  • Affected package and range. An ecosystem, a package name, and range events: introduced, fixed, last_affected, or limit.
  • Fixed version. The release to upgrade to, when one exists.
  • Severity. A CVSS vector, a label such as high, or both.
  • Weakness and references. A CWE ID, plus links to the report, the fix commit, and credits.
  • Timestamps. published, modified, and withdrawn, each an RFC 3339 time in UTC.

The npm registry serves advisories to scanners through a bulk endpoint. Here is the live response for ip@2.0.0, fetched on November 1, 2026:

Bash
curl -s -X POST https://registry.npmjs.org/-/npm/v1/security/advisories/bulk \
  -H 'content-type: application/json' \
  -d '{"ip":["2.0.0"]}'
JSON
{
  "ip": [
    {
      "id": 1101851,
      "url": "https://github.com/advisories/GHSA-2p57-rm9w-gvfp",
      "title": "ip SSRF improper categorization in isPublic",
      "severity": "high",
      "vulnerable_versions": "<=2.0.1",
      "cwe": ["CWE-918"],
      "cvss": {
        "score": 8.1,
        "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H"
      }
    },
    {
      "id": 1114832,
      "url": "https://github.com/advisories/GHSA-78xj-cgh5-2h22",
      "title": "NPM IP package incorrectly identifies some private IP addresses as public",
      "severity": "low",
      "vulnerable_versions": "=2.0.0",
      "cwe": ["CWE-918"],
      "cvss": { "score": 0, "vectorString": null }
    }
  ]
}

Two details in that response show how much judgment hides inside a record. The first advisory affects <=2.0.1, and 2.0.1 is still the latest release of ip, published February 19, 2024, so there is no fixed version to upgrade to. The second advisory is rated low with a CVSS score of 0. It was filed as a 9.8 critical, and in the node-ip severity dispute GitHub lowered it after the maintainer objected.

On GitHub, an advisory starts as a repository security advisory. The maintainer opens a private draft, fixes the bug in a temporary private fork, and can request a CVE ID from GitHub, which is a CVE Numbering Authority (CNA). Publishing makes the advisory public, and GitHub's curators then map it to a package so that scanners can match it. A record with no package mapping is invisible to every scanner that matches by name.

How do scanners match security advisories to your dependencies?

#

Scanners match security advisories by package name and version, never by inspecting code. npm audit sends the name and installed versions of every package in your tree to /-/npm/v1/security/advisories/bulk, and falls back to an older Quick Audit endpoint if that call fails, per npm's documentation. Python tools do the equivalent against OSV data, which PyPI also exposes in the vulnerabilities field of its JSON API.

To see what that matching produces, we wrote a lockfile that pins axios@1.14.1, one of the two malicious axios releases, and ran npm 10.9.9 against it on November 1, 2026. The --package-lock-only flag audits the lockfile without installing anything, so nothing malicious runs.

Bash
$ npm audit --package-lock-only
# npm audit report

axios  1.0.0 - 1.19.0
Severity: critical
Malware in axios - https://github.com/advisories/GHSA-fw8c-xr5c-95f9
Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF - https://github.com/advisories/GHSA-3p68-rc4w-qgx5
Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy - https://github.com/advisories/GHSA-w9j2-pvgh-6h63
# ... 32 more advisory lines ...
No fix available
node_modules/axios

1 critical severity vulnerability

$ npm audit --package-lock-only --json | jq -c '.metadata.vulnerabilities, (.vulnerabilities.axios.via | length)'
{"info":0,"low":0,"moderate":0,"high":0,"critical":1,"total":1}
35

npm received 35 advisories for that one version and reported one critical vulnerability. Thirty-four describe ordinary bugs with fixed versions. One says the package is malware. All 35 arrive in the same shape, and the summary collapses them into a single count.

Counting has a second trap: one vulnerability usually has several records. PyPI's JSON API returns six records for requests 2.31.0:

Bash
$ curl -s https://pypi.org/pypi/requests/2.31.0/json | jq -c '.vulnerabilities[] | {id, aliases, fixed_in}'
{"id":"GHSA-9wx4-h78v-vm56","aliases":["CVE-2024-35195","PYSEC-2026-1873"],"fixed_in":["2.32.0"]}
{"id":"GHSA-9hjg-9r4m-mvj7","aliases":["CVE-2024-47081","PYSEC-2026-1872"],"fixed_in":["2.32.4"]}
{"id":"GHSA-gc5v-m9x4-r6x2","aliases":["CVE-2026-25645","PYSEC-2026-2275"],"fixed_in":["2.33.0"]}
{"id":"PYSEC-2026-1873","aliases":["CVE-2024-35195","GHSA-9wx4-h78v-vm56"],"fixed_in":["2.32.0"]}
{"id":"PYSEC-2026-1872","aliases":["CVE-2024-47081","GHSA-9hjg-9r4m-mvj7"],"fixed_in":["2.32.4"]}
{"id":"PYSEC-2026-2275","aliases":["CVE-2026-25645","GHSA-gc5v-m9x4-r6x2"],"fixed_in":["2.33.0"]}

The requests output contains three vulnerabilities, not six. Each one appears as a GHSA record and as a PYSEC record from the Python Packaging Authority's advisory database, and both records carry the same CVE ID. The OSV specification defines aliases as symmetric and transitive, so the correct unit to count is the alias group. A dashboard that counts IDs double-counts, and a ticket closed on one ID can leave its twin open.

Security advisory vs CVE: what is the difference?

#

A CVE is an identifier, and a security advisory is the document that uses it. The CVE Program assigns CVE-YYYY-NNNNN IDs through CNAs. One CVE can appear in a dozen advisories from different vendors and distributions, and one advisory can bundle many CVEs. A growing share of open source advisories never get a CVE at all.

The OpenClaw advisory surge made that gap visible. By late February 2026, within three weeks of going viral, the OpenClaw project had published more than 200 GitHub Security Advisories, and 170 of them did not yet have a CVE ID. Scanners, SBOM tools, and compliance workflows keyed only on CVE could not see those 170.

The CVE side of the pipeline is also thinning. In April 2026, NIST stopped enriching most CVEs in the National Vulnerability Database after CVE submissions rose 263% between 2020 and 2025. Enrichment is the step that adds CVSS scores and the CPE product mappings that commercial scanners match on. As of August 17, 2026, the NVD listed 42,353 CVEs as "Not Scheduled," against 3,049 in its two active enrichment queues, per our follow-up on NIST's AI plans.

Open source package advisories feel the NVD cuts less, because GHSA and OSV records carry the package name and version range themselves. Commercial products that depend on CPE matching feel them more. Either way, a pipeline that treats the CVE record as its only source of truth is now missing data by policy, not by accident.

Advisory formats: OSV, CSAF, and the CVE record

#

Three formats carry most machine-readable advisories. OSV is the package-ecosystem format, and as of October 2026 its specification is at version 1.9.2. The Common Security Advisory Framework, CSAF 2.0, became an OASIS Standard on November 18, 2022 and is what many vendors and Linux distributions publish. The CVE record is the CVE Program's own JSON for each ID.

PropertyOSVCSAF 2.0CVE record
Identifies software byEcosystem and package name, or a package URLA product tree of vendor products and versionsVendor and product names; NVD adds CPE
Version semanticsSEMVER, ECOSYSTEM, or GIT ranges with introduced and fixed eventsPer-product status such as known_affected and fixedPer-product version ranges
Exploitability (VEX) statementsNoYes: known_not_affected status and a VEX profileNo
Used byGHSA, PyPA, RustSec, Go, OSV.devVendors and distributionsEvery CNA
Best forDependencies resolved by a package managerVendor products and exploitability statementsCross-referencing between databases

Our verdict: use OSV for anything a package manager resolves, because it is the only one of the three that names the package the way your lockfile does. Use CSAF when you ship or consume vendor products and need to state that a component is present but not exploitable. Use the CVE ID as the join key between the two, not as the thing you match on.

Are malware advisories security advisories?

#

Malware advisories are security advisories in every major database, and they break most of the format's assumptions. The GitHub Advisory Database defines three record types: reviewed, unreviewed, and malware, with malware records sourced from the npm security team and the OpenSSF Malicious Packages repository. OSV publishes the same reports under MAL- IDs. A malicious package fails the vulnerability schema in four places:

  • No fixed version. GitHub's documentation says malware alerts do not contain a fix version. Every version the attacker published is the bad version.
  • Severity is invented. The axios malware record carries the label critical with a CVSS score of 0 and no vector. The 2018 flatmap-stream malware record goes the other way, with a full CVSS 3.1 vector that scores it 9.8 as if it were a network-reachable bug.
  • Upgrading is not remediation. The advisory text states it plainly: "Any computer that has this package installed or running should be considered fully compromised." The response is credential rotation and incident response, not a version bump.
  • The record always arrives after the attack. Someone has to detect, confirm, and file before any scanner can match.

The axios timeline shows the last point to scale.

EventUTC timeTime after T+0
plain-crypto-js@4.2.0 published, a clean copy of crypto-js used as a decoyMar 30, 05:57:3218 hours before
plain-crypto-js@4.2.1 published with a postinstall dropper (T+0)Mar 30, 23:59:120
Socket flags plain-crypto-js@4.2.1 as malwareMar 31, 00:05:416m29s
axios@1.14.1 published with plain-crypto-js@4.2.1 as a dependencyMar 31, 00:21:5822m46s
axios@0.30.4 publishedMar 31, 01:00:571h01m45s
OSV MAL-2026-2306 published for plain-crypto-js (alias GHSA-2x9r-6wxq-hrr7)Mar 31, 02:07:582h08m46s
OSV MAL-2026-2307 published for axios 1.14.1 and 0.30.4 (alias GHSA-fw8c-xr5c-95f9)Mar 31, 03:15:493h16m37s
npm replaces plain-crypto-js with a 0.0.1-security holding packageMar 31, 03:25:113h25m59s

Package publish times come from the npm registry's time field, the detection time from our report on the compromise, and the advisory times from the OSV records. GitHub's pages for the matching GHSA records show only the date, March 31. By the OSV timestamps, neither package had an advisory record for 2 hours and 8 minutes, and the poisoned axios@1.14.1 went 2 hours and 54 minutes without one. The dropper ran on postinstall, so wherever install scripts were enabled, the install itself executed it before any audit could report anything.

That gap is why Socket keeps the two signals apart. Socket's CVE alerts, from Critical CVE down to Low CVE, come from advisory data, including GitHub's feed. The Known Malware alert does not wait for an advisory. It comes from our automated analysis of the published package contents, which is what flagged plain-crypto-js 16 minutes before the first poisoned axios release pulled it in.

Checking after the fact has its own blind spot. npm audit reads your lockfile, and our analysis of the axios blast radius showed that tools run through npx or installed globally resolve against the registry and never appear in a project lockfile. A clean audit the day after a compromise proves the project's lockfile is clean, not that no machine ran the malware.

Malware records also fail in the other direction. On May 26, 2026, OSV withdrew 157 malware reports that automated detection had filed against legitimate npm and PyPI packages, including FastAPI 0.136.3. Tools enforcing those records blocked builds until the records moved to withdrawn. A consumer that ignores the withdrawn field keeps blocking them.

Where security advisories go wrong

#

Security advisories are often written and scored by someone other than the maintainer of the code. The ip package shows the cost. CVE-2023-42282 was filed against it as a 9.8 critical, and the maintainer, Fedor Indutny, archived the repository in response, then restored it a week later when GitHub lowered the rating to low. "Someone filed a dubious CVE about my npm package, and then I started getting messages from all people getting warnings from npm audit," Indutny wrote on Mastodon.

Severity scores disagree even between careful publishers. In the curl and Go CVSS rejection, CISA scored curl's CVE-2024-11053 at 9.1 critical while the curl team rated it low. Across 1,567 CVEs that both NVD and GitHub scored, NVD came in higher 885 times and GitHub higher 682 times, per data we covered when NIST cut enrichment.

Status fields drift too. Until 2025, OSV converted CVEs marked "disputed" into withdrawn records, which hid them from every downstream consumer. The OSV disputed-CVE fix, which our team pushed for, restored more than 500 advisories. One of them was CVE-2023-48022 in Ray, a vulnerability the vendor disputed and attackers exploited anyway.

Most records never get human review. A 2026 study of more than 288,000 GitHub Security Advisories found that only about 8% were GitHub-reviewed, and GitHub does not send alerts for unreviewed advisories. Unreviewed records usually lack the package mapping that matching needs.

The practical answer is to stop triaging by score. Fourteen of the 35 advisories on axios@1.14.1 are tagged CWE-1321, prototype pollution, and nearly all describe gadgets: code that turns an existing pollution of Object.prototype into impact. Those gadgets matter only if something else in the process can already pollute the prototype. Reachability analysis, which checks whether your code can actually reach the vulnerable function, separates the few that matter from the rest far better than a CVSS number does.

What we think should change

#

Publishers should ship a fixed version and package coordinates with every advisory, and maintainers need a real chance to dispute a record before it reaches millions of npm audit runs. Databases should stop forcing malware into vulnerability fields. A malware record with a fabricated CVSS vector, or a score of 0 labeled critical, teaches tools to treat an active compromise like a bug with a patch. Consumers should count alias groups, honor withdrawn, and treat advisories as the floor of their data, not the ceiling.

The harder problem is volume without verification. Advisory publishing is decentralizing, through GHSA-only disclosures and GCVE's federated publishing ecosystem, and automating, through machine-filed malware reports and NIST's planned AI enrichment tool. More records will arrive faster, with less human review behind each one. The bottleneck is no longer whether an advisory exists. It is whether anyone checked that it is true.