Let's be honest: modern JavaScript projects can feel like a tangled web of packages. Knowing exactly what's in your final build is crucial, especially with rising security concerns. That's where a Software Bill of Materials (SBOM) comes in handy – it lists out all the components. We'll walk you through creating SBOMs for your JavaScript projects using Anchore's open-source tool called Syft, which makes the process surprisingly easy (and free!).

Why You Need SBOMs for Your JavaScript Projects

JavaScript developers face unique supply chain security challenges. The NPM ecosystem has seen numerous security incidents, from protestware to dependency confusion attacks. With most JavaScript applications containing hundreds or even thousands of dependencies, manually tracking each one becomes impossible.

SBOMs solve this problem by providing:

  • Vulnerability management: Quickly identify affected packages when new vulnerabilities emerge
  • License compliance: Track open source license obligations across all dependencies
  • Dependency visibility: Map your complete software supply chain
  • Regulatory compliance: Meet evolving government and industry requirements

Let's explore how to generate SBOMs across different JavaScript project scenarios.

Getting Started with Syft

Syft is an open source SBOM generation tool that supports multiple formats including SPDX and CycloneDX. It's written in Go, and ships as a single binary. Let's install it:

For Linux & macOS:

# Install the latest release of Syft using our installer script
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin

Alternatively, use Homebrew on macOS:

brew install syft

For Microsoft Windows:

winget install Anchore.Syft

Verify the installation:

syft version
Application:     syft
Version:         1.20.0
BuildDate:       2025-02-21T20:44:47Z
GitCommit:       46522bcc5dff8b65b61a7cda1393abe515802306
GitDescription:  v1.20.0
Platform:        darwin/arm64
GoVersion:       go1.24.0
Compiler:        gc

Scenario 1: Scanning a JavaScript Container Image

Let's start by scanning a container image of EverShop, an open source NodeJS e-commerce platform. Container scanning is perfect for projects already containerized or when you want to analyze production-equivalent environments.

# Pull and scan the specified container
syft evershop/evershop:latest

Here's the first few lines, which summarise the work Syft has done.

  Loaded image        evershop/evershop:latest
  Parsed image        sha256:d29e670d6b2ada863…
  Cataloged contents  9f402cbc7ddf769ce068a101…
   ├──  Packages                        [1,188 packages]
   ├──  File digests                    [1,255 files]
   ├──  File metadata                   [1,255 locations]
   └──  Executables                     [26 executables]

Next is a human-readable table consisting of the name of the software package, the version found and the type which could be npm, deb, rpm and so-on. The output is very long (over a thousand lines), because, as we know, javascript applications often contain many packages. We're only showing the first and last few lines here:

NAME                       VERSION         TYPE
@alloc/quick-lru           5.2.0           npm
@ampproject/remapping      2.3.0           npm
@babel/cli                 7.26.4          npm
@babel/code-frame          7.26.2          npm
@babel/compat-data         7.26.3          npm

yargs                      16.2.0          npm
yargs-parser               20.2.9          npm
yarn                       1.22.22         npm
zero-decimal-currencies    1.2.0           npm
zlib                       1.3.1-r2        apk

The output shows a comprehensive inventory of packages found in the container, including:

  • System packages (like Ubuntu/Debian packages)
  • Node.js dependencies from package.json
  • Other language dependencies if present

For a more structured output that can be consumed by other tools, use format options:

# Scan the container and output a CycloneDX SBOM
syft evershop/evershop:latest -o cyclonedx-json > ./evershop-sbom.json

This command generates a CycloneDX JSON SBOM, which is widely supported by security tools and can be shared with customers or partners.

Scenario 2: Scanning Source Code Directories

When working with source code only, Syft can extract dependency information directly from package manifest files.

Let's clone the EverShop repository and scan it:

# Clone the repo
git clone https://github.com/evershopcommerce/evershop.git
cd ./evershop
# Check out the latest release
git checkout v1.2.2
# Create a human readble list of contents
syft dir:.
  Indexed file system  .
  Cataloged contents   cdb4ee2aea69cc6a83331bbe96dc2c…
   ├──  Packages                        [1,045 packages]
   ├──  File digests                    [3 files]
   ├──  File metadata                   [3 locations]
   └──  Executables                     [0 executables]
[0000]  WARN no explicit name and version provided for directory source, deriving artifact ID from the given path (which is not ideal)
NAME                       VERSION         TYPE
@alloc/quick-lru.          5.2.0           npm
@ampproject/remapping      2.3.0           npm
@aws-crypto/crc32          5.2.0           npm
@aws-crypto/crc32c         5.2.0           npm
@aws-crypto/sha1-browser   5.2.0           npm

