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  }