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