github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/formats/common/spdxhelpers/to_gosbom_model_test.go (about)

     1  package spdxhelpers
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/nextlinux/gosbom/gosbom/artifact"
     7  	"github.com/nextlinux/gosbom/gosbom/file"
     8  	"github.com/nextlinux/gosbom/gosbom/pkg"
     9  	"github.com/nextlinux/gosbom/gosbom/source"
    10  	"github.com/spdx/tools-golang/spdx"
    11  	"github.com/spdx/tools-golang/spdx/v2/common"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestToGosbomModel(t *testing.T) {
    17  	sbom, err := ToGosbomModel(&spdx.Document{
    18  		SPDXVersion:                "1",
    19  		DataLicense:                "GPL",
    20  		SPDXIdentifier:             "id-doc-1",
    21  		DocumentName:               "docName",
    22  		DocumentNamespace:          "docNamespace",
    23  		ExternalDocumentReferences: nil,
    24  		DocumentComment:            "",
    25  		CreationInfo: &spdx.CreationInfo{
    26  			LicenseListVersion: "",
    27  			Created:            "",
    28  			CreatorComment:     "",
    29  		},
    30  		Packages: []*spdx.Package{
    31  			{
    32  				PackageName:            "pkg-1",
    33  				PackageSPDXIdentifier:  "id-pkg-1",
    34  				PackageVersion:         "5.4.3",
    35  				PackageLicenseDeclared: "",
    36  				PackageDescription:     "",
    37  				PackageExternalReferences: []*spdx.PackageExternalReference{
    38  					{
    39  						Category: "SECURITY",
    40  						Locator:  "cpe:2.3:a:pkg-1:pkg-1:5.4.3:*:*:*:*:*:*:*",
    41  						RefType:  "cpe23Type",
    42  					},
    43  					{
    44  						Category: "SECURITY",
    45  						Locator:  "cpe:2.3:a:pkg_1:pkg_1:5.4.3:*:*:*:*:*:*:*",
    46  						RefType:  "cpe23Type",
    47  					},
    48  					{
    49  						Category: "PACKAGE-MANAGER",
    50  						Locator:  "pkg:apk/alpine/pkg-1@5.4.3?arch=x86_64&upstream=p1-origin&distro=alpine-3.10.9",
    51  						RefType:  "purl",
    52  					},
    53  				},
    54  				Files: nil,
    55  			},
    56  			{
    57  				PackageName:            "pkg-2",
    58  				PackageSPDXIdentifier:  "id-pkg-2",
    59  				PackageVersion:         "7.3.1",
    60  				PackageLicenseDeclared: "",
    61  				PackageDescription:     "",
    62  				PackageExternalReferences: []*spdx.PackageExternalReference{
    63  					{
    64  						Category: "SECURITY",
    65  						Locator:  "cpe:2.3:a:pkg-2:pkg-2:7.3.1:*:*:*:*:*:*:*",
    66  						RefType:  "cpe23Type",
    67  					},
    68  					{
    69  						Category: "SECURITY",
    70  						Locator:  "cpe:2.3:a:pkg_2:pkg_2:7.3.1:*:*:*:*:*:*:*",
    71  						RefType:  "cpe23Type",
    72  					},
    73  					{
    74  						Category: "SECURITY",
    75  						Locator:  "cpe:2.3:a:pkg-2:pkg_2:7.3.1:*:*:*:*:*:*:*",
    76  						RefType:  "cpe23Type",
    77  					},
    78  					{
    79  						Category: "PACKAGE-MANAGER",
    80  						Locator:  "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=p2-origin@9.1.3&distro=debian-3.10.9",
    81  						RefType:  "purl",
    82  					},
    83  				},
    84  				Files: nil,
    85  			},
    86  		},
    87  		Relationships: []*spdx.Relationship{},
    88  	})
    89  
    90  	assert.NoError(t, err)
    91  
    92  	assert.NotNil(t, sbom)
    93  
    94  	pkgs := sbom.Artifacts.Packages.Sorted()
    95  
    96  	assert.Len(t, pkgs, 2)
    97  
    98  	p1 := pkgs[0]
    99  	assert.Equal(t, p1.Name, "pkg-1")
   100  	assert.Equal(t, p1.MetadataType, pkg.ApkMetadataType)
   101  	p1meta := p1.Metadata.(pkg.ApkMetadata)
   102  	assert.Equal(t, p1meta.OriginPackage, "p1-origin")
   103  	assert.Len(t, p1.CPEs, 2)
   104  
   105  	p2 := pkgs[1]
   106  	assert.Equal(t, p2.Name, "pkg-2")
   107  	assert.Equal(t, p2.MetadataType, pkg.DpkgMetadataType)
   108  	p2meta := p2.Metadata.(pkg.DpkgMetadata)
   109  	assert.Equal(t, p2meta.Source, "p2-origin")
   110  	assert.Equal(t, p2meta.SourceVersion, "9.1.3")
   111  	assert.Len(t, p2.CPEs, 3)
   112  }
   113  
   114  func Test_extractMetadata(t *testing.T) {
   115  	oneTwoThreeFour := 1234
   116  	tests := []struct {
   117  		pkg      spdx.Package
   118  		metaType pkg.MetadataType
   119  		meta     interface{}
   120  	}{
   121  		{
   122  			pkg: spdx.Package{
   123  				PackageName:    "SomeDebPkg",
   124  				PackageVersion: "43.1.235",
   125  				PackageExternalReferences: []*spdx.PackageExternalReference{
   126  					{
   127  						Category: "PACKAGE-MANAGER",
   128  						Locator:  "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=somedebpkg-origin@9.1.3&distro=debian-3.10.9",
   129  						RefType:  "purl",
   130  					},
   131  				},
   132  			},
   133  			metaType: pkg.DpkgMetadataType,
   134  			meta: pkg.DpkgMetadata{
   135  				Package:       "SomeDebPkg",
   136  				Source:        "somedebpkg-origin",
   137  				Version:       "43.1.235",
   138  				SourceVersion: "9.1.3",
   139  				Architecture:  "x86_64",
   140  			},
   141  		},
   142  		{
   143  			pkg: spdx.Package{
   144  				PackageName:    "SomeApkPkg",
   145  				PackageVersion: "3.2.9",
   146  				PackageExternalReferences: []*spdx.PackageExternalReference{
   147  					{
   148  						Category: "PACKAGE-MANAGER",
   149  						Locator:  "pkg:apk/alpine/pkg-2@7.3.1?arch=x86_64&upstream=apk-origin@9.1.3&distro=alpine-3.10.9",
   150  						RefType:  "purl",
   151  					},
   152  				},
   153  			},
   154  			metaType: pkg.ApkMetadataType,
   155  			meta: pkg.ApkMetadata{
   156  				Package:       "SomeApkPkg",
   157  				OriginPackage: "apk-origin",
   158  				Version:       "3.2.9",
   159  				Architecture:  "x86_64",
   160  			},
   161  		},
   162  		{
   163  			pkg: spdx.Package{
   164  				PackageName:    "SomeRpmPkg",
   165  				PackageVersion: "13.2.79",
   166  				PackageExternalReferences: []*spdx.PackageExternalReference{
   167  					{
   168  						Category: "PACKAGE-MANAGER",
   169  						Locator:  "pkg:rpm/pkg-2@7.3.1?arch=x86_64&epoch=1234&upstream=some-rpm-origin-1.16.3&distro=alpine-3.10.9",
   170  						RefType:  "purl",
   171  					},
   172  				},
   173  			},
   174  			metaType: pkg.RpmMetadataType,
   175  			meta: pkg.RpmMetadata{
   176  				Name:      "SomeRpmPkg",
   177  				Version:   "13.2.79",
   178  				Epoch:     &oneTwoThreeFour,
   179  				Arch:      "x86_64",
   180  				Release:   "",
   181  				SourceRpm: "some-rpm-origin-1.16.3",
   182  			},
   183  		},
   184  	}
   185  
   186  	for _, test := range tests {
   187  		t.Run(test.pkg.PackageName, func(t *testing.T) {
   188  			info := extractPkgInfo(&test.pkg)
   189  			metaType, meta := extractMetadata(&test.pkg, info)
   190  			assert.Equal(t, test.metaType, metaType)
   191  			assert.EqualValues(t, test.meta, meta)
   192  		})
   193  	}
   194  }
   195  
   196  func TestExtractSourceFromNamespaces(t *testing.T) {
   197  	tests := []struct {
   198  		namespace string
   199  		expected  source.Scheme
   200  	}{
   201  		{
   202  			namespace: "https://anchore.com/gosbom/file/d42b01d0-7325-409b-b03f-74082935c4d3",
   203  			expected:  source.FileScheme,
   204  		},
   205  		{
   206  			namespace: "https://anchore.com/gosbom/image/d42b01d0-7325-409b-b03f-74082935c4d3",
   207  			expected:  source.ImageScheme,
   208  		},
   209  		{
   210  			namespace: "https://anchore.com/gosbom/dir/d42b01d0-7325-409b-b03f-74082935c4d3",
   211  			expected:  source.DirectoryScheme,
   212  		},
   213  		{
   214  			namespace: "https://another-host/blob/123",
   215  			expected:  source.UnknownScheme,
   216  		},
   217  		{
   218  			namespace: "bla bla",
   219  			expected:  source.UnknownScheme,
   220  		},
   221  		{
   222  			namespace: "",
   223  			expected:  source.UnknownScheme,
   224  		},
   225  	}
   226  
   227  	for _, tt := range tests {
   228  		require.Equal(t, tt.expected, extractSchemeFromNamespace(tt.namespace))
   229  	}
   230  }
   231  
   232  func TestH1Digest(t *testing.T) {
   233  	tests := []struct {
   234  		name           string
   235  		pkg            spdx.Package
   236  		expectedDigest string
   237  	}{
   238  		{
   239  			name: "valid h1digest",
   240  			pkg: spdx.Package{
   241  				PackageName:    "github.com/googleapis/gnostic",
   242  				PackageVersion: "v0.5.5",
   243  				PackageExternalReferences: []*spdx.PackageExternalReference{
   244  					{
   245  						Category: "PACKAGE-MANAGER",
   246  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   247  						RefType:  "purl",
   248  					},
   249  				},
   250  				PackageChecksums: []spdx.Checksum{
   251  					{
   252  						Algorithm: spdx.SHA256,
   253  						Value:     "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c",
   254  					},
   255  				},
   256  			},
   257  			expectedDigest: "h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=",
   258  		},
   259  		{
   260  			name: "invalid h1digest algorithm",
   261  			pkg: spdx.Package{
   262  				PackageName:    "github.com/googleapis/gnostic",
   263  				PackageVersion: "v0.5.5",
   264  				PackageExternalReferences: []*spdx.PackageExternalReference{
   265  					{
   266  						Category: "PACKAGE-MANAGER",
   267  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   268  						RefType:  "purl",
   269  					},
   270  				},
   271  				PackageChecksums: []spdx.Checksum{
   272  					{
   273  						Algorithm: spdx.SHA1,
   274  						Value:     "f5f1c0b4ad2e0dfa6f79eaaaa3586411925c16f61702208ddd4bad2fc17dc47c",
   275  					},
   276  				},
   277  			},
   278  			expectedDigest: "",
   279  		},
   280  		{
   281  			name: "invalid h1digest digest",
   282  			pkg: spdx.Package{
   283  				PackageName:    "github.com/googleapis/gnostic",
   284  				PackageVersion: "v0.5.5",
   285  				PackageExternalReferences: []*spdx.PackageExternalReference{
   286  					{
   287  						Category: "PACKAGE-MANAGER",
   288  						Locator:  "pkg:golang/github.com/googleapis/gnostic@v0.5.5",
   289  						RefType:  "purl",
   290  					},
   291  				},
   292  				PackageChecksums: []spdx.Checksum{
   293  					{
   294  						Algorithm: spdx.SHA256,
   295  						Value:     "",
   296  					},
   297  				},
   298  			},
   299  			expectedDigest: "",
   300  		},
   301  	}
   302  
   303  	for _, test := range tests {
   304  		t.Run(test.name, func(t *testing.T) {
   305  			p := toGosbomPackage(&test.pkg)
   306  			require.Equal(t, pkg.GolangBinMetadataType, p.MetadataType)
   307  			meta := p.Metadata.(pkg.GolangBinMetadata)
   308  			require.Equal(t, test.expectedDigest, meta.H1Digest)
   309  		})
   310  	}
   311  }
   312  
   313  func Test_toGosbomRelationships(t *testing.T) {
   314  	type args struct {
   315  		spdxIDMap map[string]interface{}
   316  		doc       *spdx.Document
   317  	}
   318  
   319  	pkg1 := pkg.Package{
   320  		Name:    "github.com/googleapis/gnostic",
   321  		Version: "v0.5.5",
   322  	}
   323  	pkg1.SetID()
   324  
   325  	pkg2 := pkg.Package{
   326  		Name:    "rfc3339",
   327  		Version: "1.2",
   328  		Type:    pkg.RpmPkg,
   329  	}
   330  	pkg2.SetID()
   331  
   332  	pkg3 := pkg.Package{
   333  		Name:    "rfc3339",
   334  		Version: "1.2",
   335  		Type:    pkg.PythonPkg,
   336  	}
   337  	pkg3.SetID()
   338  
   339  	loc1 := file.NewLocationFromCoordinates(file.Coordinates{
   340  		RealPath:     "/somewhere/real",
   341  		FileSystemID: "abc",
   342  	})
   343  
   344  	tests := []struct {
   345  		name string
   346  		args args
   347  		want []artifact.Relationship
   348  	}{
   349  		{
   350  			name: "evident-by relationship",
   351  			args: args{
   352  				spdxIDMap: map[string]interface{}{
   353  					string(toSPDXID(pkg1)): &pkg1,
   354  					string(toSPDXID(loc1)): &loc1,
   355  				},
   356  				doc: &spdx.Document{
   357  					Relationships: []*spdx.Relationship{
   358  						{
   359  							RefA: common.DocElementID{
   360  								ElementRefID: toSPDXID(pkg1),
   361  							},
   362  							RefB: common.DocElementID{
   363  								ElementRefID: toSPDXID(loc1),
   364  							},
   365  							Relationship:        spdx.RelationshipOther,
   366  							RelationshipComment: "evident-by: indicates the package's existence is evident by the given file",
   367  						},
   368  					},
   369  				},
   370  			},
   371  			want: []artifact.Relationship{
   372  				{
   373  					From: pkg1,
   374  					To:   loc1,
   375  					Type: artifact.EvidentByRelationship,
   376  				},
   377  			},
   378  		},
   379  		{
   380  			name: "ownership-by-file-overlap relationship",
   381  			args: args{
   382  				spdxIDMap: map[string]interface{}{
   383  					string(toSPDXID(pkg2)): &pkg2,
   384  					string(toSPDXID(pkg3)): &pkg3,
   385  				},
   386  				doc: &spdx.Document{
   387  					Relationships: []*spdx.Relationship{
   388  						{
   389  							RefA: common.DocElementID{
   390  								ElementRefID: toSPDXID(pkg2),
   391  							},
   392  							RefB: common.DocElementID{
   393  								ElementRefID: toSPDXID(pkg3),
   394  							},
   395  							Relationship:        spdx.RelationshipOther,
   396  							RelationshipComment: "ownership-by-file-overlap: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by",
   397  						},
   398  					},
   399  				},
   400  			},
   401  			want: []artifact.Relationship{
   402  				{
   403  					From: pkg2,
   404  					To:   pkg3,
   405  					Type: artifact.OwnershipByFileOverlapRelationship,
   406  				},
   407  			},
   408  		},
   409  	}
   410  	for _, tt := range tests {
   411  		t.Run(tt.name, func(t *testing.T) {
   412  			actual := toGosbomRelationships(tt.args.spdxIDMap, tt.args.doc)
   413  			require.Len(t, actual, len(tt.want))
   414  			for i := range actual {
   415  				require.Equal(t, tt.want[i].From.ID(), actual[i].From.ID())
   416  				require.Equal(t, tt.want[i].To.ID(), actual[i].To.ID())
   417  				require.Equal(t, tt.want[i].Type, actual[i].Type)
   418  			}
   419  		})
   420  	}
   421  }