Skip to content

Commit

Permalink
✨ Do not error out on SBOM generation, use the sbom ErrorMessage inst…
Browse files Browse the repository at this point in the history
…ead.

Signed-off-by: Preslav <preslav@mondoo.com>
  • Loading branch information
preslavgerchev committed Feb 10, 2025
1 parent 4fc37ac commit 6d1c54d
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 121 deletions.
181 changes: 94 additions & 87 deletions sbom/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"time"

"github.com/pkg/errors"
"go.mondoo.com/cnquery/v11"
"go.mondoo.com/cnquery/v11/cli/reporter"
"go.mondoo.com/cnquery/v11/mrn"
Expand All @@ -22,7 +23,13 @@ func NewBom(report *reporter.Report) ([]*sbom.Sbom, error) {
return GenerateBom(report)
}

func GenerateBomV2(r *reporter.Report) []*sbom.Sbom {
sboms, _ := GenerateBom(r)
return sboms
}

// GenerateBom generates a BOM from a cnquery json report collection
// depercated: Use GenerateBomV2 instead
func GenerateBom(r *reporter.Report) ([]*sbom.Sbom, error) {
if r == nil {
return nil, nil
Expand All @@ -39,7 +46,6 @@ func GenerateBom(r *reporter.Report) ([]*sbom.Sbom, error) {
boms := []*sbom.Sbom{}
for mrn := range r.Assets {
asset := r.Assets[mrn]

bom := &sbom.Sbom{
Generator: generator,
Timestamp: now,
Expand All @@ -65,82 +71,55 @@ func GenerateBom(r *reporter.Report) ([]*sbom.Sbom, error) {
boms = append(boms, bom)
continue
}
if dataPoints != nil {
for k := range dataPoints.Values {
dataValue := dataPoints.Values[k]
jsondata, err := reporter.JsonValue(dataValue.Content)
if err != nil {
return nil, err
}
rb := BomFields{}
err = json.Unmarshal(jsondata, &rb)
if err != nil {
return nil, err
}
if rb.Asset != nil {
bom.Asset.Name = rb.Asset.Name
bom.Asset.Platform.Name = rb.Asset.Platform
bom.Asset.Platform.Version = rb.Asset.Version
bom.Asset.Platform.Family = rb.Asset.Family
bom.Asset.Platform.Arch = rb.Asset.Arch
bom.Asset.Platform.Cpes = rb.Asset.CPEs
bom.Asset.Platform.Labels = rb.Asset.Labels
bom.Asset.PlatformIds = enrichPlatformIds(rb.Asset.IDs)
}

if bom.Asset == nil {
bom.Asset = &sbom.Asset{}
}
if bom.Asset.Labels == nil {
bom.Asset.Labels = map[string]string{}
}
for _, dataValue := range dataPoints.Values {
jsondata, err := reporter.JsonValue(dataValue.Content)
if err != nil {
bom.Status = sbom.Status_STATUS_FAILED
bom.ErrorMessage = errors.Wrap(err, "failed to parse json data").Error()
break
}
rb := BomFields{}
err = json.Unmarshal(jsondata, &rb)
if err != nil {
bom.Status = sbom.Status_STATUS_FAILED
bom.ErrorMessage = errors.Wrap(err, "failed to parse bom fields json data").Error()
break
}
if rb.Asset != nil {
bom.Asset.Name = rb.Asset.Name
bom.Asset.Platform.Name = rb.Asset.Platform
bom.Asset.Platform.Version = rb.Asset.Version
bom.Asset.Platform.Family = rb.Asset.Family
bom.Asset.Platform.Arch = rb.Asset.Arch
bom.Asset.Platform.Cpes = rb.Asset.CPEs
bom.Asset.Platform.Labels = rb.Asset.Labels
bom.Asset.PlatformIds = enrichPlatformIds(rb.Asset.IDs)
}

// store version of running kernel
for _, kernel := range rb.KernelInstalled {
if kernel.Running {
bom.Asset.Labels[LABEL_KERNEL_RUNNING] = kernel.Version
}
}
if bom.Asset == nil {
bom.Asset = &sbom.Asset{}
}
if bom.Asset.Labels == nil {
bom.Asset.Labels = map[string]string{}
}

if rb.Packages != nil {
for _, pkg := range rb.Packages {
bomPkg := &sbom.Package{
Name: pkg.Name,
Version: pkg.Version,
Architecture: pkg.Arch,
Origin: pkg.Origin,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: pkg.Format,
}

for _, filepath := range pkg.FilePaths {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: filepath,
})
}

bom.Packages = append(bom.Packages, bomPkg)
}
// store version of running kernel
for _, kernel := range rb.KernelInstalled {
if kernel.Running {
bom.Asset.Labels[LABEL_KERNEL_RUNNING] = kernel.Version
}
}

for _, pkg := range rb.PythonPackages {
if rb.Packages != nil {
for _, pkg := range rb.Packages {
bomPkg := &sbom.Package{
Name: pkg.Name,
Version: pkg.Version,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: "pypi",
}

// deprecated path, all files are now in the FilePaths field
// TODO: update once the python resource returns multiple results
if pkg.FilePath != "" {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: pkg.FilePath,
})
Name: pkg.Name,
Version: pkg.Version,
Architecture: pkg.Arch,
Origin: pkg.Origin,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: pkg.Format,
}

for _, filepath := range pkg.FilePaths {
Expand All @@ -152,25 +131,53 @@ func GenerateBom(r *reporter.Report) ([]*sbom.Sbom, error) {

bom.Packages = append(bom.Packages, bomPkg)
}
}

for _, pkg := range rb.NpmPackages {
bomPkg := &sbom.Package{
Name: pkg.Name,
Version: pkg.Version,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: "npm",
}
for _, pkg := range rb.PythonPackages {
bomPkg := &sbom.Package{
Name: pkg.Name,
Version: pkg.Version,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: "pypi",
}

for _, filepath := range pkg.FilePaths {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: filepath,
})
}
// deprecated path, all files are now in the FilePaths field
// TODO: update once the python resource returns multiple results
if pkg.FilePath != "" {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: pkg.FilePath,
})
}

bom.Packages = append(bom.Packages, bomPkg)
for _, filepath := range pkg.FilePaths {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: filepath,
})
}

bom.Packages = append(bom.Packages, bomPkg)
}

for _, pkg := range rb.NpmPackages {
bomPkg := &sbom.Package{
Name: pkg.Name,
Version: pkg.Version,
Purl: pkg.Purl,
Cpes: pkg.CPEs,
Type: "npm",
}

for _, filepath := range pkg.FilePaths {
bomPkg.EvidenceList = append(bomPkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: filepath,
})
}

bom.Packages = append(bom.Packages, bomPkg)
}
}
boms = append(boms, bom)
Expand Down
4 changes: 2 additions & 2 deletions sbom/generator/report_collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ package generator

import (
"encoding/json"
"go.mondoo.com/cnquery/v11/cli/reporter"
"os"

"go.mondoo.com/cnquery/v11/cli/reporter"
"sigs.k8s.io/yaml"
)

Expand Down Expand Up @@ -59,7 +60,6 @@ func LoadReport(filename string) (*reporter.Report, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err

}
var report *reporter.Report
err = yaml.Unmarshal(data, &report)
Expand Down
79 changes: 47 additions & 32 deletions sbom/generator/sbom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,67 @@
package generator

import (
"go.mondoo.com/cnquery/v11/sbom"
"testing"

"go.mondoo.com/cnquery/v11/sbom"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSbomGeneration(t *testing.T) {
report, err := LoadReport("../testdata/alpine.json")
require.NoError(t, err)
t.Run("generate sbom from a full report", func(t *testing.T) {
report, err := LoadReport("../testdata/alpine.json")
require.NoError(t, err)

sboms, err := GenerateBom(report)
require.NoError(t, err)
sboms, err := GenerateBom(report)
require.NoError(t, err)

// store bom in different formats
selectedBom := sboms[0]
// store bom in different formats
selectedBom := sboms[0]

assert.Equal(t, "alpine:latest", selectedBom.Asset.Name)
assert.Equal(t, "trace-alpine", selectedBom.Asset.TraceId)
assert.Equal(t, "aarch64", selectedBom.Asset.Platform.Arch)
assert.Equal(t, "alpine", selectedBom.Asset.Platform.Name)
assert.Equal(t, "3.19.0", selectedBom.Asset.Platform.Version)
assert.Equal(t, []string{"//platformid.api.mondoo.app/runtime/docker/images/1dc785547989b0db1c3cd9949c57574393e69bea98bfe044b0588e24721aa402"}, selectedBom.Asset.PlatformIds)
assert.Equal(t, "alpine:latest", selectedBom.Asset.Name)
assert.Equal(t, "trace-alpine", selectedBom.Asset.TraceId)
assert.Equal(t, "aarch64", selectedBom.Asset.Platform.Arch)
assert.Equal(t, "alpine", selectedBom.Asset.Platform.Name)
assert.Equal(t, "3.19.0", selectedBom.Asset.Platform.Version)
assert.Equal(t, []string{"//platformid.api.mondoo.app/runtime/docker/images/1dc785547989b0db1c3cd9949c57574393e69bea98bfe044b0588e24721aa402"}, selectedBom.Asset.PlatformIds)

// search os package
pkg := findProtoPkg(selectedBom.Packages, "alpine-baselayout")
assert.Equal(t, "alpine-baselayout", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "etc/profile.d/color_prompt.sh.disabled",
})
// search os package
pkg := findProtoPkg(selectedBom.Packages, "alpine-baselayout")
assert.Equal(t, "alpine-baselayout", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "etc/profile.d/color_prompt.sh.disabled",
})

// search python package
pkg = findProtoPkg(selectedBom.Packages, "pip")
assert.Equal(t, "pip", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "/opt/lib/python3.9/site-packages/pip-21.2.4.dist-info/METADATA",
})

// search python package
pkg = findProtoPkg(selectedBom.Packages, "pip")
assert.Equal(t, "pip", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "/opt/lib/python3.9/site-packages/pip-21.2.4.dist-info/METADATA",
// search npm package
pkg = findProtoPkg(selectedBom.Packages, "npm")
assert.Equal(t, "npm", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "/opt/lib/node_modules/npm/package.json",
})
})
t.Run("generate sbom from a report with a package error", func(t *testing.T) {
report, err := LoadReport("testdata/alpine-failed-package.json")
require.NoError(t, err)

sboms, err := GenerateBom(report)
require.NoError(t, err)

// search npm package
pkg = findProtoPkg(selectedBom.Packages, "npm")
assert.Equal(t, "npm", pkg.Name)
assert.Contains(t, pkg.EvidenceList, &sbom.Evidence{
Type: sbom.EvidenceType_EVIDENCE_TYPE_FILE,
Value: "/opt/lib/node_modules/npm/package.json",
// store bom in different formats
selectedBom := sboms[0]
assert.Equal(t, sbom.Status_STATUS_FAILED, selectedBom.Status)
assert.Contains(t, selectedBom.ErrorMessage, "failed to parse bom fields json data")
})
}

Expand Down
Loading

0 comments on commit 6d1c54d

Please sign in to comment.