github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/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.ApkDBEntry{
    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:package:metadataType", Value: "apk-db-entry"},
    54  				{Name: "syft:location:0:path", Value: "test"},
    55  				{Name: "syft:metadata:gitCommitOfApkPort", Value: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479"},
    56  				{Name: "syft:metadata:installedSize", Value: "4096"},
    57  				{Name: "syft:metadata:originPackage", Value: "libc-dev"},
    58  				{Name: "syft:metadata:provides:0", Value: "so:libc.so.1"},
    59  				{Name: "syft:metadata:pullChecksum", Value: "Q1p78yvTLG094tHE1+dToJGbmYzQE="},
    60  				{Name: "syft:metadata:pullDependencies:0", Value: "musl-utils"},
    61  				{Name: "syft:metadata:size", Value: "0"},
    62  			},
    63  		},
    64  		{
    65  			name: "from dpkg",
    66  			input: pkg.Package{
    67  				Metadata: pkg.DpkgDBEntry{
    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: "dpkg-db-entry"},
    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  				Metadata: pkg.GolangBinaryBuildinfoEntry{
    93  					GoCompiledVersion: "1.17",
    94  					Architecture:      "amd64",
    95  					H1Digest:          "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
    96  				},
    97  			},
    98  			expected: []cyclonedx.Property{
    99  				{Name: "syft:package:language", Value: pkg.Go.String()},
   100  				{Name: "syft:package:metadataType", Value: "go-module-buildinfo-entry"},
   101  				{Name: "syft:package:type", Value: "go-module"},
   102  				{Name: "syft:metadata:architecture", Value: "amd64"},
   103  				{Name: "syft:metadata:goCompiledVersion", Value: "1.17"},
   104  				{Name: "syft: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  				Metadata: pkg.GolangModuleEntry{
   115  					H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
   116  				},
   117  			},
   118  			expected: []cyclonedx.Property{
   119  				{Name: "syft:package:language", Value: pkg.Go.String()},
   120  				{Name: "syft:package:metadataType", Value: "go-module-entry"},
   121  				{Name: "syft:package:type", Value: "go-module"},
   122  				{Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
   123  			},
   124  		},
   125  		{
   126  			name: "from rpm",
   127  			input: pkg.Package{
   128  				Name:    "dive",
   129  				Version: "0.9.2-1",
   130  				Type:    pkg.RpmPkg,
   131  				Metadata: pkg.RpmDBEntry{
   132  					Name:      "dive",
   133  					Epoch:     &epoch,
   134  					Arch:      "x86_64",
   135  					Release:   "1",
   136  					Version:   "0.9.2",
   137  					SourceRpm: "dive-0.9.2-1.src.rpm",
   138  					Size:      12406784,
   139  					Vendor:    "",
   140  					Files:     []pkg.RpmFileRecord{},
   141  				},
   142  			},
   143  			expected: []cyclonedx.Property{
   144  				{Name: "syft:package:metadataType", Value: "rpm-db-entry"},
   145  				{Name: "syft:package:type", Value: "rpm"},
   146  				{Name: "syft:metadata:epoch", Value: "2"},
   147  				{Name: "syft:metadata:release", Value: "1"},
   148  				{Name: "syft:metadata:size", Value: "12406784"},
   149  				{Name: "syft:metadata:sourceRpm", Value: "dive-0.9.2-1.src.rpm"},
   150  			},
   151  		},
   152  	}
   153  	for _, test := range tests {
   154  		t.Run(test.name, func(t *testing.T) {
   155  			c := encodeComponent(test.input)
   156  			if test.expected == nil {
   157  				if c.Properties != nil {
   158  					t.Fatalf("expected no properties, got: %+v", *c.Properties)
   159  				}
   160  				return
   161  			}
   162  			assert.ElementsMatch(t, test.expected, *c.Properties)
   163  		})
   164  	}
   165  }
   166  
   167  func Test_encodeCompomentType(t *testing.T) {
   168  	tests := []struct {
   169  		name string
   170  		pkg  pkg.Package
   171  		want cyclonedx.Component
   172  	}{
   173  		{
   174  			name: "non-binary package",
   175  			pkg: pkg.Package{
   176  				Name:    "pkg1",
   177  				Version: "1.9.2",
   178  				Type:    pkg.GoModulePkg,
   179  			},
   180  			want: cyclonedx.Component{
   181  				Name:    "pkg1",
   182  				Version: "1.9.2",
   183  				Type:    cyclonedx.ComponentTypeLibrary,
   184  				Properties: &[]cyclonedx.Property{
   185  					{
   186  						Name:  "syft:package:type",
   187  						Value: "go-module",
   188  					},
   189  				},
   190  			},
   191  		},
   192  		{
   193  			name: "non-binary package",
   194  			pkg: pkg.Package{
   195  				Name:    "pkg1",
   196  				Version: "3.1.2",
   197  				Type:    pkg.BinaryPkg,
   198  			},
   199  			want: cyclonedx.Component{
   200  				Name:    "pkg1",
   201  				Version: "3.1.2",
   202  				Type:    cyclonedx.ComponentTypeApplication,
   203  				Properties: &[]cyclonedx.Property{
   204  					{
   205  						Name:  "syft:package:type",
   206  						Value: "binary",
   207  					},
   208  				},
   209  			},
   210  		},
   211  	}
   212  	for _, tt := range tests {
   213  		t.Run(tt.name, func(t *testing.T) {
   214  			tt.pkg.ID()
   215  			p := encodeComponent(tt.pkg)
   216  			assert.Equal(t, tt.want, p)
   217  		})
   218  	}
   219  }
   220  
   221  func Test_deriveBomRef(t *testing.T) {
   222  	pkgWithPurl := pkg.Package{
   223  		Name:    "django",
   224  		Version: "1.11.1",
   225  		PURL:    "pkg:pypi/django@1.11.1",
   226  	}
   227  	pkgWithPurl.SetID()
   228  
   229  	pkgWithOutPurl := pkg.Package{
   230  		Name:    "django",
   231  		Version: "1.11.1",
   232  		PURL:    "",
   233  	}
   234  	pkgWithOutPurl.SetID()
   235  
   236  	pkgWithBadPurl := pkg.Package{
   237  		Name:    "django",
   238  		Version: "1.11.1",
   239  		PURL:    "pkg:pyjango@1.11.1",
   240  	}
   241  	pkgWithBadPurl.SetID()
   242  
   243  	tests := []struct {
   244  		name string
   245  		pkg  pkg.Package
   246  		want string
   247  	}{
   248  		{
   249  			name: "use pURL-id hybrid",
   250  			pkg:  pkgWithPurl,
   251  			want: fmt.Sprintf("pkg:pypi/django@1.11.1?package-id=%s", pkgWithPurl.ID()),
   252  		},
   253  		{
   254  			name: "fallback to ID when pURL is invalid",
   255  			pkg:  pkgWithBadPurl,
   256  			want: string(pkgWithBadPurl.ID()),
   257  		},
   258  		{
   259  			name: "fallback to ID when pURL is missing",
   260  			pkg:  pkgWithOutPurl,
   261  			want: string(pkgWithOutPurl.ID()),
   262  		},
   263  	}
   264  	for _, tt := range tests {
   265  		t.Run(tt.name, func(t *testing.T) {
   266  			tt.pkg.ID()
   267  			assert.Equal(t, tt.want, deriveBomRef(tt.pkg))
   268  		})
   269  	}
   270  }
   271  
   272  func Test_decodeComponent(t *testing.T) {
   273  	tests := []struct {
   274  		name         string
   275  		component    cyclonedx.Component
   276  		wantLanguage pkg.Language
   277  		wantMetadata any
   278  	}{
   279  		{
   280  			name: "derive language from pURL if missing",
   281  			component: cyclonedx.Component{
   282  				Name:       "ch.qos.logback/logback-classic",
   283  				Version:    "1.2.3",
   284  				PackageURL: "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
   285  				Type:       "library",
   286  				BOMRef:     "pkg:maven/ch.qos.logback/logback-classic@1.2.3",
   287  			},
   288  			wantLanguage: pkg.Java,
   289  		},
   290  		{
   291  			name: "handle RpmdbMetadata type without properties",
   292  			component: cyclonedx.Component{
   293  				Name:       "acl",
   294  				Version:    "2.2.53-1.el8",
   295  				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",
   296  				Type:       "library",
   297  				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",
   298  				Properties: &[]cyclonedx.Property{
   299  					{
   300  						Name:  "syft:package:metadataType",
   301  						Value: "RpmdbMetadata",
   302  					},
   303  				},
   304  			},
   305  			wantMetadata: pkg.RpmDBEntry{},
   306  		},
   307  		{
   308  			name: "handle RpmdbMetadata type with properties",
   309  			component: cyclonedx.Component{
   310  				Name:       "acl",
   311  				Version:    "2.2.53-1.el8",
   312  				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",
   313  				Type:       "library",
   314  				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",
   315  				Properties: &[]cyclonedx.Property{
   316  					{
   317  						Name:  "syft:package:metadataType",
   318  						Value: "RpmDBMetadata",
   319  					},
   320  					{
   321  						Name:  "syft:metadata:release",
   322  						Value: "some-release",
   323  					},
   324  				},
   325  			},
   326  			wantMetadata: pkg.RpmDBEntry{
   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.wantMetadata != nil {
   339  				assert.Truef(t, reflect.DeepEqual(tt.wantMetadata, p.Metadata), "metadata should match: %+v != %+v", tt.wantMetadata, p.Metadata)
   340  			}
   341  			if tt.wantMetadata == nil && tt.wantLanguage == "" {
   342  				t.Fatal("this is a useless test, please remove it")
   343  			}
   344  		})
   345  	}
   346  }