sbom

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package sbom generates SBOM documents (CycloneDX, SPDX Lite) from a neutral inventory of components. It depends on the CycloneDX and SPDX libraries — never on the scan SDK — so it can be reused by the CLI and by external consumers alike. Adapters that build an Inventory from a scan result live in the scansource subpackage.

Index

Constants

View Source
const RawSchemaVersion = "2.0"

RawSchemaVersion is the version of the raw inventory document. Bump on a breaking shape change.

Variables

This section is empty.

Functions

func Generate

func Generate(inv Inventory, format Format, opts ...Option) (string, error)

Generate renders the inventory to the requested SBOM format. It is the single entry point: an unknown format returns an error; an empty inventory yields a valid, empty document.

Types

type Component

type Component struct {
	Purl             string            `json:"purl"`                        // canonical PURL (identity), e.g. "pkg:github/scanoss/engine"
	Scope            ComponentScope    `json:"scope,omitempty"`             // detected (from the scan) | declared (from a manifest); "" == detected
	AliasPurls       []string          `json:"alias_purls,omitempty"`       // additional PURLs identifying the same component (beyond Purl)
	Vendor           string            `json:"vendor,omitempty"`            // supplier / namespace
	Name             string            `json:"name,omitempty"`              // component name
	Version          string            `json:"version,omitempty"`           // resolved version; "" renders as "NOASSERTION"
	URL              string            `json:"url,omitempty"`               // homepage / source URL ("" => no externalReference)
	URLHash          string            `json:"url_hash,omitempty"`          // SCANOSS url_hash (SPDX package checksum)
	Rank             int               `json:"rank,omitempty"`              // engine match ordering; lower is a stronger match
	ReleaseDate      string            `json:"release_date,omitempty"`      // release date of Version (YYYY-MM-DD)
	ArtifactName     string            `json:"artifact_name,omitempty"`     // release artifact holding this version, e.g. "v0.38.0.zip"
	Licenses         []License         `json:"licenses,omitempty"`          // declared and/or concluded licenses (licenses layer)
	Cryptography     []CryptoAlgorithm `json:"cryptography,omitempty"`      // cryptographic algorithms detected (crypto layer)
	Geoprovenance    []GeoLocation     `json:"geoprovenance,omitempty"`     // contributor geographic origin (geo layer)
	DownloadLocation string            `json:"download_location,omitempty"` // download location (defaults to URL)
	Evidence         []FileEvidence    `json:"evidence,omitempty"`          // where the component came from: scanned files that matched, or the manifest that declared it
}

Component is one component in the inventory: identity, scope, scan evidence, and the per-component enrichment layers attached inline. Vulnerabilities are not stored here — they are the Inventory's flat top-level list, joined back by PURL.

func (Component) AllPurls

func (c Component) AllPurls() []string

AllPurls returns the canonical Purl followed by any AliasPurls.

func (Component) DisplayName added in v0.4.0

func (c Component) DisplayName() string

DisplayName is the component's name for an SBOM package entry.

Detected components carry Name from the scan result; declared ones (sourced from a manifest) carry only a PURL, so the name is taken from its last segment — "pkg:golang/github.com/spf13/cobra" yields "cobra". Falling back to the whole PURL would put "pkg:golang/github.com/spf13/cobra" in a field that consumers render as a package name.

func (Component) IsDeclared

func (c Component) IsDeclared() bool

IsDeclared reports whether the component is a declared dependency rather than a scan-detected match. The zero-value scope counts as detected.

type ComponentScope

type ComponentScope string

ComponentScope records how a component entered the inventory.

const (
	// ScopeDetected marks a component found by the scan (an OSS match). The zero value is
	// treated as detected.
	ScopeDetected ComponentScope = "detected"
	// ScopeDeclared marks a component declared in a dependency manifest.
	ScopeDeclared ComponentScope = "declared"
)

type CryptoAlgorithm

type CryptoAlgorithm struct {
	Algorithm string `json:"algorithm"`
	Strength  string `json:"strength,omitempty"`
}

CryptoAlgorithm is a cryptographic algorithm detected in a component (crypto layer).

type FileEvidence

type FileEvidence struct {
	Path            string      `json:"path"`                        // occurrence location: scanned file path, or the manifest path for a declared dependency
	SourceHash      string      `json:"source_hash,omitempty"`       // hash of the scanned input file (from the WFP)
	FileHash        string      `json:"file_hash,omitempty"`         // hash of the matched file (== source_hash for a file match; the OSS file's for a snippet)
	MatchType       string      `json:"match_type,omitempty"`        // "file" (whole file) | "snippet" | "declared" (from a manifest)
	MatchPercentage int         `json:"match_percentage,omitempty"`  // match confidence (snippet only)
	OssFilePath     string      `json:"oss_file_path,omitempty"`     // matched file path inside the OSS component
	InputLineRanges []LineRange `json:"input_line_ranges,omitempty"` // matched line ranges in the scanned file (snippet only)
	OssLineRanges   []LineRange `json:"oss_line_ranges,omitempty"`   // matched line ranges in the OSS component (snippet only)
}

