Skip to content

Commit

Permalink
Add RPM file scanning support
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Zantow <kzantow@gmail.com>
  • Loading branch information
kzantow committed Sep 1, 2022
1 parent 1b0cfe7 commit 5185972
Show file tree
Hide file tree
Showing 17 changed files with 324 additions and 27 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/validations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ jobs:
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/packages.fingerprint' ) }}

- name: Build cache key for rpm test-fixture blobs (for unit tests)
run: make rpm-binaries-fingerprint

- name: Restore RPM test-fixture cache
id: unit-rpm-cache
uses: actions/cache@v2.1.3
with:
path: syft/pkg/cataloger/rpm/test-fixtures/rpms
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }}

- name: Build cache key for go binary test-fixture blobs (for unit tests)
run: make go-binaries-fingerprint

Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,17 @@ go-binaries-fingerprint:
cd syft/pkg/cataloger/golang/test-fixtures/archs && \
make binaries.fingerprint

.PHONY: rpm-binaries-fingerprint
rpm-binaries-fingerprint:
$(call title,RPM binary test fixture fingerprint)
cd syft/pkg/cataloger/rpm/test-fixtures && \
make rpms.fingerprint

.PHONY: fixtures
fixtures:
$(call title,Generating test fixtures)
cd syft/pkg/cataloger/java/test-fixtures/java-builds && make
cd syft/pkg/cataloger/rpm/test-fixtures && make

