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
- func Generate(inv Inventory, format Format, opts ...Option) (string, error)
- type Component
- type ComponentScope
- type CryptoAlgorithm
- type DependencyEdge
- type DependencyRoot
- type FileEvidence
- type Format
- type GeoLocation
- type GraphStatus
- type Inventory
- type License
- type LicenseAcknowledgement
- type LineRange
- type Option
- type RawDocument
- type RawMetadata
- type Vulnerability
Constants ¶
const RawSchemaVersion = "2.1"
RawSchemaVersion is the version of the raw inventory document. Bump the major on a breaking shape change and the minor on an additive one, so a reader can tell which documents it has to handle differently from which merely carry more than it knows about.
2.1 added the `dependencies` roots, `local` on a component and the `transitive` match type.
Variables ¶
This section is empty.
Functions ¶
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
Identified bool `json:"identified,omitempty"` // a bom.identify rule claimed this component for at least one of its files (see FileEvidence.Identified for which)
Local bool `json:"local,omitempty"` // the project's own package (a workspace member, a path or link dependency), not a third-party one
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) DisplayName ¶ added in v0.4.0
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 ¶
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 DependencyEdge ¶ added in v0.12.0
DependencyEdge says that From depends on To.
type DependencyRoot ¶ added in v0.12.0
type DependencyRoot struct {
Manifest string `json:"manifest"` // slash path of the manifest, relative to the project
Lockfile string `json:"lockfile"` // slash path of the lockfile it was resolved from
Ecosystem string `json:"ecosystem"`
Status GraphStatus `json:"status"`
Reason string `json:"reason,omitempty"`
Detail string `json:"detail,omitempty"`
DevKnown bool `json:"dev_known"` // the manifests said which dependencies are dev; false means every member is kept as runtime
Direct []string `json:"direct"` // refs of the components the manifest declares, sorted
Edges []DependencyEdge `json:"edges"` // From depends on To; both ends are component refs
}
DependencyRoot is one manifest whose dependency graph was resolved from a lockfile, with dev members already left out. Direct lists the components the manifest declares as this root's lockfile resolved them; a manifest resolved by two lockfiles is two roots, each with its own versions, which evidence alone cannot tell apart since it names only the manifest. Edges run between component refs (see Component.Ref).
func (DependencyRoot) Ref ¶ added in v0.12.0
func (r DependencyRoot) Ref() string
Ref identifies the root in the SBOM formats. A manifest can be resolved by more than one lockfile (package-lock.json and yarn.lock side by side), so the lockfile is part of it.
type FileEvidence ¶
type FileEvidence struct {
Path string `json:"path"` // occurrence location: scanned file path, or the manifest path for a declared or transitive 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" (the manifest declares it) | "transitive" (the manifest's lockfile pulls it in)
Identified bool `json:"identified,omitempty"` // a bom.identify rule claimed this component for this file
MatchPercentage int `json:"match_percentage,omitempty"` // match confidence (snippet only)
Confidence string `json:"confidence,omitempty"` // attribution reliability grade: LOW, MEDIUM or HIGH
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)
// MatchIndex is where this component stood among the candidates the scan returned for this
// file, after the BOM rules: 0 is the file's primary match — the component the file
// originates from — and the ones after it redistribute it (forks, mirrors, packagings,
// projects that vendor it). Nil when there is no candidate list to stand in: declared
// evidence, or a document written before the field existed.
//
// It is the only place a file's candidate order survives. A component's evidence is sorted
// by path and the components are not in any file's order, so a consumer rebuilding "the
// candidates of this file" ranks them by this field, not by where it found them.
MatchIndex *int `json:"match_index,omitempty"`
}
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), the manifest that declared it (match_type "declared", with only the path set), or the manifest whose lockfile pulls it in through another dependency (match_type "transitive", path set the same way).
func (FileEvidence) IsPrimary ¶ added in v0.10.0
func (e FileEvidence) IsPrimary() bool
IsPrimary reports whether this evidence is its file's primary match (MatchIndex 0).
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 GraphStatus ¶ added in v0.12.0
type GraphStatus string
GraphStatus says how much of a root's dependency graph the lockfile gave.
const ( // GraphComplete means the root's edges are the lockfile's. GraphComplete GraphStatus = "complete" // GraphPartial means the graph is usable but missing something; Reason says what. GraphPartial GraphStatus = "partial" // GraphUnknown means the lockfile could not be read; the root has no edges and no members. GraphUnknown GraphStatus = "unknown" )
type Inventory ¶
type Inventory struct {
Components []Component `json:"components"`
Vulnerabilities []Vulnerability `json:"vulnerabilities,omitempty"`
Dependencies []DependencyRoot `json:"dependencies,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 and depsource adapters from a scan result or a project's dependency files. Add components through Add to keep one entry per component when more than one origin reports the same one.
Dependencies is the dependency graph the lockfiles resolved, one root per manifest, with dev members already left out. Which components a root reaches, and whether directly, is on the components' evidence (see FileEvidence.MatchType); the root carries its edges.
func ParseCycloneDX ¶
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).
The dependency graph is not read back: the dependencies and compositions sections are skipped, and so are the application components without a purl that stand for a lockfile root (ours, and the ones other SBOM tools write), which are not packages. Like match_index, the graph survives a round trip through the raw format only.
func ParseRaw ¶
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 ¶
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).
The dependency graph is not read back: the project and root packages the writer adds for it are skipped, by their comment, and the DEPENDS_ON relationships are ignored. Like match_index, the graph survives a round trip through the raw format only.
func (*Inventory) Add ¶ added in v0.8.0
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.
func (Inventory) DirectMembers ¶ added in v0.12.0
func (inv Inventory) DirectMembers(root DependencyRoot) []string
DirectMembers returns the refs of the components root declares, in the root's order. A ref that is not a component is dropped, as dependsOn drops such an edge end.
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 ¶
WithAuthor sets the author / organization recorded in the document metadata. Empty values are ignored (default: the SCANOSS organization name).
func WithProjectName ¶
WithProjectName sets the name of the top-level project component (CycloneDX) / document (SPDX). Empty values are ignored and the default is kept.
func WithTimestamp ¶
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 ¶
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
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.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package depsource builds an sbom.Inventory from a project's dependency files.
|
Package depsource builds an sbom.Inventory from a project's dependency files. |
|
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. |