github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/redhat/cataloger_test.go (about)

     1  package redhat
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp/cmpopts"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	_ "modernc.org/sqlite"
    12  
    13  	"github.com/anchore/syft/syft/artifact"
    14  	"github.com/anchore/syft/syft/file"
    15  	"github.com/anchore/syft/syft/pkg"
    16  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    17  )
    18  
    19  func Test_DBCataloger(t *testing.T) {
    20  	ctx := context.TODO()
    21  	dbLocation := file.NewLocation("/var/lib/rpm/rpmdb.sqlite")
    22  	locations := file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
    23  
    24  	basePkg := pkg.Package{
    25  		Name:      "basesystem",
    26  		Version:   "11-13.el9",
    27  		Type:      pkg.RpmPkg,
    28  		Locations: locations,
    29  		Licenses:  pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "Public Domain", dbLocation)),
    30  		FoundBy:   "rpm-db-cataloger",
    31  		PURL:      "pkg:rpm/basesystem@11-13.el9?arch=noarch&upstream=basesystem-11-13.el9.src.rpm",
    32  		Metadata: pkg.RpmDBEntry{
    33  			Name:       "basesystem",
    34  			Version:    "11",
    35  			Arch:       "noarch",
    36  			Release:    "13.el9",
    37  			SourceRpm:  "basesystem-11-13.el9.src.rpm",
    38  			Size:       0,
    39  			Vendor:     "Rocky Enterprise Software Foundation",
    40  			Signatures: mustParseSignatures(t, "RSA/SHA256, Wed May 11 11:12:32 2022, Key ID 702d426d350d275d"),
    41  			Provides:   []string{"basesystem"},
    42  			Requires: []string{
    43  				"filesystem",
    44  				"rpmlib(CompressedFileNames)",
    45  				"rpmlib(FileDigests)",
    46  				"rpmlib(PayloadFilesHavePrefix)",
    47  				"rpmlib(PayloadIsZstd)",
    48  				"setup",
    49  			},
    50  			ModularityLabel: strRef(""),
    51  		},
    52  	}
    53  	basePkg.SetID()
    54  
    55  	bashPkg := pkg.Package{
    56  		Name:      "bash",
    57  		Version:   "5.1.8-6.el9_1",
    58  		Type:      pkg.RpmPkg,
    59  		Locations: locations,
    60  		Licenses:  pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "GPLv3+", dbLocation)),
    61  		FoundBy:   "rpm-db-cataloger",
    62  		PURL:      "pkg:rpm/bash@5.1.8-6.el9_1?arch=x86_64&upstream=bash-5.1.8-6.el9_1.src.rpm",
    63  		Metadata: pkg.RpmDBEntry{
    64  			Name:            "bash",
    65  			Version:         "5.1.8",
    66  			Arch:            "x86_64",
    67  			Release:         "6.el9_1",
    68  			SourceRpm:       "bash-5.1.8-6.el9_1.src.rpm",
    69  			Size:            7738634,
    70  			Signatures:      mustParseSignatures(t, "RSA/SHA256, Mon Jan 23 22:49:22 2023, Key ID 702d426d350d275d"),
    71  			ModularityLabel: strRef(""),
    72  			Vendor:          "Rocky Enterprise Software Foundation",
    73  			Provides: []string{
    74  				"/bin/bash",
    75  				"/bin/sh",
    76  				"bash",
    77  				"bash(x86-64)",
    78  				"config(bash)",
    79  			},
    80  			Requires: []string{
    81  				"/usr/bin/sh",
    82  				"config(bash)",
    83  				"filesystem",
    84  				"libc.so.6()(64bit)",
    85  				"libc.so.6(GLIBC_2.11)(64bit)",
    86  				"libc.so.6(GLIBC_2.14)(64bit)",
    87  				"libc.so.6(GLIBC_2.15)(64bit)",
    88  				"libc.so.6(GLIBC_2.2.5)(64bit)",
    89  				"libc.so.6(GLIBC_2.25)(64bit)",
    90  				"libc.so.6(GLIBC_2.3)(64bit)",
    91  				"libc.so.6(GLIBC_2.3.4)(64bit)",
    92  				"libc.so.6(GLIBC_2.33)(64bit)",
    93  				"libc.so.6(GLIBC_2.34)(64bit)",
    94  				"libc.so.6(GLIBC_2.4)(64bit)",
    95  				"libc.so.6(GLIBC_2.8)(64bit)",
    96  				"libtinfo.so.6()(64bit)",
    97  				"rpmlib(BuiltinLuaScripts)",
    98  				"rpmlib(CompressedFileNames)",
    99  				"rpmlib(FileDigests)",
   100  				"rpmlib(PayloadFilesHavePrefix)",
   101  				"rpmlib(PayloadIsZstd)",
   102  				"rtld(GNU_HASH)",
   103  			},
   104  		},
   105  	}
   106  	bashPkg.SetID()
   107  
   108  	filesystemPkg := pkg.Package{
   109  		Name:      "filesystem",
   110  		Version:   "3.16-2.el9",
   111  		Type:      pkg.RpmPkg,
   112  		Locations: locations,
   113  		Licenses:  pkg.NewLicenseSet(pkg.NewLicenseFromLocationsWithContext(ctx, "Public Domain", dbLocation)),
   114  		FoundBy:   "rpm-db-cataloger",
   115  		PURL:      "pkg:rpm/filesystem@3.16-2.el9?arch=x86_64&upstream=filesystem-3.16-2.el9.src.rpm",
   116  		Metadata: pkg.RpmDBEntry{
   117  			Name:            "filesystem",
   118  			Version:         "3.16",
   119  			Arch:            "x86_64",
   120  			Release:         "2.el9",
   121  			SourceRpm:       "filesystem-3.16-2.el9.src.rpm",
   122  			Size:            106,
   123  			Signatures:      mustParseSignatures(t, "RSA/SHA256, Mon May 16 12:32:55 2022, Key ID 702d426d350d275d"),
   124  			ModularityLabel: strRef(""),
   125  			Vendor:          "Rocky Enterprise Software Foundation",
   126  			Provides: []string{
   127  				"filesystem",
   128  				"filesystem(x86-64)",
   129  				"filesystem-afs",
   130  			},
   131  			Requires: []string{
   132  				"/bin/sh",
   133  				"rpmlib(BuiltinLuaScripts)",
   134  				"rpmlib(CompressedFileNames)",
   135  				"rpmlib(FileDigests)",
   136  				"rpmlib(PayloadFilesHavePrefix)",
   137  				"rpmlib(PayloadIsZstd)",
   138  				"setup",
   139  			},
   140  		},
   141  	}
   142  	filesystemPkg.SetID()
   143  
   144  	expectedPackages := []pkg.Package{basePkg, bashPkg, filesystemPkg}
   145  
   146  	// Note that you'll see a cycle:
   147  	//   bash --(requires)--> filesystem
   148  	//   filesystem --(requires)--> bash
   149  	//
   150  	// This is not a bug!
   151  	//
   152  	// [root@c1a4773e8a8d /]# dnf repoquery --requires --resolve filesystem
   153  	//   bash-0:5.1.8-9.el9.aarch64
   154  	//   setup-0:2.13.7-10.el9.noarch
   155  	//
   156  	//[root@c1a4773e8a8d /]# dnf repoquery --requires --resolve bash
   157  	//  filesystem-0:3.16-2.el9.aarch64
   158  	//  glibc-0:2.34-100.el9.aarch64
   159  	//  ncurses-libs-0:6.2-10.20210508.el9.aarch64
   160  
   161  	expectedRelationships := []artifact.Relationship{
   162  		// though this is expressible in the RPM DB (package depends on itself), we do not allow for it in the SBOM
   163  		//{
   164  		//	From: bashPkg,
   165  		//	To:   bashPkg,
   166  		//	Type: artifact.DependencyOfRelationship,
   167  		//},
   168  		{
   169  			From: bashPkg,
   170  			To:   filesystemPkg,
   171  			Type: artifact.DependencyOfRelationship,
   172  		},
   173  		{
   174  			From: filesystemPkg,
   175  			To:   basePkg,
   176  			Type: artifact.DependencyOfRelationship,
   177  		},
   178  		{
   179  			From: filesystemPkg,
   180  			To:   bashPkg,
   181  			Type: artifact.DependencyOfRelationship,
   182  		},
   183  	}
   184  
   185  	pkgtest.NewCatalogTester().
   186  		WithImageResolver(t, "image-minimal").
   187  		IgnoreLocationLayer().                                               // this fixture can be rebuilt, thus the layer ID will change
   188  		WithCompareOptions(cmpopts.IgnoreFields(pkg.RpmDBEntry{}, "Files")). // this is rather long... ano not the point of the test
   189  		Expects(expectedPackages, expectedRelationships).
   190  		TestCataloger(t, NewDBCataloger())
   191  
   192  }
   193  
   194  func Test_DBCataloger_Globs(t *testing.T) {
   195  	tests := []struct {
   196  		name     string
   197  		fixture  string
   198  		expected []string
   199  	}{
   200  		{
   201  			name:    "obtain DB files",
   202  			fixture: "test-fixtures/glob-paths",
   203  			expected: []string{
   204  				"usr/share/rpm/Packages",
   205  				"usr/share/rpm/Packages.db",
   206  				"usr/share/rpm/rpmdb.sqlite",
   207  				"var/lib/rpm/Packages",
   208  				"var/lib/rpm/Packages.db",
   209  				"var/lib/rpm/rpmdb.sqlite",
   210  				"var/lib/rpmmanifest/container-manifest-2",
   211  				"usr/lib/sysimage/rpm/Packages",
   212  				"usr/lib/sysimage/rpm/Packages.db",
   213  				"usr/lib/sysimage/rpm/rpmdb.sqlite",
   214  			},
   215  		},
   216  	}
   217  
   218  	for _, test := range tests {
   219  		t.Run(test.name, func(t *testing.T) {
   220  			pkgtest.NewCatalogTester().
   221  				FromDirectory(t, test.fixture).
   222  				ExpectsResolverContentQueries(test.expected).
   223  				TestCataloger(t, NewDBCataloger())
   224  		})
   225  	}
   226  }
   227  
   228  func Test_RPMFileCataloger_Globs(t *testing.T) {
   229  	tests := []struct {
   230  		name     string
   231  		fixture  string
   232  		expected []string
   233  	}{
   234  		{
   235  			name:    "obtain rpm files",
   236  			fixture: "test-fixtures/glob-paths",
   237  			expected: []string{
   238  				"dive-0.10.0.rpm",
   239  			},
   240  		},
   241  	}
   242  
   243  	for _, test := range tests {
   244  		t.Run(test.name, func(t *testing.T) {
   245  			pkgtest.NewCatalogTester().
   246  				FromDirectory(t, test.fixture).
   247  				ExpectsResolverContentQueries(test.expected).
   248  				TestCataloger(t, NewArchiveCataloger())
   249  		})
   250  	}
   251  }
   252  
   253  func Test_denySelfReferences(t *testing.T) {
   254  	a := pkg.Package{
   255  		Name: "a",
   256  	}
   257  	a.SetID()
   258  	b := pkg.Package{
   259  		Name: "b",
   260  	}
   261  	b.SetID()
   262  	c := pkg.Package{
   263  		Name: "c",
   264  	}
   265  	c.SetID()
   266  
   267  	pkgs := []pkg.Package{a, b, c}
   268  
   269  	tests := []struct {
   270  		name              string
   271  		pkgs              []pkg.Package
   272  		rels              []artifact.Relationship
   273  		err               error
   274  		wantPkgs          int
   275  		wantRelationships int
   276  		wantErr           assert.ErrorAssertionFunc
   277  	}{
   278  		{
   279  			name: "no self references",
   280  			pkgs: pkgs,
   281  			rels: []artifact.Relationship{
   282  				{
   283  					From: a,
   284  					To:   b,
   285  					Type: artifact.DependencyOfRelationship,
   286  				},
   287  			},
   288  			wantPkgs:          3,
   289  			wantRelationships: 1,
   290  			wantErr:           assert.NoError,
   291  		},
   292  		{
   293  			name: "remove self references",
   294  			pkgs: pkgs,
   295  			rels: []artifact.Relationship{
   296  				{
   297  					From: a,
   298  					To:   a,
   299  					Type: artifact.DependencyOfRelationship,
   300  				},
   301  				{
   302  					From: a,
   303  					To:   b,
   304  					Type: artifact.DependencyOfRelationship,
   305  				},
   306  			},
   307  			wantPkgs:          3,
   308  			wantRelationships: 1,
   309  			wantErr:           assert.NoError,
   310  		},
   311  		{
   312  			name: "preserve errors",
   313  			pkgs: pkgs,
   314  			rels: []artifact.Relationship{
   315  				{
   316  					From: a,
   317  					To:   b,
   318  					Type: artifact.DependencyOfRelationship,
   319  				},
   320  			},
   321  			err:               errors.New("stop me!"),
   322  			wantPkgs:          3,
   323  			wantRelationships: 1,
   324  			wantErr:           assert.Error,
   325  		},
   326  	}
   327  	for _, tt := range tests {
   328  		t.Run(tt.name, func(t *testing.T) {
   329  			if tt.wantErr == nil {
   330  				tt.wantErr = assert.NoError
   331  			}
   332  
   333  			gotPkgs, gotRels, err := denySelfReferences(tt.pkgs, tt.rels, tt.err)
   334  
   335  			tt.wantErr(t, err)
   336  			assert.Len(t, gotPkgs, tt.wantPkgs)
   337  			assert.Len(t, gotRels, tt.wantRelationships)
   338  		})
   339  	}
   340  }
   341  
   342  func mustParseSignatures(t testing.TB, sigs ...string) []pkg.RpmSignature {
   343  	signatures, err := parseSignatures(sigs...)
   344  	require.NoError(t, err)
   345  	return signatures
   346  }