FileEvidence is one occurrence of a component in the scanned project (a CycloneDX evidence.occurrence): a scanned file that matched (match_type "file"/"snippet", with — for snippets — where and how strongly it matched inside the OSS component), or the manifest that declared it (match_type "declared", with only the path set).

type Format

type Format string

Format is a supported SBOM output format.

const (
	// FormatCycloneDX selects CycloneDX JSON output.
	FormatCycloneDX Format = "cyclonedx"
	// FormatSPDX selects SPDX 2.3 (Lite field subset) JSON output.
	FormatSPDX Format = "spdx"
)

type GeoLocation

type GeoLocation struct {
	Name       string  `json:"name"`
	Percentage float64 `json:"percentage,omitempty"`
}

GeoLocation is one geographic origin of a component's contributors (geo layer), with the share of contribution when known.

type Inventory

type Inventory struct {
	Components      []Component     `json:"components"`
	Vulnerabilities []Vulnerability `json:"vulnerabilities,omitempty"`
}

Inventory is the neutral, format-agnostic bill of materials and the core of the CLI's raw output. Detected components (scan matches) and declared dependency components live in ONE Components list, tagged by Component.Scope, so enrichment decorates the union origin-agnostic. Per-component layers (licenses, cryptography, geoprovenance) attach inline on each component; vulnerabilities are a flat top-level list joined to components by base PURL. Build it directly, or via the scansource adapters from a scan result. Add components through Add to keep one entry per component when more than one origin reports the same one.

func ParseCycloneDX

func ParseCycloneDX(data []byte) (Inventory, error)

ParseCycloneDX decodes a CycloneDX JSON document (any 1.x minor version the library accepts) into a neutral Inventory. It is the inverse of buildCycloneDX: components, licenses and vulnerabilities are mapped back. Fields outside the Inventory model — file evidence occurrences, hashes, component type — are not preserved (best-effort).

func ParseRaw

func ParseRaw(data []byte) (Inventory, error)

ParseRaw reads a raw inventory document back into an Inventory. Any envelope fields (schema_version, metadata) are accepted and ignored; a bare `{components, vulnerabilities}` object parses too. To be recognized as an inventory the input must carry a `schema_version` or a `components` key — arbitrary JSON that has neither is rejected. A v3 scan result (whose `components` are an object keyed by url_hash, not an array) also fails and returns an error.

func ParseSPDX

func ParseSPDX(data []byte) (Inventory, error)

ParseSPDX decodes an SPDX 2.3 JSON document into a neutral Inventory. It is the inverse of buildSPDXLite: packages, licenses and checksums are mapped back. SPDX 2.3 has no vulnerability model, so the Inventory carries no vulnerabilities. Fields outside the Inventory model are not preserved (best-effort).

func (*Inventory) Add added in v0.8.0

func (inv *Inventory) Add(comps ...Component)

Add appends comps, folding any that shares an identity (Purl and Version) with a component already present rather than listing it twice: the evidence lists are combined, and a detected component wins over a declared one — scope and metadata both. A component both matched by a scan and declared in a manifest is one component, detected, carrying its file matches and its manifest occurrence together. An empty Scope counts as detected, as the field documents.

The inventory takes its own copy of each component's evidence, so what it holds is not the caller's to change afterwards — and two inventories seeded from one Component value cannot grow into each other's memory.

It is how the inventory keeps one entry per component. Appending to Components directly still works, and leaves the caller to answer what two entries for one component mean.

type License

type License struct {
	ID              string                 `json:"id"`                        // SPDX id or "LicenseRef-*", e.g. "GPL-2.0-only"
	Acknowledgement LicenseAcknowledgement `json:"acknowledgement,omitempty"` // declared (default) | concluded
}

License is a single license on a component, with its acknowledgement. The same id may appear more than once (e.g. both declared and concluded).

type LicenseAcknowledgement

type LicenseAcknowledgement string

LicenseAcknowledgement is how a license was established for a component.

const (
	// AckDeclared marks a license stated by the project (from the decoration service).
	AckDeclared LicenseAcknowledgement = "declared"
	// AckConcluded marks a license determined by review (e.g. from a downstream consumer's identifications).
	AckConcluded LicenseAcknowledgement = "concluded"
)

type LineRange added in v0.3.0

