github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/redhat/package.go (about)

     1  package redhat
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/anchore/packageurl-go"
     9  	"github.com/anchore/syft/syft/file"
    10  	"github.com/anchore/syft/syft/linux"
    11  	"github.com/anchore/syft/syft/pkg"
    12  )
    13  
    14  func newDBPackage(dbOrRpmLocation file.Location, m pkg.RpmDBEntry, distro *linux.Release, licenses []string) pkg.Package {
    15  	p := pkg.Package{
    16  		Name:      m.Name,
    17  		Version:   toELVersion(m.Epoch, m.Version, m.Release),
    18  		Licenses:  pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbOrRpmLocation, licenses...)...),
    19  		PURL:      packageURL(m.Name, m.Arch, m.Epoch, m.SourceRpm, m.Version, m.Release, distro),
    20  		Locations: file.NewLocationSet(dbOrRpmLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
    21  		Type:      pkg.RpmPkg,
    22  		Metadata:  m,
    23  	}
    24  
    25  	p.SetID()
    26  	return p
    27  }
    28  
    29  func newArchivePackage(archiveLocation file.Location, m pkg.RpmArchive, licenses []string) pkg.Package {
    30  	p := pkg.Package{
    31  		Name:      m.Name,
    32  		Version:   toELVersion(m.Epoch, m.Version, m.Release),
    33  		Licenses:  pkg.NewLicenseSet(pkg.NewLicensesFromLocation(archiveLocation, licenses...)...),
    34  		PURL:      packageURL(m.Name, m.Arch, m.Epoch, m.SourceRpm, m.Version, m.Release, nil),
    35  		Locations: file.NewLocationSet(archiveLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
    36  		Type:      pkg.RpmPkg,
    37  		Metadata:  m,
    38  	}
    39  
    40  	p.SetID()
    41  	return p
    42  }
    43  
    44  // newMetadataFromManifestLine parses an entry in an RPM manifest file as used in Mariner distroless containers.
    45  // Each line is the output from:
    46  // - rpm --query --all --query-format "%{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{BUILDTIME}\t%{VENDOR}\t%{EPOCH}\t%{SIZE}\t%{ARCH}\t%{EPOCHNUM}\t%{SOURCERPM}\n"
    47  // - https://github.com/microsoft/CBL-Mariner/blob/3df18fac373aba13a54bd02466e64969574f13af/toolkit/docs/how_it_works/5_misc.md?plain=1#L150
    48  func newMetadataFromManifestLine(entry string) (*pkg.RpmDBEntry, error) {
    49  	parts := strings.Split(entry, "\t")
    50  	if len(parts) < 10 {
    51  		return nil, fmt.Errorf("unexpected number of fields in line: %s", entry)
    52  	}
    53  
    54  	versionParts := strings.Split(parts[1], "-")
    55  	if len(versionParts) != 2 {
    56  		return nil, fmt.Errorf("unexpected version field: %s", parts[1])
    57  	}
    58  	version := versionParts[0]
    59  	release := versionParts[1]
    60  
    61  	converted, err := strconv.Atoi(parts[8])
    62  	var epoch *int
    63  	if err != nil || parts[5] == "(none)" {
    64  		epoch = nil
    65  	} else {
    66  		epoch = &converted
    67  	}
    68  
    69  	converted, err = strconv.Atoi(parts[6])
    70  	var size int
    71  	if err == nil {
    72  		size = converted
    73  	}
    74  	return &pkg.RpmDBEntry{
    75  		Name:      parts[0],
    76  		Version:   version,
    77  		Epoch:     epoch,
    78  		Arch:      parts[7],
    79  		Release:   release,
    80  		SourceRpm: parts[9],
    81  		Vendor:    parts[4],
    82  		Size:      size,
    83  	}, nil
    84  }
    85  
    86  // packageURL returns the PURL for the specific RHEL package (see https://github.com/package-url/purl-spec)
    87  func packageURL(name, arch string, epoch *int, srpm string, version, release string, distro *linux.Release) string {
    88  	var namespace string
    89  	if distro != nil {
    90  		namespace = distro.ID
    91  	}
    92  
    93  	qualifiers := map[string]string{}
    94  
    95  	if arch != "" {
    96  		qualifiers[pkg.PURLQualifierArch] = arch
    97  	}
    98  
    99  	if epoch != nil {
   100  		qualifiers[pkg.PURLQualifierEpoch] = strconv.Itoa(*epoch)
   101  	}
   102  
   103  	if srpm != "" {
   104  		qualifiers[pkg.PURLQualifierUpstream] = srpm
   105  	}
   106  
   107  	return packageurl.NewPackageURL(
   108  		packageurl.TypeRPM,
   109  		namespace,
   110  		name,
   111  		// for purl the epoch is a qualifier, not part of the version
   112  		// see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst under the RPM section
   113  		fmt.Sprintf("%s-%s", version, release),
   114  		pkg.PURLQualifiers(
   115  			qualifiers,
   116  			distro,
   117  		),
   118  		"",
   119  	).ToString()
   120  }