Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions cmd/ssbom/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func run() error {
}
defer zstdReader.Close()

osRelease, err := ReadOSRelease(filepath.Join(root, osReleasePath))
osId, versionId, err := ReadOSRelease(filepath.Join(root, osReleasePath))
if err != nil {
if os.IsNotExist(err) {
fmt.Println("Warning: OS release file not found in the chiselled rootfs.")
Expand All @@ -67,7 +67,7 @@ func run() error {
}
}

doc, err := converter.Convert(zstdReader, osRelease)
doc, err := converter.Convert(zstdReader, osId, versionId)
if err != nil {
return err
}
Expand All @@ -83,13 +83,18 @@ func run() error {
return nil
}

func ReadOSRelease(configfile string) (string, error) {
func ReadOSRelease(configfile string) (string, string, error) {
cfg, err := ini.Load(configfile)
if err != nil {
return "", err
return "", "", err
}

versionId := cfg.Section("").Key("VERSION_ID").String()
osId := cfg.Section("").Key("ID").String()

return versionId, nil
if versionId == "" || osId == "" {
return "", "", fmt.Errorf("invalid os-release file: missing ID or VERSION_ID")
}

return osId, versionId, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/canonical/chisel v1.1.1-0.20250127163729-ad87b0bb6f96 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/h2non/filetype v1.1.3 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/pretty v0.2.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
Expand Down
50 changes: 32 additions & 18 deletions internal/builder/builder.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package builder

import (
"crypto/md5"
"fmt"
"regexp"
"strings"
"time"

"github.com/google/uuid"
"github.com/spdx/tools-golang/spdx"
"github.com/spdx/tools-golang/spdx/v2/common"
)

const EmptySHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
const EmptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
const DocumentName = "Chiselled Ubuntu Rootfs"

type PackageInfo struct {
Name string
Expand Down Expand Up @@ -40,15 +44,25 @@ var ChiselSbomDocCreator = []common.Creator{
},
}

func BuildSPDXDocument(distro string, sliceInfos *[]SliceInfo, packageInfos *[]PackageInfo, pathInfos *[]PathInfo) (*spdx.Document, error) {
func BuildSPDXDocument(name, distro string, sliceInfos *[]SliceInfo, packageInfos *[]PackageInfo, pathInfos *[]PathInfo) (*spdx.Document, error) {
normalizedName := regexp.MustCompile(`(\s|#)+`).ReplaceAllString(strings.TrimSpace(name), "-")
randomUuid, err := uuid.NewUUID()
if err != nil {
return nil, err
}

docNameSpace := "https://sbom.canonical.com/spdxdocs/" + normalizedName + "-" + randomUuid.String()

doc := &spdx.Document{
SPDXVersion: spdx.Version,
DataLicense: spdx.DataLicense,
SPDXIdentifier: spdx.ElementID("DOCUMENT"),
DocumentName: DocumentName,
DocumentName: name,
CreationInfo: &spdx.CreationInfo{
Creators: ChiselSbomDocCreator,
Created: time.Now().UTC().Format(time.RFC3339),
},
DocumentNamespace: docNameSpace,
}

if distro != "" {
Expand Down Expand Up @@ -102,31 +116,33 @@ func BuildSPDXDocument(distro string, sliceInfos *[]SliceInfo, packageInfos *[]P
return doc, nil
}

func getMD5Hash(s string) string {
h := md5.New()
h.Write([]byte(s))
return fmt.Sprintf("%x", h.Sum(nil))
}

func OSId(distro string) string {
return fmt.Sprintf("OperatingSystem-ubuntu-%s", distro)
return fmt.Sprintf("OperatingSystem-ubuntu-%s", getMD5Hash(distro))
}

func (p *PackageInfo) SPDXId() string {
return fmt.Sprintf("Package-%s", p.Name)
return fmt.Sprintf("Package-%s", getMD5Hash(p.Name))
}

func (s *SliceInfo) SPDXId() string {
return fmt.Sprintf("Slice-%s", s.Name)
return fmt.Sprintf("Slice-%s", getMD5Hash(s.Name))
}

func (p *PathInfo) SPDXId() string {
return fmt.Sprintf("File-%s", p.Path)
return fmt.Sprintf("File-%s", getMD5Hash(p.Path))
}

var UbuntuPackageSupplier = common.Supplier{
SupplierType: "Person",
Supplier: "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
}

func (p *PackageInfo) CPE23Locator() string {
return fmt.Sprintf("cpe:2.3:a:%s:%s:%s:*:*:*:*:*:*:*", p.Name, p.Name, p.Version)
}

func (p *PackageInfo) PurlLocator() string {
locator := fmt.Sprintf("pkg:deb/ubuntu/%s@%s?arch=%s", p.Name, p.Version, p.Arch)
if p.Distro != "" {
Expand All @@ -146,11 +162,6 @@ func (p *PackageInfo) buildPackageSection() (*spdx.Package, *spdx.Relationship,
PackageComment: "This package includes one or more slice(s); see Relationship information.",
PackageSupplier: &UbuntuPackageSupplier,
PackageExternalReferences: []*spdx.PackageExternalReference{
{
Category: "SECURITY",
RefType: "cpe23Type",
Locator: p.CPE23Locator(),
},
{
Category: "PACKAGE_MANAGER",
RefType: "purl",
Expand All @@ -175,7 +186,7 @@ func (s *SliceInfo) buildSliceSection() (*spdx.Package, *spdx.Relationship, erro
PackageName: s.Name,
PackageSPDXIdentifier: common.ElementID(s.SPDXId()),
PackageDownloadLocation: "NOASSERTION",
FilesAnalyzed: false,
FilesAnalyzed: true,
PackageComment: fmt.Sprintf("This slice is a sub-package of the package %s; see Relationship information.", packageName),
}

Expand Down Expand Up @@ -211,9 +222,12 @@ func (f *PathInfo) buildPathSection() (*spdx.File, []*spdx.Relationship, error)
if f.FinalSHA256 != "" {
sha256 = f.FinalSHA256
}
if sha256 == "" {
sha256 = EmptySHA256
}
var fileType int
file := &spdx.File{
FileName: f.Path,
FileName: strings.TrimLeft(f.Path, "/"),
FileSPDXIdentifier: common.ElementID(f.SPDXId()),
Checksums: []common.Checksum{{Algorithm: common.SHA256, Value: sha256}},
FileCopyrightText: "NOASSERTION",
Expand Down
4 changes: 2 additions & 2 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

// Convert converts a JSONWall to an SPDX document.
func Convert(reader io.Reader, distro string) (*spdx.Document, error) {
func Convert(reader io.Reader, name string, distro string) (*spdx.Document, error) {
db, err := jsonwall.ReadDB(reader)
if err != nil {
return nil, fmt.Errorf("cannot read manifest: %s", err)
Expand All @@ -27,7 +27,7 @@ func Convert(reader io.Reader, distro string) (*spdx.Document, error) {
packageInfos := manifestData.ProcessPackages()
pathInfos := manifestData.ProcessPaths()

doc, err := builder.BuildSPDXDocument(manifestData.Distro, &sliceInfos, &packageInfos, &pathInfos)
doc, err := builder.BuildSPDXDocument(name, distro, &sliceInfos, &packageInfos, &pathInfos)
if err != nil {
return nil, err
}
Expand Down