normalize

package
v1.0.0-rc.18 Latest Latest
Warning

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

Go to latest
Published: Feb 17, 2026 License: AGPL-3.0 Imports: 25 Imported by: 0

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

View Source
const GraphRootNodeID = "ROOT"

Variables

View Source
var ErrNodeNotReachable = fmt.Errorf("node not reachable from current scope")
View Source
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

View Source
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 ArtifactPurl(scanner string, assetName string) string

func BeautifyPURL

func BeautifyPURL(pURL string) (string, error)

function to make purl look more visually appealing

func BomIsSBOM

func BomIsSBOM(bom *cdx.BOM) bool

func CheckVersion

func CheckVersion(exactVersion, introduced, fixed *string, lookingForVersion, affectedComponentType string) (bool, error)

func ConvertToSemver

func ConvertToSemver(originalVersion string) (string, error)

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 DeepSort

func DeepSort(el any) any

this is a deep sort function that sorts all maps and slices recursively it is REALLY expensive, so use it wisely! it treats any arrays as sets and sorts them by their canonical JSON representation

func EncodeCanonical

func EncodeCanonical(obj interface{}) (out []byte, err error)

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 FixFixedVersion(purl string, fixedVersion *string) *string

func PURLToString

func PURLToString(purl packageurl.PackageURL) (string, error)

func ParseGraphNodeID

func ParseGraphNodeID(id string) (prefix, name string)

ParseGraphNodeID extracts the type prefix and name from a node ID. e.g., "artifact:my-app" -> ("artifact", "my-app")

func Purlify

func Purlify(artifactName string, assetVersionName string) string

func QualifiersMapToString

func QualifiersMapToString(qualifiers map[string]string) string

func SanitizeExternalReferencesURL

func SanitizeExternalReferencesURL(url string) string

func SemverCompare

func SemverCompare(v1, v2 string) int

func SemverSort

func SemverSort(versions []string)

func SortStringsSlice

func SortStringsSlice(slice []string) []string

func StructuralCompareCdxBoms

func StructuralCompareCdxBoms(a, b *cdx.BOM) error

func ToPurlWithoutVersion

func ToPurlWithoutVersion(purl packageurl.PackageURL) string

func UppercaseCVEID

func UppercaseCVEID(cveID string) string

Types

type BOMMetadata

type BOMMetadata struct {
	AssetVersionSlug      string
	AssetSlug             string
	OrgSlug               string
	ProjectSlug           string
	FrontendURL           string
	ArtifactName          string
	AssetID               uuid.UUID
	AddExternalReferences bool
	RootName              string // defaults to ArtifactName if empty
	AssetVersionName      string
}

type FlatGraph

type FlatGraph struct {
	Nodes []string
	Edges [][2]string
}

NodeIDsAndEdges returns a flat representation of the graph for comparison.

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

func (d GraphDiff) AddedNodeIDs() []string

AddedNodeIDs returns just the IDs of added nodes.

func (GraphDiff) IsEmpty

func (d GraphDiff) IsEmpty() bool

IsEmpty returns true if there are no changes.

func (GraphDiff) RemovedNodeIDs

func (d GraphDiff) RemovedNodeIDs() []string

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) String

func (p Path) String() string

String returns a comma-separated string of all nodes in the path.

func (Path) ToStringSlice

func (p Path) ToStringSlice() []string

ToStringSlice returns all nodes in the path, including fake/structural nodes.

type PurlMatchContext

type PurlMatchContext struct {
	SearchPurl                  string
	NormalizedVersion           string
	HowToInterpretVersionString VersionInterpretationType
	Qualifiers                  packageurl.Qualifiers
	Namespace                   string
}

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 {
	// contains filtered or unexported fields
}

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 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

func SBOMGraphFromCycloneDX(bom *cdx.BOM, artifactName, infoSourceID string, keepOriginalSbomRootComponent bool) (*SBOMGraph, error)

SBOMGraphFromCycloneDX creates an SBOMGraph from a CycloneDX BOM.

func SBOMGraphFromVulnerabilities

func SBOMGraphFromVulnerabilities(vulns []cdx.Vulnerability) *SBOMGraph

func (*SBOMGraph) AddArtifact

func (g *SBOMGraph) AddArtifact(name string) string

AddArtifact adds an artifact node as a child of root.

func (*SBOMGraph) AddComponent

func (g *SBOMGraph) AddComponent(comp cdx.Component) string

AddComponent adds a component node.

func (*SBOMGraph) AddEdge

func (g *SBOMGraph) AddEdge(parentID, childID string)

AddEdge adds a directed edge from parent to child.

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) AddVulnerability

func (g *SBOMGraph) AddVulnerability(vuln cdx.Vulnerability)

AddVulnerability adds a vulnerability with deduplication. When the same CVE+Affects combination exists, state priority determines which one to keep: exploitable > in_triage > false_positive

func (*SBOMGraph) Artifacts

