Documentation
¶
Overview ¶
Package normalize provides utilities for normalizing package identifiers. This file handles mapping binary package names to their source package names for Linux distributions (Debian, Alpine), enabling vulnerability matching against security advisories that reference source packages.
Index ¶
- Constants
- Variables
- func ArtifactPurl(scanner string, assetName string) string
- func AssetName(assetName string) (string, error)
- func AssetSlugPath(assetName string) (string, error)
- func BomIsSBOM(bom *cdx.BOM) bool
- func CheckVersion(exactVersion, introduced, fixed *string, ...) (bool, error)
- func ConvertToSemver(originalVersion string) (string, error)
- func CycloneDXVEXFromVulnerabilities(vulns []cdx.Vulnerability, metadata BOMMetadata) *cdx.BOM
- func EncodeCanonical(obj any) (out []byte, err error)
- func FixFixedVersion(purl string, fixedVersion *string) *string
- func MergeCycloneDXVEX(boms []*cdx.BOM, rootName string) *cdx.BOM
- func PURLToString(purl packageurl.PackageURL) (string, error)
- func ParseGraphNodeID(id string) (prefix, name string)
- func Purlify(artifactName string, assetVersionName string) string
- func SanitizeExternalReferencesURL(url string) string
- func SemverCompare(v1, v2 string) int
- func SemverSort(versions []string)
- func SortStringsSlice(slice []string) []string
- func ToPurlWithoutVersion(purl packageurl.PackageURL) string
- func UppercaseCVEID(cveID string) string
- type BOMMetadata
- type FlatGraph
- type GraphComponent
- type GraphDiff
- type GraphNode
- type GraphNodeType
- type InfoSourceType
- type Path
- type PurlMatchContext
- type SBOMGraph
- func InvalidSBOMGraphFromCycloneDX(bom *cdx.BOM, artifactName, infoSourceID string) (*SBOMGraph, error)
- func NewSBOMGraph() *SBOMGraph
- func SBOMGraphFromComponents[T GraphComponent](components []T, licenseOverwrites map[string]string) (*SBOMGraph, error)
- func SBOMGraphFromCycloneDX(bom *cdx.BOM, artifactName, infoSourceID string) (*SBOMGraph, error)
- func (g *SBOMGraph) AddArtifact(name string) string
- func (g *SBOMGraph) AddComponent(comp cdx.Component) string
- func (g *SBOMGraph) AddEdge(parentID, childID string)
- func (g *SBOMGraph) AddInfoSource(artifactID, sourceID string, sourceType InfoSourceType) string
- func (g *SBOMGraph) Artifacts() iter.Seq[*GraphNode]
- func (g *SBOMGraph) Children(nodeID string) iter.Seq[*GraphNode]
- func (g *SBOMGraph) ChildrenOfType(nodeID string, nodeType GraphNodeType) iter.Seq[*GraphNode]
- func (g *SBOMGraph) ClearScope()
- func (g *SBOMGraph) Clone() *SBOMGraph
- func (g *SBOMGraph) ComponentEdges() iter.Seq2[string, string]
- func (g *SBOMGraph) Components() iter.Seq[*GraphNode]
- func (g *SBOMGraph) ComponentsWithMultipleSources() []string
- func (g *SBOMGraph) CountInfoSourcesPerComponent() map[string]map[InfoSourceType]int
- func (g *SBOMGraph) CurrentScopeID() string
- func (g *SBOMGraph) DeleteArtifactFromGraph(artifactName string) GraphDiff
- func (g *SBOMGraph) EdgesIter() iter.Seq2[string, string]
- func (g *SBOMGraph) EnrichSBOM(extra *cdx.BOM, parentRef string) (isNew bool, err error)
- func (g *SBOMGraph) FindAllComponentOnlyPathsToPURL(purl string, limit int) []Path
- func (g *SBOMGraph) GetArtifactIDs() []string
- func (g *SBOMGraph) GetInfoSourceIDs(artifactID string) []string
- func (g *SBOMGraph) GetInfoSourceNode(infoSourceID string) *GraphNode
- func (g *SBOMGraph) GetParentIDs(nodeID string) []string
- func (g *SBOMGraph) GetRootID() string
- func (g *SBOMGraph) HasInfoSource(infoSourceID string) bool
- func (g *SBOMGraph) InfoSources() iter.Seq[*GraphNode]
- func (g *SBOMGraph) IsReachable(id string) bool
- func (g *SBOMGraph) IsScoped() bool
- func (g *SBOMGraph) LicenseDistribution() map[string]int
- func (g *SBOMGraph) MakeValid()
- func (g *SBOMGraph) MergeGraph(other *SBOMGraph) GraphDiff
- func (g *SBOMGraph) MinimalTreeToPURL(purl string, maxDepth int) minimalTree
- func (g *SBOMGraph) Node(id string) *GraphNode
- func (g *SBOMGraph) NodeIDsAndEdges() FlatGraph
- func (g *SBOMGraph) NodesOfType(nodeType GraphNodeType) iter.Seq[*GraphNode]
- func (g *SBOMGraph) Scope(id string) error
- func (g *SBOMGraph) ScopeToArtifact(artifactName string) error
- func (g *SBOMGraph) ScopeToInfoSource(infoSource string, t InfoSourceType) error
- func (g *SBOMGraph) ToCycloneDX(metadata BOMMetadata) *cdx.BOM
- func (g *SBOMGraph) ToMinimalTree() minimalTree
- type VersionInterpretationType
Constants ¶
const GraphRootNodeID = "ROOT"
Variables ¶
var ErrNodeNotReachable = fmt.Errorf("node not reachable from current scope")
var PURLEcosystems = map[string]string{
"Alpine": "apk",
"crates.io": "cargo",
"Debian": "deb",
"Go": "golang",
"Hackage": "hackage",
"Hex": "hex",
"Maven": "maven",
"npm": "npm",
"NuGet": "nuget",
"OSS-Fuzz": "generic",
"Packagist": "composer",
"Pub": "pub",
"PyPI": "pypi",
"RubyGems": "gem",
}
PURL conversion utilities
var ValidSemverRegex = regexp.MustCompile(`^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)
Regex for validating a correct semver.
Functions ¶
func ArtifactPurl ¶
func AssetSlugPath ¶ added in v1.9.0
AssetSlugPath expands a normalized "<organization>/<project>/<asset>" name into the web UI path "<organization>/projects/<project>/assets/<asset>".
func CheckVersion ¶
func ConvertToSemver ¶
ConvertToSemver converts various version formats to semantic versioning format. It handles: - Epoch prefixes (e.g., "2:1.2.3" -> "1.2.3") - "v" prefixes (e.g., "v1.2.3" -> "1.2.3") - Pre-release identifiers with "-" (e.g., "1.2.3-rc1") - Build metadata with "+" (e.g., "1.2.3+build1") - Tilde versions "~" (e.g., "1.2.3~rc1" -> "1.2.3-rc1") - Missing version segments (e.g., "1.2" -> "1.2.0")
Returns an error if: - Version contains invalid characters (only 0-9 and . allowed in version part) - Version has more than 3 numeric segments
func CycloneDXVEXFromVulnerabilities ¶ added in v1.9.0
func CycloneDXVEXFromVulnerabilities(vulns []cdx.Vulnerability, metadata BOMMetadata) *cdx.BOM
CycloneDXVEXFromVulnerabilities builds a CycloneDX VEX BOM directly from a set of vulnerabilities, without going through an SBOMGraph. The affected components (from each vulnerability's Affects refs) become the BOM's components, all declared as direct dependencies of the root component described by metadata.
func EncodeCanonical ¶
EncodeCanonical JSON canonicalizes the passed object and returns it as a byte slice. It uses the OLPC canonical JSON specification (see http://wiki.laptop.org/go/Canonical_JSON). If canonicalization fails the byte slice is nil and the second return value contains the error.
func FixFixedVersion ¶
func MergeCycloneDXVEX ¶ added in v1.9.0
MergeCycloneDXVEX merges several CycloneDX VEX BOMs into one under a single release root. Because the components and dependencies are derived from the vulnerabilities' affected refs, merging is simply rebuilding the BOM from the union of all vulnerabilities.
func PURLToString ¶
func PURLToString(purl packageurl.PackageURL) (string, error)
func ParseGraphNodeID ¶
ParseGraphNodeID extracts the type prefix and name from a node ID. e.g., "artifact:my-app" -> ("artifact", "my-app")
func SemverCompare ¶
func SemverSort ¶
func SemverSort(versions []string)
func SortStringsSlice ¶
func ToPurlWithoutVersion ¶
func ToPurlWithoutVersion(purl packageurl.PackageURL) string
func UppercaseCVEID ¶
Types ¶
type BOMMetadata ¶
type GraphComponent ¶
type GraphComponent interface {
GetID() string
GetDependentID() string
ToCdxComponent(componentLicenseOverwrites map[string]string) (cdx.Component, error)
}
GraphComponent represents a component that can be loaded from the database.
type GraphDiff ¶
type GraphDiff struct {
AddedNodes []*GraphNode // nodes added
RemovedNodes []*GraphNode // nodes removed
AddedEdges [][2]string // edges added [parent, child] - ROOT will be replaced by nil so that it matches the db schema
RemovedEdges [][2]string // edges removed [parent, child] - ROOT will be replaced by nil so that it matches the db schema
}
GraphDiff represents differences between two graph states.
func (GraphDiff) AddedNodeIDs ¶
AddedNodeIDs returns just the IDs of added nodes.
func (GraphDiff) RemovedNodeIDs ¶
RemovedNodeIDs returns just the IDs of removed nodes.
type GraphNode ¶
type GraphNode struct {
BOMRef string // Unique identifier
Type GraphNodeType // What kind of node this is
// For components, this holds the full CycloneDX component data
Component *cdx.Component
// InfoSource-specific fields (only set for GraphNodeTypeInfoSource)
InfoType InfoSourceType
}
GraphNode represents any node in the SBOM graph. All nodes (root, artifacts, info sources, components) share this structure.
type GraphNodeType ¶
type GraphNodeType string
GraphNodeType identifies what kind of node this is in the graph.
const ( GraphNodeTypeRoot GraphNodeType = "root" GraphNodeTypeArtifact GraphNodeType = "artifact" GraphNodeTypeInfoSource GraphNodeType = "infosource" GraphNodeTypeComponent GraphNodeType = "component" )
type InfoSourceType ¶
type InfoSourceType string
InfoSourceType indicates where component information came from.
const (
InfoSourceSBOM InfoSourceType = "sbom"
)
func RemoveInformationSourcePrefixIfExists ¶
func RemoveInformationSourcePrefixIfExists(origin string) (InfoSourceType, string)
type Path ¶
type Path []string
Path represents a vulnerability path through the dependency graph. It can contain both structural nodes (root, artifact, info sources) and component nodes (PURLs).
func (Path) ToStringSlice ¶
ToStringSlice returns all nodes in the path, including fake/structural nodes.
type PurlMatchContext ¶
type PurlMatchContext struct {
SearchPurl string `json:"searchPurl"` // purl without version and qualifiers, used for database matching
NormalizedVersion string `json:"normalizedVersion"`
HowToInterpretVersionString VersionInterpretationType `json:"howToInterpretVersionString"`
Qualifiers packageurl.Qualifiers `json:"qualifiers"`
Namespace string `json:"namespace"`
}
PurlMatchContext holds the parsed purl information for matching
func ParsePurlForMatching ¶
func ParsePurlForMatching(purl packageurl.PackageURL) *PurlMatchContext
ParsePurlForMatching parses a purl and version into a context for database matching
type SBOMGraph ¶
type SBOMGraph struct {
Nodes map[string]*GraphNode // id -> node
Edges map[string]map[string]struct{} // parent id -> set of child ids
RootID string // ID of the root node (constant: "ROOT")
ScopeID string // The id of the current scope node
}
SBOMGraph is a directed graph representing a software bill of materials.
Structure:
ROOT
└── artifact:my-app
└── sbom:package-lock.json
└── pkg:npm/lodash@4.17.21
└── pkg:npm/some-dep@1.0.0
All nodes live in the same graph. Scoping is done by traversing from a specific node.
func InvalidSBOMGraphFromCycloneDX ¶ added in v1.9.0
func InvalidSBOMGraphFromCycloneDX(bom *cdx.BOM, artifactName, infoSourceID string) (*SBOMGraph, error)
InvalidSBOMGraphFromCycloneDX is like SBOMGraphFromCycloneDX but skips pruning unidentifiable components (e.g. a purl-less root). Call MakeValid once done enriching, or use SBOMGraphFromCycloneDX directly if you don't need to enrich the graph via EnrichSBOM first.
func NewSBOMGraph ¶
func NewSBOMGraph() *SBOMGraph
NewSBOMGraph creates an empty graph with a root node.
func SBOMGraphFromComponents ¶
func SBOMGraphFromComponents[T GraphComponent](components []T, licenseOverwrites map[string]string) (*SBOMGraph, error)
SBOMGraphFromComponents builds an SBOMGraph from database components. The components include artifact nodes, information source nodes, and regular components. This function reconstructs the full graph structure from the flat component list. Uses generics to avoid slice type conversion and reduce memory allocations.
func SBOMGraphFromCycloneDX ¶
SBOMGraphFromCycloneDX creates an SBOMGraph from a CycloneDX BOM. The original root component is always kept as a node in the graph when it has a valid PackageURL, so that re-uploading a downloaded SBOM is idempotent. When the root component has no (valid) PackageURL, it cannot be identified as a real package, so its direct dependencies are reparented to the info source node instead.
func (*SBOMGraph) AddArtifact ¶
AddArtifact adds an artifact node as a child of root.
func (*SBOMGraph) AddComponent ¶
AddComponent adds a component node.
func (*SBOMGraph) AddInfoSource ¶
func (g *SBOMGraph) AddInfoSource(artifactID, sourceID string, sourceType InfoSourceType) string
AddInfoSource adds an information source node as a child of an artifact. The info source ID is unique per artifact to prevent conflicts when merging graphs.
func (*SBOMGraph) ChildrenOfType ¶
ChildrenOfType returns children of a specific type.
func (*SBOMGraph) ClearScope ¶
func (g *SBOMGraph) ClearScope()
func (*SBOMGraph) ComponentEdges ¶
ComponentEdges returns edges between components only.
func (*SBOMGraph) Components ¶
Components returns all component nodes reachable from scope.
func (*SBOMGraph) ComponentsWithMultipleSources ¶
ComponentsWithMultipleSources returns component IDs that appear in multiple SBOMs. These cannot be automatically marked as "fixed".
func (*SBOMGraph) CountInfoSourcesPerComponent ¶
func (g *SBOMGraph) CountInfoSourcesPerComponent() map[string]map[InfoSourceType]int
CountInfoSourcesPerComponent returns how many info sources each component appears under. Key insight: traverse from each info source and count how many times each component is reached.
func (*SBOMGraph) CurrentScopeID ¶
func (*SBOMGraph) DeleteArtifactFromGraph ¶
DeleteArtifactFromGraph removes an artifact and all its subtree from the graph. Returns a GraphDiff representing what was removed. DeleteArtifactFromGraph removes an artifact and all its subtree from the graph. Returns a GraphDiff representing what was removed. Only deletes nodes that are exclusively reachable through this artifact.
func (*SBOMGraph) EdgesIter ¶ added in v1.3.0
EdgesIter returns all edges where both endpoints are reachable from scope.
func (*SBOMGraph) EnrichSBOM ¶ added in v1.9.0
EnrichSBOM adds or replaces extra's root component (and everything it declares) in g. If g already has a component with the same Name as extra's root (e.g. an unresolved binary a scan found but couldn't inspect further), that component's identity (BOMRef) is kept, but its whole subtree is replaced with extra's - down to no children at all, if that's what extra declares - via MergeGraph's existing "last source wins" replace semantics (including its automatic pruning of anything left unreachable). Otherwise extra's root is attached as a new child of parentRef, additively - parentRef keeps whatever other children it already had.
The returned bool reports which of those two happened: true if extra's root was attached as a brand new node, false if an existing component's subtree was replaced in place.
func (*SBOMGraph) FindAllComponentOnlyPathsToPURL ¶
func (*SBOMGraph) GetArtifactIDs ¶
GetArtifactIDs returns all artifact IDs in the graph.
func (*SBOMGraph) GetInfoSourceIDs ¶
GetInfoSourceIDs returns all info source IDs for a given artifact.
func (*SBOMGraph) GetInfoSourceNode ¶
GetInfoSourceNode returns the info source node by ID, or nil if not found.
func (*SBOMGraph) GetParentIDs ¶
GetParentIDs returns all parent node IDs for a given node.
func (*SBOMGraph) HasInfoSource ¶
HasInfoSource checks if an info source exists in the graph.
func (*SBOMGraph) InfoSources ¶
InfoSources returns all information source nodes reachable from scope.
func (*SBOMGraph) IsReachable ¶ added in v1.3.0
func (*SBOMGraph) LicenseDistribution ¶
func (*SBOMGraph) MakeValid ¶ added in v1.9.0
func (g *SBOMGraph) MakeValid()
MakeValid prunes unidentifiable components (see pruneUnidentifiablePackages). Call this once after building a graph via InvalidSBOMGraphFromCycloneDX and doing any enrichment (EnrichSBOM) on it.
func (*SBOMGraph) MergeGraph ¶
MergeGraph merges another graph into this one, returning the diff. Artifacts from the other graph are added/merged under this graph's root. When an info source with the same ID exists, it replaces the entire subtree.
func (*SBOMGraph) MinimalTreeToPURL ¶
MinimalTreeToPURL returns a minimal tree structure containing only the subgraph of nodes that lead to the specified PURL. This collects all ancestor nodes without enumerating individual paths, avoiding combinatorial explosion. The maxDepth parameter limits how far back we traverse (0 = unlimited).
func (*SBOMGraph) NodeIDsAndEdges ¶
NodeIDsAndEdges returns all node IDs and edges reachable from the current scope.
func (*SBOMGraph) NodesOfType ¶
func (g *SBOMGraph) NodesOfType(nodeType GraphNodeType) iter.Seq[*GraphNode]
NodesOfType returns all reachable nodes of a specific type.
func (*SBOMGraph) ScopeToArtifact ¶
func (*SBOMGraph) ScopeToInfoSource ¶
func (g *SBOMGraph) ScopeToInfoSource(infoSource string, t InfoSourceType) error
func (*SBOMGraph) ToCycloneDX ¶
func (g *SBOMGraph) ToCycloneDX(metadata BOMMetadata) *cdx.BOM
ToCycloneDX exports the scoped view as a CycloneDX BOM.
func (*SBOMGraph) ToMinimalTree ¶
func (g *SBOMGraph) ToMinimalTree() minimalTree
type VersionInterpretationType ¶
type VersionInterpretationType string
const ( ExactVersionString VersionInterpretationType = "exact" SemanticVersionString VersionInterpretationType = "semver_range" EmptyVersion VersionInterpretationType = "empty_version" EcosystemSpecificVersion VersionInterpretationType = "ecosystem_specific" )