Query OSV.dev for known vulnerabilities across a whole npm dependency inventory at once: either pass a flat {packages:[...]} list, or paste raw package.json / lockfile / CycloneDX JSON / SPDX JSON content via content. The tool normalizes npm dependencies first, then chunk-queries OSV behind the scenes so large SBOMs don't stop at the upstream 100-package batch limit. Each finding includes severity, a summary, CVE aliases, and the fixed version — not just a bare advisory ID — so a dependency audit answer doesn't need a follow-up call per flagged package. For an explicit packages list or raw package.json content — names that were never actually resolved against a registry, unlike a real lockfile/SBOM — package names are also cross-checked against the npm registry (capped at 200 unique names): a name that doesn't exist there would otherwise show a silent, indistinguishable vulnerabilityCount: 0 — see unresolvedPackages/existenceCheckNote and do not read those entries as a clean bill of health. A packages[].version that doesn't currently appear on the registry (a typo'd/fabricated version, OR a real version that was published and later removed, e.g. unpublished for containing malware) is cross-checked the same way — see nonexistentVersions; don't assume it never existed, and don't assume vulnerabilityCount:0 for it means clean, since OSV can still carry findings for a version the registry no longer lists. A packages[] entry given with NO version at all (e.g. {name:"react"}) is intentionally queried unversioned against OSV — this returns advisories affecting ANY historical published version of that package, not just the latest or whatever a project actually has installed; see the corresponding warnings entry naming which packages this applied to, and don't report "package X is vulnerable" from an unversioned result without separately confirming against the specific version in use (get_package/get_package_version). Each result also carries signals (deprecated, hasInstallScripts for the specific requested/resolved version, popularityTier/maintenanceTier, and possibleTyposquatOf — same deterministic rule-based labels as get_package/search_packages, capped at the same 200 unique names): a clean vulnerabilityCount:0 does NOT mean safe to use if signals flags a likely typosquat, an abandoned/stale package, or a deprecation notice — surface those explicitly rather than reporting only the vulnerability count. signals is null for a scanStatus: "not-scanned" entry, deliberately — a git/file/workspace/URL dependency can be declared under a name that collides with a real npm package (e.g. a git dependency literally named "lodash"), and that unrelated public package's popularity/maintenance signals must not be attached to it just because the name happens to resolve on the registry. A lockfile-resolved result also carries source (resolvedUrl/integrity straight from that lockfile entry, plus nonRegistryHost): nonRegistryHost: true means the tarball URL points somewhere other than the expected npm/yarn registry host — e.g. a compromised mirror or a hand-edited lockfile — which a name+version match against OSV cannot detect on its own, since a malicious tarball can share the same name/version as the real package and carry zero OSV findings. source is null when the input format doesn't record this (package.json content, an explicit packages entry, or pnpm-lock, which never records a resolvedUrl). nonRegistryHost: false alone is NOT proof the tarball is correct — source.identityMismatch: true catches a SAME-HOST swap that host-checking cannot: a lockfile entry can declare e.g. "lodash@4.18.1" while resolvedUrl actually points at the real registry.npmjs.org's own tarball for a completely different package/version, and vulnerabilityCount above was still computed for the DECLARED name/version, not whatever that resolved tarball actually is — treat identityMismatch: true as a lockfile-tamper finding, not a cosmetic mismatch, and see source.resolvedName/resolvedVersion for what the tarball actually names. vulnerabilityCount/advisoryCount are raw OSV/GHSA advisory counts and can over-count: OSV sometimes publishes more than one advisory record for the same underlying CVE — use uniqueVulnerabilityCount (deduped by shared CVE alias) when reporting 'how many distinct issues' rather than a raw advisory tally. When content is itself a package.json (not a lockfile/SBOM), projectLifecycleScripts surfaces that SCANNED PROJECT's own preinstall/install/postinstall/prepare scripts, if any — these run arbitrary code the moment someone runs npm install on the project itself, separate from anything a dependency does, and 'scan my package.json' should not silently skip the one script that actually executes for the project being scanned. An npm alias (e.g. "totally-safe": "npm:minimist@0.0.8") is followed to its real target in every input format — results[i].package.actualName names the real package that vulnerability/signal data attaches to (.name stays the declared/alias key); this is NOT silently skipped, since doing so would let a vulnerable package hide behind whatever name a project calls it. A dependency whose spec points somewhere other than the registry (git/file/workspace/URL) or that never resolved to a version is excluded from vulnerability querying entirely rather than queried by name alone — vulnerabilityCount: 0 for one of these would otherwise misleadingly attach an unrelated public npm package's entire vulnerability history to it. When content is a package.json, peerDependencies are excluded from scanning by default (a peer is often intentionally left unresolved by the consumer) — see ignoredPeerDependencyNames, and pass includePeerDependencies: true to also check them, since a vulnerable/malicious peerDependency is otherwise invisible to this scan. results[i].package.declaredSpec is set whenever .version was RESOLVED from a package.json semver range/tag (e.g. "^18.2.0" -> "18.2.0") rather than being an already-exact pin or a lockfile-derived version — a range can silently pick up a new, possibly-compromised release the next time this project is installed, while an exact pin can't, so don't treat a range-resolved isVulnerable:false as equally durable to a pinned one just because they look identical today.