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 }