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