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  }