type LineRange struct {
	StartLine int `json:"start_line"` // first line of the matched range
	EndLine   int `json:"end_line"`   // last line of the matched range
}

LineRange is a matched line range (inclusive) within a file. It mirrors the shape the scan engine reports, so ranges travel from the scan result to the SBOM writers without being encoded to text and parsed back.

type Option

type Option func(*options)

Option configures SBOM generation.

func WithAuthor

func WithAuthor(name string) Option

WithAuthor sets the author / organization recorded in the document metadata. Empty values are ignored (default: the SCANOSS organization name).

func WithProjectName

func WithProjectName(name string) Option

WithProjectName sets the name of the top-level project component (CycloneDX) / document (SPDX). Empty values are ignored and the default is kept.

func WithTimestamp

func WithTimestamp(t time.Time) Option

WithTimestamp sets the document creation timestamp (e.g. a point-in-time snapshot time, or a fixed value for reproducible output). The zero time is ignored and the current time is used at render.

func WithTool

func WithTool(name string) Option

WithTool sets the generating tool recorded in the document metadata. The value is taken literally: a caller passing "my-tool-1.4.0" gets exactly that, with no version appended. Pass WithToolVersion as well to have CycloneDX record the version in its own field.

func WithToolVersion added in v0.4.0

func WithToolVersion(version string) Option

WithToolVersion sets the version recorded alongside the tool name. Empty leaves the default. CycloneDX records it in its own field; SPDX has no such field, so there the two are joined as "name-version", which is that format's convention.

type RawDocument

type RawDocument struct {
	SchemaVersion string      `json:"schema_version"`
	Metadata      RawMetadata `json:"metadata"`
	Inventory
}

RawDocument is the raw output format: an Inventory wrapped in a versioned envelope. The embedded Inventory promotes its `components`/`vulnerabilities` keys to the top level, so the JSON is `{schema_version, metadata, components, vulnerabilities}` — the neutral interchange contract for the scan → enrich → convert pipe. It is the raw counterpart to the CycloneDX/SPDX documents that Generate produces, kept here so a single definition serves both the writer and the reader.

func NewRawDocument

func NewRawDocument(inv Inventory, meta RawMetadata) RawDocument

NewRawDocument wraps inv in a raw envelope stamped with the current schema version and the given metadata.

func (RawDocument) Marshal

func (d RawDocument) Marshal() (string, error)

Marshal renders the raw document as indented JSON — the raw-format counterpart to Generate.

type RawMetadata

type RawMetadata struct {
	Tool        string `json:"tool,omitempty"`
	ToolVersion string `json:"tool_version,omitempty"`
	Project     string `json:"project,omitempty"`
}

RawMetadata identifies the tool and project that produced a raw document. Values are supplied by the caller (the CLI), so pkg/sbom carries no application identity of its own.

type Vulnerability

type Vulnerability struct {
	ID       string   `json:"id"`                 // advisory/CVE id, e.g. "CVE-2021-1234"
	Severity string   `json:"severity,omitempty"` // critical|high|medium|low|none|"" (case-insensitive)
	Source   string   `json:"source,omitempty"`   // advisory source name, e.g. "NVD"
	URL      string   `json:"url,omitempty"`      // advisory URL (optional)
	Summary  string   `json:"summary,omitempty"`  // short description (optional)
	Purls    []string `json:"purls,omitempty"`    // PURLs of the affected components, versioned when the source says which version

	// Optional quantitative scoring. All fields are optional: when unset they are not
	// rendered, and the output is identical to a severity-only vulnerability.
	CVSSScore  *float64 `json:"cvss_score,omitempty"`  // CVSS base score 0.0–10.0
	CVSSVector string   `json:"cvss_vector,omitempty"` // CVSS vector string, e.g. "CVSS:3.1/AV:N/..."
	CVSSMethod string   `json:"cvss_method,omitempty"` // CVSS scoring method, e.g. "CVSSv31"
	CWEs       []int    `json:"cwes,omitempty"`        // CWE ids, e.g. [77]
	EPSSScore  *float64 `json:"epss_score,omitempty"`  // EPSS probability 0.0–1.0 (no native CycloneDX field; emitted as a property)
}

Vulnerability is one known vulnerability affecting one or more components, in a format independent of any decoration API wire type.

Directories

Path Synopsis
Package scansource adapts SCANOSS SDK values — a v3 scan result and the licenses, vulnerabilities, cryptography and geoprovenance decoration responses — into the neutral sbom.Inventory consumed by the sbom package.
Package scansource adapts SCANOSS SDK values — a v3 scan result and the licenses, vulnerabilities, cryptography and geoprovenance decoration responses — into the neutral sbom.Inventory consumed by the sbom package.

Jump to

Keyboard shortcuts

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