Most Node.js teams already have the tooling to produce a Software Bill of Materials and don't know it. Since npm CLI v9, the npm sbom command has shipped in the box, reading package-lock.json and emitting a CycloneDX or SPDX document with zero extra installs. That's a meaningful shift: as recently as 2021, generating an SBOM meant reaching for a third-party tool and hoping its dependency resolution matched what actually got installed. The regulatory backdrop is now well established too — Executive Order 14028, signed 12 May 2021, directed federal software vendors toward SBOM requirements, and the NTIA's July 2021 "Minimum Elements for a Software Bill of Materials" named SPDX, CycloneDX, and SWID as the three acceptable formats. (EO 14028 itself remains in effect, though OMB moved in early 2026 to rescind the centralized SSDF self-attestation memos that had implemented it, shifting enforcement to a more decentralized, agency-led model — SBOMs as an artifact aren't going away, but the compliance mechanics around them are still shifting.) CycloneDX, an OWASP-founded standard now stewarded jointly with Ecma International, has moved on to spec version 1.7 (published October 2025); SPDX — whose 2.2.1 release is ratified as the ISO/IEC 5962:2021 international standard — underwent a full model overhaul in SPDX 3.0 (released 16 April 2024, with the 3.0.1 patch release following in December 2024) that added "profiles" for security and AI use cases, though that newer 3.x line has not itself completed ISO ratification yet. This post walks through generating both formats for a real Node.js project, starting with npm's native command, moving to dedicated generators for CI-grade output, and closing with what to actually do with the SBOM once it exists.
What's the fastest way to generate a Node.js SBOM with zero extra tooling?
The fastest path is npm sbom, a command built into npm CLI v9 and v10 that reads your package-lock.json directly — no crawling node_modules, no network calls, no separate install. Running npm sbom --sbom-format cyclonedx from a project root produces a CycloneDX 1.5-family JSON document listing every resolved dependency, direct and transitive, with its version and resolved integrity hash; swapping to --sbom-format spdx produces an SPDX 2.3 document instead. Because it reads the lockfile rather than the filesystem, it's fast even on large monorepos and it's deterministic — the same lockfile always produces the same component list. The tradeoff is that it's intentionally minimal: it won't enrich components with license detection beyond what's in package.json, won't flag known vulnerabilities, and doesn't support CycloneDX's newer 1.6/1.7 fields like cryptographic asset inventories or attestations. Treat npm sbom as the baseline every Node.js repo should be able to produce today, not the end state of an SBOM program.
When should you reach for a dedicated CycloneDX generator instead?
Reach for @cyclonedx/cyclonedx-npm when you need control over spec version, output format, or richer component metadata than the native npm command provides. Installed via npx @cyclonedx/cyclonedx-npm --output-format JSON --output-file sbom.json, it supports every CycloneDX spec version from 1.2 through 1.6, selectable with --spec-version, and defaults to 1.6 — one release behind the current 1.7 spec, but still the newest version the tool itself targets. It also emits valid XML with --output-format XML for toolchains that expect it, and it can include development dependencies with --omit dev inverted, which matters if your security policy wants visibility into build-time tooling, not just runtime code. For projects that span more than Node.js — a repo with a Python data-processing script or a Go sidecar alongside the Node app — the broader @cyclonedx/cdxgen tool covers multiple ecosystems from a single invocation, which is worth the extra dependency in mixed-language monorepos where cyclonedx-npm would only see part of the picture.
Is SPDX 2.3 or SPDX 3.0 the right target for a Node.js project today?
SPDX 2.3 remains the practical target for most Node.js pipelines, even though SPDX 3.0 is the current published specification line. SPDX 3.0, released 16 April 2024, restructured the entire data model around use-case-specific "profiles" — including a security profile designed to carry vulnerability and VEX data natively — with 3.0.1 following as a point release in December 2024. Note that the ISO/IEC 5962:2021 designation still refers specifically to SPDX 2.2.1; the 3.x line has not yet completed its own ISO submission. The problem is tooling maturity: npm's native npm sbom --sbom-format spdx command, along with most CI integrations and SBOM consumers in production today, still generates and expects SPDX 2.2 or 2.3 documents. Unless a downstream consumer specifically requires the 3.0 model — a federal contract mandating the security profile, for example — generating SPDX 2.3 keeps you compatible with the widest set of validators, viewers, and vulnerability-matching pipelines. Revisit this as 3.0 tooling matures; the format itself isn't going away.
Why do CycloneDX and SPDX both need to exist for the same project?
They coexist because they were built for different primary audiences, and many organizations end up needing to produce both. CycloneDX grew out of the OWASP Dependency-Track project with security tooling as the first-class use case — its schema has dedicated fields for vulnerability data, pedigree, and component evidence baked in from early versions. SPDX grew out of software license compliance auditing, and its strength is precise, machine-readable license and copyright attribution across a dependency tree — a requirement CycloneDX supports but treats as secondary. A vendor selling into the federal government under EO 14028's SBOM guidance may be asked for either format depending on the contracting agency, since NTIA's Minimum Elements document named both as acceptable. Generating both from the same package-lock.json in one CI step, rather than picking one, avoids a scramble the first time a customer or auditor asks for the other.
How Safeguard Helps
Safeguard treats SBOM generation as a starting point, not a deliverable to file away. The Safeguard CLI wraps this exact workflow — npm install -g @safeguard-sh/cli, then safeguard auth login and safeguard sbom generate --source . --name "my-app" --version "1.0.0" — so a Node.js project gets a governed SBOM generated in CI the same way it would locally, without hand-picking between npm sbom, cyclonedx-npm, and cdxgen on every repo. Once generated, that SBOM isn't a static file: Safeguard's platform ingests it into a queryable dependency tree with per-component vulnerability, license, and provenance data, and exports it back out in more than 30 formats, including SPDX (JSON, XML, RDF) and CycloneDX (JSON, XML), so the same underlying scan satisfies whichever format a customer, auditor, or downstream tool asks for next.