github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/format/common/cyclonedxhelpers/to_format_model_test.go (about)

     1  package cyclonedxhelpers
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/CycloneDX/cyclonedx-go"
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/anchore/syft/syft/artifact"
    13  	"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
    14  	"github.com/anchore/syft/syft/linux"
    15  	"github.com/anchore/syft/syft/pkg"
    16  	"github.com/anchore/syft/syft/sbom"
    17  	"github.com/anchore/syft/syft/source"
    18  )
    19  
    20  func Test_formatCPE(t *testing.T) {
    21  	tests := []struct {
    22  		cpe      string
    23  		expected string
    24  	}{
    25  		{
    26  			cpe:      "cpe:2.3:o:amazon:amazon_linux:2",
    27  			expected: "cpe:2.3:o:amazon:amazon_linux:2:*:*:*:*:*:*:*",
    28  		},
    29  		{
    30  			cpe:      "cpe:/o:opensuse:leap:15.2",
    31  			expected: "cpe:2.3:o:opensuse:leap:15.2:*:*:*:*:*:*:*",
    32  		},
    33  		{
    34  			cpe:      "invalid-cpe",
    35  			expected: "",
    36  		},
    37  	}
    38  
    39  	for _, test := range tests {
    40  		t.Run(test.cpe, func(t *testing.T) {
    41  			out := formatCPE(test.cpe)
    42  			assert.Equal(t, test.expected, out)
    43  		})
    44  	}
    45  }
    46  
    47  func Test_relationships(t *testing.T) {
    48  	p1 := pkg.Package{
    49  		Name: "p1",
    50  	}
    51  
    52  	p2 := pkg.Package{
    53  		Name: "p2",
    54  	}
    55  
    56  	p3 := pkg.Package{
    57  		Name: "p3",
    58  	}
    59  
    60  	p4 := pkg.Package{
    61  		Name: "p4",
    62  	}
    63  
    64  	for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
    65  		p.PURL = fmt.Sprintf("pkg:generic/%s@%s", p.Name, p.Name)
    66  		p.SetID()
    67  	}
    68  
    69  	tests := []struct {
    70  		name     string
    71  		sbom     sbom.SBOM
    72  		expected *[]cyclonedx.Dependency
    73  	}{
    74  		{
    75  			name: "package dependencyOf relationships output as dependencies",
    76  			sbom: sbom.SBOM{
    77  				Artifacts: sbom.Artifacts{
    78  					Packages: pkg.NewCollection(p1, p2, p3, p4),
    79  				},
    80  				Relationships: []artifact.Relationship{
    81  					{
    82  						From: p2,
    83  						To:   p1,
    84  						Type: artifact.DependencyOfRelationship,
    85  					},
    86  					{
    87  						From: p3,
    88  						To:   p1,
    89  						Type: artifact.DependencyOfRelationship,
    90  					},
    91  					{
    92  						From: p4,
    93  						To:   p2,
    94  						Type: artifact.DependencyOfRelationship,
    95  					},
    96  				},
    97  			},
    98  			expected: &[]cyclonedx.Dependency{
    99  				{
   100  					Ref: helpers.DeriveBomRef(p1),
   101  					Dependencies: &[]string{
   102  						helpers.DeriveBomRef(p2),
   103  						helpers.DeriveBomRef(p3),
   104  					},
   105  				},
   106  				{
   107  					Ref: helpers.DeriveBomRef(p2),
   108  					Dependencies: &[]string{
   109  						helpers.DeriveBomRef(p4),
   110  					},
   111  				},
   112  			},
   113  		},
   114  		{
   115  			name: "package contains relationships not output",
   116  			sbom: sbom.SBOM{
   117  				Artifacts: sbom.Artifacts{
   118  					Packages: pkg.NewCollection(p1, p2, p3),
   119  				},
   120  				Relationships: []artifact.Relationship{
   121  					{
   122  						From: p2,
   123  						To:   p1,
   124  						Type: artifact.ContainsRelationship,
   125  					},
   126  					{
   127  						From: p3,
   128  						To:   p1,
   129  						Type: artifact.ContainsRelationship,
   130  					},
   131  				},
   132  			},
   133  			expected: nil,
   134  		},
   135  	}
   136  
   137  	for _, test := range tests {
   138  		t.Run(test.name, func(t *testing.T) {
   139  			cdx := ToFormatModel(test.sbom)
   140  			got := cdx.Dependencies
   141  			require.Equal(t, test.expected, got)
   142  		})
   143  	}
   144  }
   145  
   146  func Test_toBomDescriptor(t *testing.T) {
   147  	type args struct {
   148  		name        string
   149  		version     string
   150  		srcMetadata source.Description
   151  	}
   152  	tests := []struct {
   153  		name string
   154  		args args
   155  		want *cyclonedx.Metadata
   156  	}{
   157  		{
   158  			name: "with image labels source metadata",
   159  			args: args{
   160  				name:    "test-image",
   161  				version: "1.0.0",
   162  				srcMetadata: source.Description{
   163  					Metadata: source.ImageMetadata{
   164  						Labels: map[string]string{
   165  							"key1": "value1",
   166  						},
   167  					},
   168  				},
   169  			},
   170  			want: &cyclonedx.Metadata{
   171  				Timestamp:  "",
   172  				Lifecycles: nil,
   173  				Tools: &cyclonedx.ToolsChoice{
   174  					Components: &[]cyclonedx.Component{
   175  						{
   176  							Type:    cyclonedx.ComponentTypeApplication,
   177  							Author:  "anchore",
   178  							Name:    "test-image",
   179  							Version: "1.0.0",
   180  						},
   181  					},
   182  				},
   183  				Authors: nil,
   184  				Component: &cyclonedx.Component{
   185  					BOMRef:             "",
   186  					MIMEType:           "",
   187  					Type:               "container",
   188  					Supplier:           nil,
   189  					Author:             "",
   190  					Publisher:          "",
   191  					Group:              "",
   192  					Name:               "",
   193  					Version:            "",
   194  					Description:        "",
   195  					Scope:              "",
   196  					Hashes:             nil,
   197  					Licenses:           nil,
   198  					Copyright:          "",
   199  					CPE:                "",
   200  					PackageURL:         "",
   201  					SWID:               nil,
   202  					Modified:           nil,
   203  					Pedigree:           nil,
   204  					ExternalReferences: nil,
   205  					Properties:         nil,
   206  					Components:         nil,
   207  					Evidence:           nil,
   208  					ReleaseNotes:       nil,
   209  				},
   210  				Manufacture: nil,
   211  				Supplier:    nil,
   212  				Licenses:    nil,
   213  				Properties: &[]cyclonedx.Property{
   214  					{
   215  						Name:  "syft:image:labels:key1",
   216  						Value: "value1",
   217  					},
   218  				}},
   219  		},
   220  	}
   221  	for _, tt := range tests {
   222  		t.Run(tt.name, func(t *testing.T) {
   223  			subject := toBomDescriptor(tt.args.name, tt.args.version, tt.args.srcMetadata)
   224  
   225  			require.NotEmpty(t, subject.Component.BOMRef)
   226  			subject.Timestamp = "" // not under test
   227  
   228  			require.NotNil(t, subject.Component)
   229  			require.NotEmpty(t, subject.Component.BOMRef)
   230  			subject.Component.BOMRef = "" // not under test
   231  
   232  			if d := cmp.Diff(tt.want, subject); d != "" {
   233  				t.Errorf("toBomDescriptor() mismatch (-want +got):\n%s", d)
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  func Test_toOsComponent(t *testing.T) {
   240  	tests := []struct {
   241  		name     string
   242  		release  linux.Release
   243  		expected cyclonedx.Component
   244  	}{
   245  		{
   246  			name: "basic os component",
   247  			release: linux.Release{
   248  				ID:        "myLinux",
   249  				VersionID: "myVersion",
   250  			},
   251  			expected: cyclonedx.Component{
   252  				BOMRef:  "os:myLinux@myVersion",
   253  				Type:    cyclonedx.ComponentTypeOS,
   254  				Name:    "myLinux",
   255  				Version: "myVersion",
   256  				SWID: &cyclonedx.SWID{
   257  					TagID:   "myLinux",
   258  					Name:    "myLinux",
   259  					Version: "myVersion",
   260  				},
   261  				Properties: &[]cyclonedx.Property{
   262  					{
   263  						Name:  "syft:distro:id",
   264  						Value: "myLinux",
   265  					},
   266  					{
   267  						Name:  "syft:distro:versionID",
   268  						Value: "myVersion",
   269  					},
   270  				},
   271  			},
   272  		},
   273  	}
   274  
   275  	for _, test := range tests {
   276  		t.Run(test.name, func(t *testing.T) {
   277  			gotSlice := toOSComponent(&test.release)
   278  			require.Len(t, gotSlice, 1)
   279  			got := gotSlice[0]
   280  			require.Equal(t, test.expected, got)
   281  		})
   282  	}
   283  }
   284  
   285  func Test_toOSBomRef(t *testing.T) {
   286  	tests := []struct {
   287  		name      string
   288  		osName    string
   289  		osVersion string
   290  		expected  string
   291  	}{
   292  		{
   293  			name:      "no name or version specified",
   294  			osName:    "",
   295  			osVersion: "",
   296  			expected:  "os:unknown",
   297  		},
   298  		{
   299  			name:      "no version specified",
   300  			osName:    "my-name",
   301  			osVersion: "",
   302  			expected:  "os:my-name",
   303  		},
   304  		{
   305  			name:      "no name specified",
   306  			osName:    "",
   307  			osVersion: "my-version",
   308  			expected:  "os:unknown",
   309  		},
   310  		{
   311  			name:      "both name and version specified",
   312  			osName:    "my-name",
   313  			osVersion: "my-version",
   314  			expected:  "os:my-name@my-version",
   315  		},
   316  	}
   317  	for _, test := range tests {
   318  		t.Run(test.name, func(t *testing.T) {
   319  			got := toOSBomRef(test.osName, test.osVersion)
   320  			require.Equal(t, test.expected, got)
   321  		})
   322  	}
   323  }