.PHONY: generate-json-schema
generate-json-schema: ## Generate a new json schema
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ require (
github.com/google/go-containerregistry v0.11.0
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add
github.com/knqyf263/go-rpmdb v0.0.0-20220629110411-9a3bd2ebb923
github.com/sassoftware/go-rpmutils v0.2.0
github.com/sigstore/cosign v1.11.1
github.com/sigstore/rekor v0.11.0
github.com/sigstore/sigstore v1.4.0
Expand All @@ -79,6 +80,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/DataDog/zstd v1.4.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.5.2 h1:CkdGw2R/tZWmEbSypJVZG+3+2SAsDjJirfIrG/RbIVg=
github.com/CycloneDX/cyclonedx-go v0.5.2/go.mod h1:nQCiF4Tvrg5Ieu8qPhYMvzPGMu5I7fANZkrSsJjl5mg=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs=
Expand Down Expand Up @@ -1190,6 +1192,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
Expand Down Expand Up @@ -1611,6 +1614,8 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/sassoftware/go-rpmutils v0.1.1/go.mod h1:euhXULoBpvAxqrBHEyJS4Tsu3hHxUmQWNymxoJbzgUY=
github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 h1:sUNzanSKA9z/h8xXl+ZJoxIYZL0Qx306MmxqRrvUgr0=
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74/go.mod h1:YlB8wFIZmFLZ1JllNBfSURzz52fBxbliNgYALk1UDmk=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
Expand Down
11 changes: 7 additions & 4 deletions syft/pkg/cataloger/cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/php"
"github.com/anchore/syft/syft/pkg/cataloger/portage"
"github.com/anchore/syft/syft/pkg/cataloger/python"
"github.com/anchore/syft/syft/pkg/cataloger/rpmdb"
"github.com/anchore/syft/syft/pkg/cataloger/rpm"
"github.com/anchore/syft/syft/pkg/cataloger/ruby"
"github.com/anchore/syft/syft/pkg/cataloger/rust"
"github.com/anchore/syft/syft/pkg/cataloger/swift"
Expand Down Expand Up @@ -54,7 +54,8 @@ func ImageCatalogers(cfg Config) []Cataloger {
php.NewPHPComposerInstalledCataloger(),
javascript.NewJavascriptPackageCataloger(),
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
rpm.NewRpmdbCataloger(),
rpm.NewFileCataloger(),
java.NewJavaCataloger(cfg.Java()),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(),
Expand All @@ -73,7 +74,8 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
php.NewPHPComposerLockCataloger(),
javascript.NewJavascriptLockCataloger(),
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
rpm.NewRpmdbCataloger(),
rpm.NewFileCataloger(),
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
apkdb.NewApkdbCataloger(),
Expand All @@ -100,7 +102,8 @@ func AllCatalogers(cfg Config) []Cataloger {
javascript.NewJavascriptLockCataloger(),
javascript.NewJavascriptPackageCataloger(),
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
rpm.NewRpmdbCataloger(),
rpm.NewFileCataloger(),
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
apkdb.NewApkdbCataloger(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
/*
Package rpmdb provides a concrete Cataloger implementation for RPM "Package" DB files.
Package rpm provides a concrete DBCataloger implementation for RPM "Package" DB files
and a FileCataloger for RPM files.
*/
package rpmdb
package rpm

import (
"fmt"

"github.com/anchore/syft/internal"

"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)

const catalogerName = "rpmdb-cataloger"
const dbCatalogerName = "rpmdb-cataloger"

type Cataloger struct{}
type DBCataloger struct{}

// NewRpmdbCataloger returns a new RPM DB cataloger object.
func NewRpmdbCataloger() *Cataloger {
return &Cataloger{}
func NewRpmdbCataloger() *DBCataloger {
return &DBCataloger{}
}

// Name returns a string that uniquely describes a cataloger
func (c *Cataloger) Name() string {
return catalogerName
func (c *DBCataloger) Name() string {
return dbCatalogerName
}

// UsesExternalSources indicates that the rpmdb cataloger does not use external sources
func (c *Cataloger) UsesExternalSources() bool {
func (c *DBCataloger) UsesExternalSources() bool {
return false
}

// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm db installation.
func (c *Cataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
func (c *DBCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
fileMatches, err := resolver.FilesByGlob(pkg.RpmDBGlob)
if err != nil {
return nil, nil, fmt.Errorf("failed to find rpmdb's by glob: %w", err)
Expand Down
141 changes: 141 additions & 0 deletions syft/pkg/cataloger/rpm/file_cataloger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package rpm

import (
"fmt"
"strconv"
"strings"

rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"

"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)

type FileCataloger struct{}

// NewFileCataloger returns a new RPM file cataloger object.
func NewFileCataloger() *FileCataloger {
return &FileCataloger{}
}

// Name returns a string that uniquely describes a cataloger
func (c *FileCataloger) Name() string {
return "rpm-file-cataloger"
}

// UsesExternalSources indicates that the rpm file cataloger does not use external sources
func (c *FileCataloger) UsesExternalSources() bool {
return false
}

// Catalog is given an object to resolve file references and content, this function returns any discovered Packages after analyzing rpm files
func (c *FileCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
fileMatches, err := resolver.FilesByGlob("**/*.rpm")
if err != nil {
return nil, nil, fmt.Errorf("failed to find rpm files's by glob: %w", err)
}

var pkgs []pkg.Package
for _, location := range fileMatches {
contentReader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, err
}

rpm, err := rpmutils.ReadRpm(contentReader)
if err != nil {
return nil, nil, err
}

nevra, err := rpm.Header.GetNEVRA()
if err != nil {
return nil, nil, err
}

licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE)
sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM)
vendor, _ := rpm.Header.GetString(rpmutils.VENDOR)
digestAlgorithm := getDigestAlgorithm(rpm.Header)
size, _ := rpm.Header.InstalledSize()
files, _ := rpm.Header.GetFiles()

p := pkg.Package{
Name: nevra.Name,
Version: nevra.Version,
FoundBy: c.Name(),
Licenses: licenses,
Locations: source.NewLocationSet(location),
Type: pkg.RpmPkg,
MetadataType: pkg.RpmdbMetadataType,
Metadata: pkg.RpmdbMetadata{
Name: nevra.Name,
Version: nevra.Version,
Epoch: parseEpoch(nevra.Epoch),
Arch: nevra.Arch,
Release: nevra.Release,
SourceRpm: sourceRpm,
Vendor: vendor,
License: strings.Join(licenses, " AND "),
Size: int(size),
Files: mapFiles(files, digestAlgorithm),
},
}
p.SetID()
pkgs = append(pkgs, p)

internal.CloseAndLogError(contentReader, location.VirtualPath)
if err != nil {
return nil, nil, fmt.Errorf("unable to catalog rpm file=%+v: %w", location.RealPath, err)
}
}

return pkgs, nil, nil
}

func getDigestAlgorithm(header *rpmutils.RpmHeader) string {
digestAlgorithm, _ := header.GetString(rpmutils.FILEDIGESTALGO)
if digestAlgorithm != "" {
return digestAlgorithm
}
digestAlgorithms, _ := header.GetUint32s(rpmutils.FILEDIGESTALGO)
if len(digestAlgorithms) > 0 {
digestAlgo := int(digestAlgorithms[0])
return rpmutils.GetFileAlgoName(digestAlgo)
}
return ""
}

func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmdbFileRecord {
var out []pkg.RpmdbFileRecord
for _, f := range files {
digest := file.Digest{}
if f.Digest() != "" {
digest = file.Digest{
Algorithm: digestAlgorithm,
Value: f.Digest(),
}
}
out = append(out, pkg.RpmdbFileRecord{
Path: f.Name(),
Mode: pkg.RpmdbFileMode(f.Mode()),
Size: int(f.Size()),
Digest: digest,
UserName: f.UserName(),
GroupName: f.GroupName(),
Flags: rpmdb.FileFlags(f.Flags()).String(),
})
}
return out
}

func parseEpoch(epoch string) *int {
i, err := strconv.Atoi(epoch)
if err != nil {
return nil
}
return &i
}
Loading

0 comments on commit 5185972

Please sign in to comment.