yaml                       1.10.2          npm
yaml                       2.6.0           npm
yargs                      16.2.0          npm
yargs-parser               20.2.9          npm
zero-decimal-currencies    1.2.0           npm

The source-only scan focuses on dependencies declared in package.json files but won't include installed packages in node_modules or system libraries that might be present in a container.

For tracking changes between versions, we can check out a specific tag:

# Check out an earlier tag from over a year ago
git checkout v1.0.0
# Create a machine readable SBOM document in SPDX format
syft dir:. -o spdx-json > ./evershop-v1.0.0-sbom.json

Scenario 3: Scanning a Built Project on Your Workstation

For the most complete view of your JavaScript project, scan the entire built project with installed dependencies:

# Assuming you're in your project directory and have run npm install
syft dir:. -o spdx-json > ./evershop-v1.2.2-sbom.json
# Grab five random examples from the SBOM with version and license info
jq '.packages[] | "\(.name) \(.versionInfo) \(.licenseDeclared)"' \
    < ./evershop-v1.2.2-sbom.json | shuf | head -n 5
"pretty-time 1.1.0 MIT"
"postcss-js 4.0.1 MIT"
"minimist 1.2.8 MIT"
"@evershop/postgres-query-builder 1.2.0 MIT"
"path-type 4.0.0 MIT"

This approach captures:

  • Declared dependencies from package.json
  • Actual installed packages in node_modules
  • Development dependencies if they're installed
  • Any other files that might contain package information

Going Beyond SBOM Generation: Finding Vulnerabilities with Grype

An SBOM is most valuable when you use it to identify security issues. Grype, another open source tool from Anchore, can scan directly or use Syft SBOMs to find vulnerabilities.

For Linux & macOS:

# Install the latest release of Grype using our installer script
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

Alternatively, use Homebrew on macOS:

brew install grype

For Microsoft Windows:

winget install Anchore.Grype

Verify the installation:

grype version
Application:         grype
Version:             0.89.1
BuildDate:           2025-03-13T20:22:27Z
GitCommit:           718ea3060267edcae7b10a9bf16c0acdad10820a
GitDescription:      v0.89.1
Platform:            darwin/arm64
GoVersion:           go1.24.1
Compiler:            gc
Syft Version:        v1.20.0
Supported DB Schema: 6

Let's check an older version of EverShop for known vulnerabilities. Note that the first time you run grype, it will download a ~66MB daily vulnerability database and unpack it.

# Clone the example repo, if we haven't already
git clone https://github.com/evershopcommerce/evershop.git
cd ./evershop
# Check out an older release of the application from > 1 year ago
git checkout v1.0.0
# Create an SPDX formatted SBOM and keep it
syft dir:. -o spdx-json > ./evershop-v1.0.0-sbom.json
# Scan the SBOM for known vulnerabilities
grype ./evershop-v1.0.0-sbom.json

We can also scan the directory directly with Grype, which leverages Syft internally. However, it's usually preferable to use Syft to generate the SBOM initially, because that's a time consuming part of the process.

grype dir:.

Either way we run it, Grype identifies vulnerabilities in the dependencies, showing severity levels, the vulnerability ID, and version that the issue was fixed in.

  Scanned for vulnerabilities     [43 vulnerability matches]
   ├── by severity: 2 critical, 19 high, 14 medium, 8 low, 0 negligible
   └── by status:   40 fixed, 3 not-fixed, 0 ignored
NAME                    INSTALLED   FIXED-IN    TYPE  VULNERABILITY        SEVERITY
@babel/helpers          7.20.7      7.26.10     npm   GHSA-968p-4wvh-cqc8  Medium
@babel/runtime          7.22.5      7.26.10     npm   GHSA-968p-4wvh-cqc8  Medium
@babel/traverse         7.20.12     7.23.2      npm   GHSA-67hx-6x53-jw92  Critical
@evershop/evershop      1.0.0-rc.8  1.0.0-rc.9  npm   GHSA-32r3-57hp-cgfw  Critical
@evershop/evershop      1.0.0-rc.8  1.0.0-rc.9  npm   GHSA-ggpm-9qfx-mhwg  High
axios                   0.21.4      1.8.2       npm   GHSA-jr5f-v2jv-69x6  High

We can even ask Grype to explain the vulnerabilities in more detail. Let's take one of the critical vulnerabilities and get Grype to elaborate on the details. Note that we are scanning the existing SBOM, which is faster than running Grype against the container or directory, as it skips the need to build the SBOM internally.

grype ./evershop-v1.0.0-sbom.json -o json | grype explain --id GHSA-67hx-6x53-jw92

The output is a human readable description with clickable links to find out more from the upstream sources.

