github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/formats/common/cyclonedxhelpers/component_test.go (about) 1 package cyclonedxhelpers 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 8 "github.com/CycloneDX/cyclonedx-go" 9 "github.com/stretchr/testify/assert" 10 11 "github.com/anchore/syft/syft/file" 12 "github.com/anchore/syft/syft/pkg" 13 ) 14 15 func Test_encodeComponentProperties(t *testing.T) { 16 epoch := 2 17 tests := []struct { 18 name string 19 input pkg.Package 20 expected *[]cyclonedx.Property 21 }{ 22 { 23 name: "no metadata", 24 input: pkg.Package{}, 25 expected: nil, 26 }, 27 { 28 name: "from apk", 29 input: pkg.Package{ 30 FoundBy: "cataloger", 31 Locations: file.NewLocationSet( 32 file.NewLocationFromCoordinates(file.Coordinates{RealPath: "test"}), 33 ), 34 Metadata: pkg.ApkMetadata{ 35 Package: "libc-utils", 36 OriginPackage: "libc-dev", 37 Maintainer: "Natanael Copa <ncopa@alpinelinux.org>", 38 Version: "0.7.2-r0", 39 Architecture: "x86_64", 40 URL: "http://alpinelinux.org", 41 Description: "Meta package to pull in correct libc", 42 Size: 0, 43 InstalledSize: 4096, 44 Dependencies: []string{"musl-utils"}, 45 Provides: []string{"so:libc.so.1"}, 46 Checksum: "Q1p78yvTLG094tHE1+dToJGbmYzQE=", 47 GitCommit: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479", 48 Files: []pkg.ApkFileRecord{}, 49 }, 50 }, 51 expected: &[]cyclonedx.Property{ 52 {Name: "syft:package:foundBy", Value: "cataloger"}, 53 {Name: "syft:location:0:path", Value: "test"}, 54 {Name: "syft:metadata:gitCommitOfApkPort", Value: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479"}, 55 {Name: "syft:metadata:installedSize", Value: "4096"}, 56 {Name: "syft:metadata:originPackage", Value: "libc-dev"}, 57 {Name: "syft:metadata:provides:0", Value: "so:libc.so.1"}, 58 {Name: "syft:metadata:pullChecksum", Value: "Q1p78yvTLG094tHE1+dToJGbmYzQE="}, 59 {Name: "syft:metadata:pullDependencies:0", Value: "musl-utils"}, 60 {Name: "syft:metadata:size", Value: "0"}, 61 }, 62 }, 63 { 64 name: "from dpkg", 65 input: pkg.Package{ 66 MetadataType: pkg.DpkgMetadataType, 67 Metadata: pkg.DpkgMetadata{ 68 Package: "tzdata", 69 Version: "2020a-0+deb10u1", 70 Source: "tzdata-dev", 71 SourceVersion: "1.0", 72 Architecture: "all", 73 InstalledSize: 3036, 74 Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>", 75 Files: []pkg.DpkgFileRecord{}, 76 }, 77 }, 78 expected: &[]cyclonedx.Property{ 79 {Name: "syft:package:metadataType", Value: "DpkgMetadata"}, 80 {Name: "syft:metadata:installedSize", Value: "3036"}, 81 {Name: "syft:metadata:source", Value: "tzdata-dev"}, 82 {Name: "syft:metadata:sourceVersion", Value: "1.0"}, 83 }, 84 }, 85 { 86 name: "from go bin", 87 input: pkg.Package{ 88 Name: "golang.org/x/net", 89 Version: "v0.0.0-20211006190231-62292e806868", 90 Language: pkg.Go, 91 Type: pkg.GoModulePkg, 92 MetadataType: pkg.GolangBinMetadataType, 93 Metadata: pkg.GolangBinMetadata{ 94 GoCompiledVersion: "1.17", 95 Architecture: "amd64", 96 H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=", 97 }, 98 }, 99 expected: &[]cyclonedx.Property{ 100 {Name: "syft:package:language", Value: pkg.Go.String()}, 101 {Name: "syft:package:metadataType", Value: "GolangBinMetadata"}, 102 {Name: "syft:package:type", Value: "go-module"}, 103 {Name: "syft:metadata:architecture", Value: "amd64"}, 104 {Name: "syft:metadata:goCompiledVersion", Value: "1.17"}, 105 {Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="}, 106 }, 107 }, 108 { 109 name: "from go mod", 110 input: pkg.Package{ 111 Name: "golang.org/x/net", 112 Version: "v0.0.0-20211006190231-62292e806868", 113 Language: pkg.Go, 114 Type: pkg.GoModulePkg, 115 MetadataType: pkg.GolangModMetadataType, 116 Metadata: pkg.GolangModMetadata{ 117 H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=", 118 }, 119 }, 120 expected: &[]cyclonedx.Property{ 121 {Name: "syft:package:language", Value: pkg.Go.String()}, 122 {Name: "syft:package:metadataType", Value: "GolangModMetadata"}, 123 {Name: "syft:package:type", Value: "go-module"}, 124 {Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="}, 125 }, 126 }, 127 { 128 name: "from rpm", 129 input: pkg.Package{ 130 Name: "dive", 131 Version: "0.9.2-1", 132 Type: pkg.RpmPkg, 133 MetadataType: pkg.RpmMetadataType, 134 Metadata: pkg.RpmMetadata{ 135 Name: "dive", 136 Epoch: &epoch, 137 Arch: "x86_64", 138 Release: "1", 139 Version: "0.9.2", 140 SourceRpm: "dive-0.9.2-1.src.rpm", 141 Size: 12406784, 142 Vendor: "", 143 Files: []pkg.RpmdbFileRecord{}, 144 }, 145 }, 146 expected: &[]cyclonedx.Property{ 147 {Name: "syft:package:metadataType", Value: "RpmMetadata"}, 148 {Name: "syft:package:type", Value: "rpm"}, 149 {Name: "syft:metadata:epoch", Value: "2"}, 150 {Name: "syft:metadata:release", Value: "1"}, 151 {Name: "syft:metadata:size", Value: "12406784"}, 152 {Name: "syft:metadata:sourceRpm", Value: "dive-0.9.2-1.src.rpm"}, 153 }, 154 }, 155 } 156 for _, test := range tests { 157 t.Run(test.name, func(t *testing.T) { 158 c := encodeComponent(test.input) 159 assert.Equal(t, test.expected, c.Properties) 160 }) 161 } 162 } 163 164 func Test_encodeCompomentType(t *testing.T) { 165 tests := []struct { 166 name string 167 pkg pkg.Package 168 want cyclonedx.Component 169 }{ 170 { 171 name: "non-binary package", 172 pkg: pkg.Package{ 173 Name: "pkg1", 174 Version: "1.9.2", 175 Type: pkg.GoModulePkg, 176 }, 177 want: cyclonedx.Component{ 178 Name: "pkg1", 179 Version: "1.9.2", 180 Type: cyclonedx.ComponentTypeLibrary, 181 Properties: &[]cyclonedx.Property{ 182 { 183 Name: "syft:package:type", 184 Value: "go-module", 185 }, 186 }, 187 }, 188 }, 189 { 190 name: "non-binary package", 191 pkg: pkg.Package{ 192 Name: "pkg1", 193 Version: "3.1.2", 194 Type: pkg.BinaryPkg, 195 }, 196 want: cyclonedx.Component{ 197 Name: "pkg1", 198 Version: "3.1.2", 199 Type: cyclonedx.ComponentTypeApplication, 200 Properties: &[]cyclonedx.Property{ 201 { 202 Name: "syft:package:type", 203 Value: "binary", 204 }, 205 }, 206 }, 207 }, 208 } 209 for _, tt := range tests { 210 t.Run(tt.name, func(t *testing.T) { 211 tt.pkg.ID() 212 p := encodeComponent(tt.pkg) 213 assert.Equal(t, tt.want, p) 214 }) 215 } 216 } 217 218 func Test_deriveBomRef(t *testing.T) { 219 pkgWithPurl := pkg.Package{ 220 Name: "django", 221 Version: "1.11.1", 222 PURL: "pkg:pypi/django@1.11.1", 223 } 224 pkgWithPurl.SetID() 225 226 pkgWithOutPurl := pkg.Package{ 227 Name: "django", 228 Version: "1.11.1", 229 PURL: "", 230 } 231 pkgWithOutPurl.SetID() 232 233 pkgWithBadPurl := pkg.Package{ 234 Name: "django", 235 Version: "1.11.1", 236 PURL: "pkg:pyjango@1.11.1", 237 } 238 pkgWithBadPurl.SetID() 239 240 tests := []struct { 241 name string 242 pkg pkg.Package 243 want string 244 }{ 245 { 246 name: "use pURL-id hybrid", 247 pkg: pkgWithPurl, 248 want: fmt.Sprintf("pkg:pypi/django@1.11.1?package-id=%s", pkgWithPurl.ID()), 249 }, 250 { 251 name: "fallback to ID when pURL is invalid", 252 pkg: pkgWithBadPurl, 253 want: string(pkgWithBadPurl.ID()), 254 }, 255 { 256 name: "fallback to ID when pURL is missing", 257 pkg: pkgWithOutPurl, 258 want: string(pkgWithOutPurl.ID()), 259 }, 260 } 261 for _, tt := range tests { 262 t.Run(tt.name, func(t *testing.T) { 263 tt.pkg.ID() 264 assert.Equal(t, tt.want, deriveBomRef(tt.pkg)) 265 }) 266 } 267 } 268 269 func Test_decodeComponent(t *testing.T) { 270 tests := []struct { 271 name string 272 component cyclonedx.Component 273 wantLanguage pkg.Language 274 wantMetadataType pkg.MetadataType 275 wantMetadata interface{} 276 }{ 277 { 278 name: "derive language from pURL if missing", 279 component: cyclonedx.Component{ 280 Name: "ch.qos.logback/logback-classic", 281 Version: "1.2.3", 282 PackageURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3", 283 Type: "library", 284 BOMRef: "pkg:maven/ch.qos.logback/logback-classic@1.2.3", 285 }, 286 wantLanguage: pkg.Java, 287 }, 288 { 289 name: "handle RpmdbMetadata type without properties", 290 component: cyclonedx.Component{ 291 Name: "acl", 292 Version: "2.2.53-1.el8", 293 PackageURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8", 294 Type: "library", 295 BOMRef: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8", 296 Properties: &[]cyclonedx.Property{ 297 { 298 Name: "syft:package:metadataType", 299 Value: "RpmdbMetadata", 300 }, 301 }, 302 }, 303 wantMetadataType: pkg.RpmMetadataType, 304 wantMetadata: pkg.RpmMetadata{}, 305 }, 306 { 307 name: "handle RpmdbMetadata type with properties", 308 component: cyclonedx.Component{ 309 Name: "acl", 310 Version: "2.2.53-1.el8", 311 PackageURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8", 312 Type: "library", 313 BOMRef: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8", 314 Properties: &[]cyclonedx.Property{ 315 { 316 Name: "syft:package:metadataType", 317 Value: "RpmMetadata", 318 }, 319 { 320 Name: "syft:metadata:release", 321 Value: "some-release", 322 }, 323 }, 324 }, 325 wantMetadataType: pkg.RpmMetadataType, 326 wantMetadata: pkg.RpmMetadata{ 327 Release: "some-release", 328 }, 329 }, 330 } 331 332 for _, tt := range tests { 333 t.Run(tt.name, func(t *testing.T) { 334 p := decodeComponent(&tt.component) 335 if tt.wantLanguage != "" { 336 assert.Equal(t, tt.wantLanguage, p.Language) 337 } 338 if tt.wantMetadataType != "" { 339 assert.Equal(t, tt.wantMetadataType, p.MetadataType) 340 } 341 if tt.wantMetadata != nil { 342 assert.Truef(t, reflect.DeepEqual(tt.wantMetadata, p.Metadata), "metadata should match: %+v != %+v", tt.wantMetadata, p.Metadata) 343 } 344 }) 345 } 346 }