fix(sbom): add buildInfo info as properties (#9683)

This commit is contained in:
DmitriyLewen
2025-10-20 12:00:39 +06:00
committed by GitHub
parent fc976bea48
commit 2c43425e05
5 changed files with 331 additions and 24 deletions

View File

@@ -47,6 +47,12 @@ const (
PropertyLayerDigest = "LayerDigest"
PropertyLayerDiffID = "LayerDiffID"
// Red Hat packages only.
// A package can use multiple NVR + Arch content sets or fields.
PropertyContentSet = "ContentSet"
PropertyNVR = "NVR"
PropertyArch = "Arch"
// Relationships
RelationshipDescribes RelationshipType = "describes"
RelationshipContains RelationshipType = "contains"

View File

@@ -201,30 +201,8 @@ func (m *Decoder) decodePackage(ctx context.Context, c *core.Component) (*ftypes
pkg.Name = m.pkgName(pkg, c)
pkg.ID = dependency.ID(p.LangType(), pkg.Name, p.Version) // Re-generate ID with the updated name
var err error
for _, prop := range c.Properties {
switch prop.Name {
case core.PropertyPkgID:
pkg.ID = prop.Value
case core.PropertyFilePath:
pkg.FilePath = prop.Value
case core.PropertySrcName:
pkg.SrcName = prop.Value
case core.PropertySrcVersion:
pkg.SrcVersion = prop.Value
case core.PropertySrcRelease:
pkg.SrcRelease = prop.Value
case core.PropertySrcEpoch:
if pkg.SrcEpoch, err = strconv.Atoi(prop.Value); err != nil {
return nil, xerrors.Errorf("invalid src epoch: %w", err)
}
case core.PropertyModularitylabel:
pkg.Modularitylabel = prop.Value
case core.PropertyLayerDigest:
pkg.Layer.Digest = prop.Value
case core.PropertyLayerDiffID:
pkg.Layer.DiffID = prop.Value
}
if err := fillPkgFieldsFromComponentProps(c.Properties, pkg); err != nil {
return nil, xerrors.Errorf("failed to fill package properties: %w", err)
}
pkg.Identifier.BOMRef = c.PkgIdentifier.BOMRef
@@ -413,3 +391,43 @@ func (m *Decoder) addOrphanPkgs(sbom *types.SBOM) error {
}
return nil
}
func fillPkgFieldsFromComponentProps(props []core.Property, pkg *ftypes.Package) error {
buildInfo := &ftypes.BuildInfo{}
var err error
for _, prop := range props {
switch prop.Name {
case core.PropertyPkgID:
pkg.ID = prop.Value
case core.PropertyFilePath:
pkg.FilePath = prop.Value
case core.PropertySrcName:
pkg.SrcName = prop.Value
case core.PropertySrcVersion:
pkg.SrcVersion = prop.Value
case core.PropertySrcRelease:
pkg.SrcRelease = prop.Value
case core.PropertySrcEpoch:
if pkg.SrcEpoch, err = strconv.Atoi(prop.Value); err != nil {
return xerrors.Errorf("invalid src epoch: %w", err)
}
case core.PropertyModularitylabel:
pkg.Modularitylabel = prop.Value
case core.PropertyLayerDigest:
pkg.Layer.Digest = prop.Value
case core.PropertyLayerDiffID:
pkg.Layer.DiffID = prop.Value
case core.PropertyContentSet:
buildInfo.ContentSets = append(buildInfo.ContentSets, prop.Value)
case core.PropertyNVR:
buildInfo.Nvr = prop.Value
case core.PropertyArch:
buildInfo.Arch = prop.Value
}
}
if len(buildInfo.ContentSets) > 0 || buildInfo.Nvr != "" {
pkg.BuildInfo = buildInfo
}
return nil
}

View File

@@ -85,6 +85,24 @@ var (
Version: "2.0.0",
},
},
Properties: core.Properties{
{
Name: core.PropertyContentSet,
Value: "rhel-9-for-aarch64-appstream-rpms",
},
{
Name: core.PropertyContentSet,
Value: "rhel-9-for-aarch64-appstream-source-rpms",
},
{
Name: core.PropertySrcName,
Value: "rpm-package",
},
{
Name: core.PropertySrcVersion,
Value: "2.0.0",
},
},
Licenses: []string{"GPL-2.0"},
}
@@ -233,6 +251,47 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
},
},
},
{
name: "rpm package",
setupBOM: func() *core.BOM {
bom := core.NewBOM(core.Options{})
bom.SerialNumber = "test-multiple-os-types"
bom.Version = 1
// Add RPM package
bom.AddComponent(rpmTestComponent)
return bom
},
wantSBOM: types.SBOM{
Metadata: types.Metadata{
OS: nil, // No OS detected
},
Packages: []ftypes.PackageInfo{
{
Packages: ftypes.Packages{
{
ID: "rpm-package@2.0.0",
Name: "rpm-package",
Version: "2.0.0",
SrcName: "rpm-package",
SrcVersion: "2.0.0",
Licenses: []string{"GPL-2.0"},
Identifier: ftypes.PkgIdentifier{
PURL: rpmTestComponent.PkgIdentifier.PURL,
},
BuildInfo: &ftypes.BuildInfo{
ContentSets: []string{
"rhel-9-for-aarch64-appstream-rpms",
"rhel-9-for-aarch64-appstream-source-rpms",
},
},
},
},
},
},
},
},
{
name: "multiple OS package types should return error",
setupBOM: func() *core.BOM {

View File

@@ -423,6 +423,29 @@ func (*Encoder) component(result types.Result, pkg ftypes.Package) *core.Compone
},
}
// Fill Red Hat specific properties
if pkg.BuildInfo != nil {
for _, cs := range pkg.BuildInfo.ContentSets {
properties = append(properties, core.Property{
Name: core.PropertyContentSet,
Value: cs,
})
}
if pkg.BuildInfo.Nvr != "" {
properties = append(properties, core.Property{
Name: core.PropertyNVR,
Value: pkg.BuildInfo.Nvr,
})
}
if pkg.BuildInfo.Arch != "" {
properties = append(properties, core.Property{
Name: core.PropertyArch,
Value: pkg.BuildInfo.Arch,
})
}
}
var files []core.File
if pkg.FilePath != "" || pkg.Digest != "" {
files = append(files, core.File{

View File

@@ -589,6 +589,207 @@ func TestEncoder_Encode(t *testing.T) {
},
},
},
{
name: "Red Hat container image",
report: types.Report{
SchemaVersion: 2,
ArtifactName: "redhat/ubi9-minimal",
ArtifactType: ftypes.TypeContainerImage,
Metadata: types.Metadata{
OS: &ftypes.OS{
Family: ftypes.RedHat,
Name: "9.5",
},
RepoTags: []string{
"redhat/ubi9:latest",
},
RepoDigests: []string{
"redhat/ubi9-minimal@sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c",
},
ImageConfig: v1.ConfigFile{
Config: v1.Config{
Labels: map[string]string{
"vendor": "aquasecurity",
},
},
},
},
Results: []types.Result{
{
Target: "redhat/ubi9-minimal (redhat 9.5)",
Type: ftypes.RedHat,
Class: types.ClassOSPkg,
Packages: []ftypes.Package{
{
ID: "glibc@2.34-125.el9_5.8.aarch64",
Name: "glibc",
Version: "2.34",
Release: "125.el9_5.8.aarch64",
Arch: "aarch64",
SrcName: "glibc",
SrcVersion: "2.34",
SrcRelease: "125.el9_5.8",
Maintainer: "Red Hat, Inc.",
BuildInfo: &ftypes.BuildInfo{
ContentSets: []string{
"rhel-9-for-aarch64-appstream-rpms",
"rhel-9-for-aarch64-appstream-source-rpms",
},
},
Identifier: ftypes.PkgIdentifier{
UID: "2acbd589f06ebbb8",
PURL: &packageurl.PackageURL{
Type: packageurl.TypeRPM,
Name: "glibc",
Version: "2.34-125.el9_5.8",
Qualifiers: packageurl.Qualifiers{
{
Key: "arch",
Value: "aarch64",
},
{
Key: "distro",
Value: "redhat-9.5",
},
},
},
},
},
},
},
},
},
wantComponents: map[uuid.UUID]*core.Component{
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): {
Type: core.TypeContainerImage,
Name: "redhat/ubi9-minimal",
Root: true,
PkgIdentifier: ftypes.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeOCI,
Name: "ubi9-minimal",
Version: "sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c",
Qualifiers: packageurl.Qualifiers{
{
Key: "repository_url",
Value: "index.docker.io/redhat/ubi9-minimal",
},
},
},
BOMRef: "pkg:oci/ubi9-minimal@sha256%3Ae1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c?repository_url=index.docker.io%2Fredhat%2Fubi9-minimal",
},
Properties: []core.Property{
{
Name: "Labels:vendor",
Value: "aquasecurity",
},
{
Name: core.PropertyRepoDigest,
Value: "redhat/ubi9-minimal@sha256:e1c4703364c5cb58f5462575dc90345bcd934ddc45e6c32f9c162f2b5617681c",
},
{
Name: core.PropertyRepoTag,
Value: "redhat/ubi9:latest",
},
{
Name: core.PropertySchemaVersion,
Value: "2",
},
},
},
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): {
Type: core.TypeOS,
Name: "redhat",
Version: "9.5",
Properties: []core.Property{
{
Name: core.PropertyClass,
Value: "os-pkgs",
},
{
Name: core.PropertyType,
Value: "redhat",
},
},
PkgIdentifier: ftypes.PkgIdentifier{
BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
},
},
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): {
Type: core.TypeLibrary,
Name: "glibc",
Version: "2.34-125.el9_5.8",
SrcName: "glibc",
SrcVersion: "2.34-125.el9_5.8",
Supplier: "Red Hat, Inc.",
Properties: []core.Property{
{
Name: core.PropertyContentSet,
Value: "rhel-9-for-aarch64-appstream-rpms",
},
{
Name: core.PropertyContentSet,
Value: "rhel-9-for-aarch64-appstream-source-rpms",
},
{
Name: core.PropertyPkgID,
Value: "glibc@2.34-125.el9_5.8.aarch64",
},
{
Name: core.PropertyPkgType,
Value: "redhat",
},
{
Name: core.PropertySrcName,
Value: "glibc",
},
{
Name: core.PropertySrcRelease,
Value: "125.el9_5.8",
},
{
Name: core.PropertySrcVersion,
Value: "2.34",
},
},
PkgIdentifier: ftypes.PkgIdentifier{
UID: "2acbd589f06ebbb8",
PURL: &packageurl.PackageURL{
Type: packageurl.TypeRPM,
Name: "glibc",
Version: "2.34-125.el9_5.8",
Qualifiers: packageurl.Qualifiers{
{
Key: "arch",
Value: "aarch64",
},
{
Key: "distro",
Value: "redhat-9.5",
},
},
},
BOMRef: "pkg:rpm/glibc@2.34-125.el9_5.8?arch=aarch64&distro=redhat-9.5",
},
},
},
wantRels: map[uuid.UUID][]core.Relationship{
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): {
{
Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"),
Type: core.RelationshipContains,
},
},
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): {
{
Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"),
Type: core.RelationshipContains,
},
},
uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): nil,
},
wantVulns: make(map[uuid.UUID][]core.Vulnerability),
},
{
name: "root package",
report: types.Report{