func (g *SBOMGraph) Artifacts() iter.Seq[*GraphNode]

Artifacts returns all artifact nodes reachable from scope.

func (*SBOMGraph) Children

func (g *SBOMGraph) Children(nodeID string) iter.Seq[*GraphNode]

Children returns an iterator over direct children of a node.

func (*SBOMGraph) ChildrenOfType

func (g *SBOMGraph) ChildrenOfType(nodeID string, nodeType GraphNodeType) iter.Seq[*GraphNode]

ChildrenOfType returns children of a specific type.

func (*SBOMGraph) ClearScope

func (g *SBOMGraph) ClearScope()

func (*SBOMGraph) Clone

func (g *SBOMGraph) Clone() *SBOMGraph

Clone creates a deep copy of the graph.

func (*SBOMGraph) ComponentEdges

func (g *SBOMGraph) ComponentEdges() iter.Seq2[string, string]

ComponentEdges returns edges between components only.

func (*SBOMGraph) Components

func (g *SBOMGraph) Components() iter.Seq[*GraphNode]

Components returns all component nodes reachable from scope.

func (*SBOMGraph) ComponentsWithMultipleSources

func (g *SBOMGraph) ComponentsWithMultipleSources() []string

ComponentsWithMultipleSources returns component IDs that appear in multiple SBOMs or have VEX/CSAF. 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 (g *SBOMGraph) CurrentScopeID() string

func (*SBOMGraph) DeleteArtifactFromGraph

func (g *SBOMGraph) DeleteArtifactFromGraph(artifactName string) GraphDiff

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) Edges

func (g *SBOMGraph) Edges() iter.Seq2[string, string]

Edges returns all edges where both endpoints are reachable from scope.

func (*SBOMGraph) FindAllComponentOnlyPathsToPURL

func (g *SBOMGraph) FindAllComponentOnlyPathsToPURL(purl string, limit int) []Path

func (*SBOMGraph) GetArtifactIDs

func (g *SBOMGraph) GetArtifactIDs() []string

GetArtifactIDs returns all artifact IDs in the graph.

func (*SBOMGraph) GetInfoSourceIDs

func (g *SBOMGraph) GetInfoSourceIDs(artifactID string) []string

GetInfoSourceIDs returns all info source IDs for a given artifact.

func (*SBOMGraph) GetInfoSourceNode

func (g *SBOMGraph) GetInfoSourceNode(infoSourceID string) *GraphNode

GetInfoSourceNode returns the info source node by ID, or nil if not found.

func (*SBOMGraph) GetParentIDs

func (g *SBOMGraph) GetParentIDs(nodeID string) []string

GetParentIDs returns all parent node IDs for a given node.

func (*SBOMGraph) GetRootID

func (g *SBOMGraph) GetRootID() string

GetRootID returns the ID of the current scope root.

func (*SBOMGraph) HasInfoSource

func (g *SBOMGraph) HasInfoSource(infoSourceID string) bool

HasInfoSource checks if an info source exists in the graph.

func (*SBOMGraph) InfoSources

func (g *SBOMGraph) InfoSources() iter.Seq[*GraphNode]

InfoSources returns all information source nodes reachable from scope.

func (*SBOMGraph) IsScoped

func (g *SBOMGraph) IsScoped() bool

func (*SBOMGraph) LicenseDistribution

func (g *SBOMGraph) LicenseDistribution() map[string]int

func (*SBOMGraph) MergeGraph

func (g *SBOMGraph) MergeGraph(other *SBOMGraph) GraphDiff

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

func (g *SBOMGraph) MinimalTreeToPURL(purl string, maxDepth int) minimalTree

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) Node

func (g *SBOMGraph) Node(id string) *GraphNode

Node returns a node by ID, or nil if not reachable from scope.

func (*SBOMGraph) NodeIDsAndEdges

func (g *SBOMGraph) NodeIDsAndEdges() FlatGraph

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) Scope

func (g *SBOMGraph) Scope(id string) error

func (*SBOMGraph) ScopeToArtifact

func (g *SBOMGraph) ScopeToArtifact(artifactName string) error

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

func (*SBOMGraph) Vulnerabilities

func (g *SBOMGraph) Vulnerabilities() iter.Seq[*cdx.Vulnerability]

Vulnerabilities returns all vulnerabilities. Deduplication with state priority is handled in AddVulnerability.

type VersionInterpretationType

type VersionInterpretationType string
const (
	ExactVersionString       VersionInterpretationType = "exact"
	SemanticVersionString    VersionInterpretationType = "semver_range"
	EmptyVersion             VersionInterpretationType = "empty_version"
	EcosystemSpecificVersion VersionInterpretationType = "ecosystem_specific"
)

type VexReport

type VexReport struct {
	Report *cdx.BOM
	Source string
}

func NewVexReport

func NewVexReport(report *cdx.BOM, source string) (*VexReport, error)

Jump to

Keyboard shortcuts

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