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 }