// Package sbom generates Software Bills of Materials in CycloneDX 1.4 JSON format. // https://cyclonedx.org/specification/overview/ package sbom import ( "fmt" "time" ) const ( FormatCycloneDX = "cyclonedx-json-1.4" SpecVersion = "1.4" BOMFormat = "CycloneDX" ) // Document is the top-level CycloneDX 1.4 BOM. type Document struct { BOMFormat string `json:"bomFormat"` SpecVersion string `json:"specVersion"` SerialNumber string `json:"serialNumber"` Version int `json:"version"` Metadata Metadata `json:"metadata"` Components []Component `json:"components"` } type Metadata struct { Timestamp string `json:"timestamp"` Tools []Tool `json:"tools"` Component *Component `json:"component,omitempty"` } type Tool struct { Vendor string `json:"vendor"` Name string `json:"name"` Version string `json:"version"` } // Component represents a software dependency in the BOM. type Component struct { Type string `json:"type"` // "library", "application", "framework" Name string `json:"name"` Version string `json:"version,omitempty"` PURL string `json:"purl,omitempty"` Description string `json:"description,omitempty"` Scope string `json:"scope,omitempty"` // "required", "optional" ExternalRefs []ExternalRef `json:"externalReferences,omitempty"` } type ExternalRef struct { Type string `json:"type"` // "website", "vcs", "distribution" URL string `json:"url"` } // NewDocument creates a blank CycloneDX 1.4 document with metadata populated. func NewDocument(repoName, sha string) *Document { return &Document{ BOMFormat: BOMFormat, SpecVersion: SpecVersion, SerialNumber: fmt.Sprintf("urn:uuid:forgebucket:%s:%s", repoName, sha[:7]), Version: 1, Metadata: Metadata{ Timestamp: time.Now().UTC().Format(time.RFC3339), Tools: []Tool{ {Vendor: "ForgeBucket", Name: "sbom-generator", Version: "1.0.0"}, }, Component: &Component{ Type: "application", Name: repoName, }, }, Components: []Component{}, } } // PURL helpers — produce Package URL strings per ecosystem. func golangPURL(module, version string) string { return fmt.Sprintf("pkg:golang/%s@%s", module, version) } func npmPURL(name, version string) string { return fmt.Sprintf("pkg:npm/%s@%s", name, version) } func pypiPURL(name, version string) string { return fmt.Sprintf("pkg:pypi/%s@%s", name, version) } func cargoPURL(name, version string) string { return fmt.Sprintf("pkg:cargo/%s@%s", name, version) } func gemPURL(name, version string) string { return fmt.Sprintf("pkg:gem/%s@%s", name, version) } func mavenPURL(group, artifact, version string) string { return fmt.Sprintf("pkg:maven/%s/%s@%s", group, artifact, version) }