sbom

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 8 Imported by: 0

README

sbom

Go library for reading and writing Software Bill of Materials documents. Reads CycloneDX and SPDX JSON into a single Document / Package / Relationship model, and writes that model back out as CycloneDX (JSON or XML) or SPDX JSON.

Ported from github.com/andrew/sbom.

Installation

go get github.com/git-pkgs/sbom

Parsing

import "github.com/git-pkgs/sbom"

data, _ := os.ReadFile("bom.cdx.json")
doc, err := sbom.Parse(data)
if err != nil {
    log.Fatal(err)
}

fmt.Println(doc.Type)             // cyclonedx
fmt.Println(doc.SpecVersion)      // 1.6
fmt.Println(doc.Document.Name)    // my-app

for _, p := range doc.Packages {
    fmt.Println(p.Name, p.Version, p.PURL())
}

Parse auto-detects the format. Detect returns just the type without a full parse and runs in roughly constant time regardless of document size, since it only token-scans top-level keys until it hits a discriminator.

switch sbom.Detect(data) {
case sbom.TypeCycloneDX:
case sbom.TypeSPDX:
}

Generating

Build an *sbom.SBOM, populate Document and Packages, then Encode:

s := sbom.New(sbom.TypeCycloneDX)
s.Document = sbom.Document{
    Name:      "my-app",
    Namespace: "https://example.com/my-app",
    Component: sbom.Component{Type: "application", Name: "my-app", Version: "1.2.3"},
    Creators:  []sbom.Creator{{Type: "Tool", Name: "my-tool-1.0"}},
}
s.AddPackage(sbom.Package{
    Name: "lodash", Version: "4.17.21", LicenseDeclared: "MIT",
    ExternalRefs: []sbom.ExternalRef{{Type: "purl", Locator: "pkg:npm/lodash@4.17.21"}},
})

sbom.Encode(os.Stdout, s, sbom.FormatCycloneDXJSON)
sbom.Encode(os.Stdout, s, sbom.FormatSPDXJSON)

Supported formats

Format Parse Encode Notes
CycloneDX JSON yes yes nested components flattened, license.id / license.name / expression all read
CycloneDX XML no yes
SPDX JSON yes yes {"sbom": ...} and in-toto {"predicate": ...} envelopes unwrapped on parse

XML/YAML/tag-value parsing is not handled yet.

Why not protobom?

protobom is the OpenSSF normaliser and covers every serialisation. It's pure Go (no cgo) but importing pkg/reader adds about 21 indirect modules including google.golang.org/protobuf, sigs.k8s.io/release-utils, logrus, tablewriter, and a handful of terminal-colour libraries that have no business in a parser. This module has zero dependencies and the field mappings were already worked out in the Ruby gem, so for tools that just need the package list it's a smaller surface to audit. If you need full spec coverage, use protobom.

Benchmarks

go test -bench . -benchmem

On an M1 Pro, Parse runs at roughly 100-165 MB/s; the 3 MB syft-generated nginx.spdx.json (151 packages) parses in about 18 ms. Detect is sub-microsecond on the same file.

License

MIT

Documentation

Overview

Package sbom parses Software Bill of Materials documents into a unified data model. It reads CycloneDX and SPDX (JSON serialisations) and normalises both into the same Document/Package/Relationship shape so callers don't need to care which format they were handed.

The model and field mappings are a port of github.com/andrew/sbom (Ruby).

doc, err := sbom.Parse(data)
for _, p := range doc.Packages {
    fmt.Println(p.Name, p.Version, p.PURL())
}

Index

Constants

View Source
const (
	SupplierOrganization = "Organization"
	SupplierPerson       = "Person"
)

Supplier/originator type values, shared across both formats.

Variables

View Source
var ErrUnrecognized = errors.New("sbom: unrecognized format")

ErrUnrecognized is returned when the input does not look like any supported SBOM format.

Functions

func Encode

func Encode(w io.Writer, s *SBOM, f Format) error

Encode writes s to w in the requested Format. Document fields left empty are filled with spec-mandated defaults (timestamps, NOASSERTION, etc.).

Types

type Checksum

type Checksum struct {
	Algorithm string
	Value     string
}

Checksum is a single hash over a package artefact.

type Component

type Component struct {
	Type    string
	Name    string
	Version string
}

Component is the root subject the SBOM describes (CycloneDX metadata.component / SPDX root package).

type Creator

type Creator struct {
	Type string
	Name string
}

Creator is a tool, person, or organization that produced the document.

type Document

type Document struct {
	Name        string
	ID          string
	Type        Type
	SpecVersion string
	DataLicense string
	Namespace   string
	Created     string
	Supplier    string
	Creators    []Creator
	Component   Component
}

Document holds metadata about the SBOM document itself, distinct from the packages it describes.

type ExternalRef

type ExternalRef struct {
	Category string
	Type     string
	Locator  string
}

ExternalRef is a typed pointer to an external resource. PURLs and CPEs are stored here in both formats.

type Format

type Format int

Format selects an output serialisation for Encode.

const (
	FormatCycloneDXJSON Format = iota
	FormatCycloneDXXML
	FormatSPDXJSON
)

type Package

type Package struct {
	ID               string
	Name             string
	Version          string
	Type             string
	Description      string
	Supplier         string
	SupplierType     string
	Originator       string
	OriginatorType   string
	Homepage         string
	DownloadLocation string
	Filename         string
	LicenseConcluded string
	LicenseDeclared  string
	Copyright        string
	Checksums        []Checksum
	ExternalRefs     []ExternalRef
	Properties       []Property
}

Package is a single component/package entry from the SBOM, regardless of source format.

func (*Package) CPE

func (p *Package) CPE() string

CPE returns the first CPE identifier for this package, if any.

func (*Package) PURL

func (p *Package) PURL() string

PURL returns the Package URL for this package, if one was declared. In CycloneDX this is the top-level purl field; in SPDX it lives in externalRefs with referenceType "purl". Both are normalised into ExternalRefs so this is a simple lookup.

type Property

type Property struct {
	Name  string
	Value string
}

Property is an arbitrary name/value annotation on a package.

type Relationship

type Relationship struct {
	SourceID string
	Source   string
	TargetID string
	Target   string
	Type     string
}

Relationship links two elements in the SBOM. SourceID/TargetID are the raw identifiers from the document; Source/Target are resolved names where the parser could determine them.

type SBOM

type SBOM struct {
	Type          Type
	SpecVersion   string
	Document      Document
	Packages      []Package
	Relationships []Relationship
	// contains filtered or unexported fields
}

SBOM is the unified parse result.

func New

func New(t Type) *SBOM

New returns an empty SBOM ready for AddPackage calls. Use this when building a document for Encode rather than parsing one.

func Parse

func Parse(data []byte) (*SBOM, error)

Parse sniffs the SBOM format from content and parses it. Only JSON serialisations are supported.

func (*SBOM) AddPackage

func (s *SBOM) AddPackage(p Package)

AddPackage appends p, replacing any existing package with the same (Name, Version) pair.

type Type

type Type string

Type identifies the source SBOM specification.

const (
	TypeUnknown   Type = ""
	TypeCycloneDX Type = "cyclonedx"
	TypeSPDX      Type = "spdx"
)

func Detect

func Detect(data []byte) Type

Detect inspects content and returns the SBOM Type without fully parsing it. Returns TypeUnknown if the format can't be determined.

Only the top-level object keys are scanned; nested arrays and objects are skipped without allocation so detection cost is independent of document size once a discriminator is found.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL