Generating an SBOM with Open Source Tools

Explains how to generate an SBOM for each environment using general-purpose open source tools.

With Docker installed, BomLens alone can scan source code, container images, server rootfs, executables, and firmware. Consider reviewing it first instead of installing and combining the open source tools below individually.

Tool Selection Guide

graph TD
    A{{"Classify the supplied software"}}

    subgraph G1["Software delivery"]
      direction LR
      T1["Source code / app<br>(e.g., OSS/BSS, portals, middleware)"]
      T2["Executable / library<br>(e.g., .jar, .dll, .so)"]
      T3["Firmware with no OS<br>(e.g., bare-metal / RTOS devices)"]
    end

    subgraph G2["Delivery including an OS (e.g., Linux)"]
      direction LR
      T4["Container image<br>(e.g., CNF, containerized network function)"]
      T5["Server / VM image<br>(e.g., VNF, server appliance)"]
      T6["Firmware with an embedded OS<br>(e.g., base stations, routers, OLT/ONT, set-top boxes)"]
    end

    %% Left: source-code scan with an inner box
    subgraph M1["Scan the source code"]
      M1_Sub["BomLens or cdxgen"]
    end

    %% Right: source + OS image scan with inner boxes (stacked vertically)
    subgraph M2["Scan source + OS image"]
      direction TB
      M2_Top["OS (e.g., Linux) scan<br>(BomLens or Syft/Trivy)"]
      M2_Bottom["Source code scan<br>(BomLens or cdxgen)"]
    end

    A --> G1
    A --> G2
    
    %% Connect only to the group borders (one arrow per box)
    G1 --> M1
    G2 --> M2
    
    %% Groups flow to the next step
    M1 --> P(["Submit the SBOM"])
    M2 --> P

    classDef start fill:#F2F2F2,stroke:#171717,color:#171717,stroke-width:1.5px
    classDef typebox fill:#ffffff,stroke:#c8c8c8,color:#171717,stroke-width:1px
    classDef submit fill:#F2F2F2,stroke:#171717,color:#171717,stroke-width:1.5px
    
    %% White inner-box styles (left/right border colors)
    classDef subwhite_left fill:#ffffff,stroke:#00A651,color:#171717,stroke-width:1px
    classDef subwhite_right fill:#ffffff,stroke:#68127A,color:#171717,stroke-width:1px

    class A start
    class T1,T2,T3,T4,T5,T6 typebox
    class M1_Sub subwhite_left
    class M2_Top,M2_Bottom subwhite_right
    class P submit

    style G1 fill:#F1FAF5,stroke:#00A651,stroke-width:1px,color:#0A5A32
    style G2 fill:#FAF4FB,stroke:#68127A,stroke-width:1px,color:#4A0D57
    
    %% Outer group boxes keep their fill and border colors
    style M1 fill:#D9F0E4,stroke:#00A651,stroke-width:1px,color:#0A5A32
    style M2 fill:#EEDCF3,stroke:#68127A,stroke-width:1px,color:#4A0D57

Source code and apps, executables or libraries, and firmware with no OS are all scanned from the source code you developed with BomLens or cdxgen. Scanning a finished binary directly yields no package manager metadata, so purls are omitted and the SBOM is rejected.

When you ship an OS or base image as part of the delivery (a container image, a server, or firmware with an embedded OS), split it into two layers, scan each, and submit them together. Scan the image or rootfs as delivered with BomLens or Syft/Trivy for the OS layer, and the source code (the app layer) with BomLens or cdxgen. The per-layer commands and the file naming rule are in Server delivery below.

If you supply commercial software or a finished product made by a third party and have no access to the source code, obtain the SBOM from the manufacturer instead of scanning. See Commercial Software.

Major Tools

BomLens (provided by SK Telecom)

BomLens is an SBOM generation tool built by SK Telecom. It scans source code, container images, server rootfs, executables, and firmware with a single Docker container, instead of installing the open source tools below individually. It also includes features tuned for SK Telecom’s submission criteria, such as automatically filling in the top-level component name and self-checking the SBOM (a conformance report) before submission.

Before submission, verify against SK Telecom’s own review criteria (100% PURL coverage and more) with the --conformance-profile skt-submission option. See the Validation Checklist for details. If a scan errors out or the result looks wrong, check Common Rejection Reasons for a similar case first.

If BomLens does not cover your case, or you prefer to combine open source tools directly, see below.

cdxgen (source code analysis)

Automatically analyzes projects in various languages such as Java, Python, Node.js, and Go, and generates an SBOM in CycloneDX format.

cdxgen statically parses lockfiles and manifests. For accurate results, run it when dependencies are installed or resolved (a lockfile is present, or after a build). Scanning pure source without resolved dependencies may omit some components or purls.

Syft (container image and binary analysis)

Analyzes built container images and build artifacts that include package manager metadata to identify both OS packages and application libraries. Supports CycloneDX and SPDX formats.

Trivy (container image analysis)

An all-in-one tool that can perform container image analysis and vulnerability scanning together.

Language-Specific Dedicated Plugins

Using a build tool plugin lets you extract more accurate dependency information.

Language/Build ToolPlugin/ToolOfficial Documentation
Java (Maven)cyclonedx-maven-pluginLink
Java (Gradle)cyclonedx-gradle-pluginLink
Pythoncyclonedx-bomLink
Node.js@cyclonedx/cyclonedx-npmLink
Gocyclonedx-gomodLink