GHSA-67hx-6x53-jw92 from github:language:javascript (Critical)
Babel vulnerable to arbitrary code execution when compiling specifically crafted malicious code
Related vulnerabilities:
    - nvd:cpe CVE-2023-45133 (High)
Matched packages:
    - Package: @babel/traverse, version: 7.20.12
      PURL: pkg:npm/%40babel/[email protected]
      Match explanation(s):
          - github:language:javascript:GHSA-67hx-6x53-jw92 Direct match (package name, version, and ecosystem) against @babel/traverse (version 7.20.12).
      Locations:
URLs:
    - https://github.com/advisories/GHSA-67hx-6x53-jw92
    - https://nvd.nist.gov/vuln/detail/CVE-2023-45133

Auditing Licenses with Grant

Security isn't the only compliance concern for JavaScript developers. Grant helps audit license compliance based on the SBOM data.

For Linux & macOS:

curl -sSfL https://raw.githubusercontent.com/anchore/grant/main/install.sh | sh -s -- -b /usr/local/bin

Alternatively, use Homebrew on macOS:

brew install anchore/grant/grant

Grant is not currently published for Microsoft Windows, but can be built from source.

Verify the installation:

grant version
Application: grant
Version:    0.2.6
BuildDate:  2025-01-22T21:09:16Z
GitCommit:  d24cecfd62c471577bef8139ad28a8078604589e
GitDescription: v0.2.6
Platform:   darwin/arm64
GoVersion:  go1.23.4
Compiler:   gc

# Analyze licenses used by packages listed in the SBOM
grant analyze -s evershop-sbom.json

Grant identifies licenses for each component and flags any potential license compliance issues in your dependencies. By default the Grant configuration has a deny-all for all licenses.

* ./evershop-v1.0.0-sbom.json
  * license matches for rule: default-deny-all; matched with pattern *
    * Apache-2.0
    * Artistic-2.0
    * BSD-2-Clause
    * BSD-3-Clause
    * CC-BY-3.0
    * CC0-1.0
    * ISC
    * MIT
    * Unlicense
    * WTFPL

Finding out which packages are under what license is straightforward with the --show-packages option:

grant check ./evershop-v1.0.0-sbom.json --show-packages
* ./evershop-v1.0.0-sbom.json
  * license matches for rule: default-deny-all; matched with pattern *
    * Apache-2.0
      * @ampproject/remapping
      * @webassemblyjs/leb128
      * @xtuc/long
      * acorn-node
      * ansi-html-community

Integrating SBOMs into Your Development Workflow

For maximum benefit, integrate SBOM generation and vulnerability scanning into your CI/CD pipeline:

  • Generate during builds: Add SBOM generation to your build process
  • Scan for vulnerabilities: Automatically check for security issues
  • Store SBOMs as artifacts: Keep them alongside each release
  • Track changes: Compare SBOMs between versions to identify supply chain changes

For example, in GitHub workflows use our sbom-action and scan-action, built on Syft and Grype:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Create SBOM
        uses: anchore/sbom-action@v0
        id: sbom
        with:
          format: spdx-json
          output-file: "${{ github.event.repository.name }}-sbom.spdx.json"
      
      - name: Scan SBOM
        uses: anchore/scan-action@v6
        id: scan
        with:
          sbom: "${{ github.event.repository.name }}-sbom.spdx.json"
          fail-build: false
          severity-cutoff: medium
          output-format: json
          
      - name: Upload SBOM as artifact
        uses: actions/upload-artifact@v2
        with:
          name: sbom.json
          path: sarif_file: ${{ steps.sbom.outputs.sbom }}

Best Practices for JavaScript SBOM Generation

  • Generate SBOMs for both development and production dependencies: Each has different security implications
  • Use package lockfiles: These provide deterministic builds and more accurate SBOM generation
  • Include SBOMs in your release process: Make them available to users of your libraries or applications
  • Automate the scanning process: Don't rely on manual checks
  • Keep tools updated: Vulnerability databases are constantly evolving

Wrapping Up

The JavaScript ecosystem moves incredibly fast, and keeping track of what's in your apps can feel like a never-ending battle. That's where tools like Syft, Grype, and Grant come in. They give you X-ray vision into your dependencies without the hassle of sign-ups, API keys, or usage limits.

Once developers start generating SBOMs and actually see what's lurking in their node_modules folders, they can't imagine going back to flying blind. Whether you're trying to patch the next Log4j-style vulnerability in record time or just making sure you're not accidentally violating license terms, having that dependency data at your fingertips is a game-changer.

Give these tools a spin in your next project. Your future self will thank you when that critical security advisory hits your inbox, and you can immediately tell if you're affected and exactly where.


Want to learn more about software supply chain security? Check out our resources on SBOM management and container vulnerability scanning.