Server Delivery

This applies when you deliver something that includes an OS or base image (a server, a container image, or firmware with an embedded OS). Generate each of the two layers and submit them together.

LayerTargetSymptom if missing
OSThe operating system and every installed package (for example RHEL and every package in the rpm database)OS vulnerabilities omitted
ApplicationThe delivered application and its package manager dependencies (direct and transitive)App dependencies omitted

Scan the two layers separately

For the OS layer, target the server’s rootfs (the extracted root filesystem) or its container image. Syft reads the package database (rpm/dpkg/apk) and identifies every installed package with a real purl (pkg:rpm/...). The target must be the state delivered after the build, not the original base image you received, because it must include the OS packages installed during the build. Scanning a folder that only holds unpacked installation files with no package database yields empty purls and is rejected.

The target must be the root of the rootfs. Point Syft at a subdirectory and it still reads the package database, but it cannot determine the distribution. Syft takes the distribution from /etc/os-release inside the target and writes it into each purl. A correct result looks like pkg:rpm/rhel/bind@9.11.36-16.el8_10.6, with the distribution between the type and the package name. When that slot is empty, the SBOM passes format validation but SK Telecom’s system cannot identify the packages, so every OS package fails to match and the submission is rejected. Confirm that this file is present in the target before you scan.

# First confirm the target carries distribution information
cat /path/to/server-rootfs/etc/os-release

# With BomLens (point it at the rootfs directory directly; it also fills in the top-level component name)
./scan-sbom.sh --project myserver-os --version 1.0.0 --target /path/to/server-rootfs --generate-only

# To use an open source tool directly instead: against a rootfs directory
syft dir:/path/to/server-rootfs -o cyclonedx-json=myserver-os_1.0.0_bom.json

# If the server is packaged as a container image (BomLens takes the image name as the target directly)
./scan-sbom.sh --project myserver-os --version 1.0.0 --target myserver:7 --generate-only
syft myserver:7 -o cyclonedx-json=myserver-os_1.0.0_bom.json

For the application layer, scan the application source after the build is complete. With a package manager (Maven, npm, pip, Go modules, Conan, and so on), transitive dependencies are resolved automatically.

cd /path/to/app-source
cdxgen -o myserver-app_1.0.0_bom.json

# Or with BomLens
./scan-sbom.sh --project myserver-app --version 1.0.0 --target /path/to/app-source --generate-only

The OS-layer scan sometimes picks up dependencies installed as files, such as Python or Node.js packages. Generate the application layer anyway. A C/C++ application that vendors its libraries into the source is not identified by the OS-layer scan at all.

Firmware with an embedded OS (base stations, routers, and similar)

Firmware that embeds an OS, such as a base station, router, OLT/ONT, or set-top box, follows the same two-layer structure above. For the OS layer, target the whole firmware image file and use BomLens’s --firmware option.

./scan-sbom.sh --project mydevice-os --version 1.0.0 --target /path/to/firmware.bin --firmware --generate-only

If a separate application runs on top of an embedded Linux, scan its source the same way as the “application layer” above. If the firmware image already bundles the application, the OS-layer scan alone may be enough.

Submit each layer

Submit the per-layer SBOMs as they are, without merging them. SK Telecom’s system registers each SBOM document as one scan unit and treats the documents registered against the same product version as a single combined list. The layers may even use different formats.

Each file needs its own name, and a resubmission must reuse the same name. The SBOM document name is the identity of the scan, so a changed name leaves the previous submission in place and vulnerabilities you have already fixed keep being counted. A suffix naming the layer is safe because it does not change on resubmission, but do not append a sequence number for the submission round.

LayerExample file name
OSmyserver-os_1.0.0_bom.json
Applicationmyserver-app_1.0.0_bom.json

The layer is distinguished by a suffix on the --project value (-os, -app). BomLens always produces {project name}_{version}_bom.json, so give each layer a different --project (e.g. myserver-os, myserver-app) when generating both with BomLens. Running it twice with the same name produces the same file name, so the later run overwrites the earlier one’s output. When you generate a layer directly with cdxgen or Syft, match its output file name to the same convention (e.g. -o myserver-app_1.0.0_bom.json).

Record the same value as the leading part of the file name ({name}_{version}) for the top-level component name (metadata.component.name in CycloneDX, DocumentName in SPDX). BomLens fills this in automatically from --project/--version, so you do not need to edit it by hand. That value is the identifier that must be unique across all submissions. See the metadata section of Submission Requirements for details.

For how to decide the submission unit for a product with several nodes, such as a cluster, see the submission unit section of Submission Procedure.

Common Precautions

Verify the following before using a tool.

  • Transitive dependency inclusion: Generate the SBOM after the build (package installation) is complete so that transitive dependencies are included. Missing dependencies are grounds for rejection; for the per-language build commands to run first, see the dependency scope section of the Submission Requirements.
  • PURL inclusion: Verify that the generated SBOM includes a purl field for every component. SK Telecom’s system maps vulnerabilities based on PURL. For the verification commands and the regeneration procedure, see the Validation Checklist.
  • Output format: CycloneDX JSON format is recommended. (Use -o cyclonedx-json or an equivalent option)
  • Project information: Verify that the metadata accurately records the name and version of the delivered project.