github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/common/cyclonedxhelpers/licenses_test.go (about) 1 package cyclonedxhelpers 2 3 import ( 4 "testing" 5 6 "github.com/CycloneDX/cyclonedx-go" 7 "github.com/google/go-cmp/cmp" 8 "github.com/stretchr/testify/assert" 9 10 "github.com/anchore/syft/syft/license" 11 "github.com/anchore/syft/syft/pkg" 12 ) 13 14 func Test_encodeLicense(t *testing.T) { 15 tests := []struct { 16 name string 17 input pkg.Package 18 expected *cyclonedx.Licenses 19 }{ 20 { 21 name: "no licenses", 22 input: pkg.Package{}, 23 }, 24 { 25 name: "no SPDX licenses", 26 input: pkg.Package{ 27 Licenses: pkg.NewLicenseSet( 28 pkg.NewLicense("RandomLicense"), 29 ), 30 }, 31 expected: &cyclonedx.Licenses{ 32 { 33 License: &cyclonedx.License{ 34 Name: "RandomLicense", 35 }, 36 }, 37 }, 38 }, 39 { 40 name: "single SPDX ID and Non SPDX ID", 41 input: pkg.Package{ 42 Licenses: pkg.NewLicenseSet( 43 pkg.NewLicense("mit"), 44 pkg.NewLicense("FOOBAR"), 45 ), 46 }, 47 expected: &cyclonedx.Licenses{ 48 { 49 License: &cyclonedx.License{ 50 ID: "MIT", 51 }, 52 }, 53 { 54 License: &cyclonedx.License{ 55 Name: "FOOBAR", 56 }, 57 }, 58 }, 59 }, 60 { 61 name: "with complex SPDX license expression", 62 input: pkg.Package{ 63 Licenses: pkg.NewLicenseSet( 64 pkg.NewLicense("MIT AND GPL-3.0-only"), 65 ), 66 }, 67 expected: &cyclonedx.Licenses{ 68 { 69 Expression: "MIT AND GPL-3.0-only", 70 }, 71 }, 72 }, 73 { 74 name: "with multiple complex SPDX license expression", 75 input: pkg.Package{ 76 Licenses: pkg.NewLicenseSet( 77 pkg.NewLicense("MIT AND GPL-3.0-only"), 78 pkg.NewLicense("MIT AND GPL-3.0-only WITH Classpath-exception-2.0"), 79 ), 80 }, 81 expected: &cyclonedx.Licenses{ 82 { 83 Expression: "(MIT AND GPL-3.0-only) AND (MIT AND GPL-3.0-only WITH Classpath-exception-2.0)", 84 }, 85 }, 86 }, 87 { 88 name: "with multiple URLs and expressions", 89 input: pkg.Package{ 90 Licenses: pkg.NewLicenseSet( 91 pkg.NewLicenseFromURLs("MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"), 92 pkg.NewLicense("MIT AND GPL-3.0-only"), 93 pkg.NewLicenseFromURLs("FakeLicense", "htts://someurl.com"), 94 ), 95 }, 96 expected: &cyclonedx.Licenses{ 97 { 98 License: &cyclonedx.License{ 99 ID: "MIT", 100 URL: "https://opensource.org/licenses/MIT", 101 }, 102 }, 103 { 104 License: &cyclonedx.License{ 105 ID: "MIT", 106 URL: "https://spdx.org/licenses/MIT.html", 107 }, 108 }, 109 { 110 License: &cyclonedx.License{ 111 Name: "FakeLicense", 112 URL: "htts://someurl.com", 113 }, 114 }, 115 { 116 License: &cyclonedx.License{ 117 Name: "MIT AND GPL-3.0-only", 118 }, 119 }, 120 }, 121 }, 122 { 123 name: "with multiple values licenses are deduplicated", 124 input: pkg.Package{ 125 Licenses: pkg.NewLicenseSet( 126 pkg.NewLicense("Apache-2"), 127 pkg.NewLicense("Apache-2.0"), 128 ), 129 }, 130 expected: &cyclonedx.Licenses{ 131 { 132 License: &cyclonedx.License{ 133 ID: "Apache-2.0", 134 }, 135 }, 136 }, 137 }, 138 { 139 name: "with multiple URLs and single with no URLs", 140 input: pkg.Package{ 141 Licenses: pkg.NewLicenseSet( 142 pkg.NewLicense("MIT"), 143 pkg.NewLicenseFromURLs("MIT", "https://opensource.org/licenses/MIT", "https://spdx.org/licenses/MIT.html"), 144 pkg.NewLicense("MIT AND GPL-3.0-only"), 145 ), 146 }, 147 expected: &cyclonedx.Licenses{ 148 { 149 License: &cyclonedx.License{ 150 ID: "MIT", 151 URL: "https://opensource.org/licenses/MIT", 152 }, 153 }, 154 { 155 License: &cyclonedx.License{ 156 ID: "MIT", 157 URL: "https://spdx.org/licenses/MIT.html", 158 }, 159 }, 160 { 161 License: &cyclonedx.License{ 162 Name: "MIT AND GPL-3.0-only", 163 }, 164 }, 165 }, 166 }, 167 // TODO: do we drop the non SPDX ID license and do a single expression 168 // OR do we keep the non SPDX ID license and do multiple licenses where the complex 169 // expressions are set as the NAME field? 170 //{ 171 // name: "with multiple complex SPDX license expression and a non spdx id", 172 // input: pkg.Package{ 173 // Licenses: []pkg.License{ 174 // { 175 // SPDXExpression: "MIT AND GPL-3.0-only", 176 // }, 177 // { 178 // SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", 179 // }, 180 // { 181 // Value: "FOOBAR", 182 // }, 183 // }, 184 // }, 185 // expected: &cyclonedx.Licenses{ 186 // { 187 // Expression: "(MIT AND GPL-3.0-only) AND (MIT AND GPL-3.0-only WITH Classpath-exception-2.0)", 188 // }, 189 // }, 190 //}, 191 } 192 for _, test := range tests { 193 t.Run(test.name, func(t *testing.T) { 194 if d := cmp.Diff(test.expected, encodeLicenses(test.input)); d != "" { 195 t.Errorf("unexpected license (-want +got):\n%s", d) 196 } 197 }) 198 } 199 } 200 201 func TestDecodeLicenses(t *testing.T) { 202 tests := []struct { 203 name string 204 input *cyclonedx.Component 205 expected []pkg.License 206 }{ 207 { 208 name: "no licenses", 209 input: &cyclonedx.Component{}, 210 expected: []pkg.License{}, 211 }, 212 { 213 name: "no SPDX license ID or expression", 214 input: &cyclonedx.Component{ 215 Licenses: &cyclonedx.Licenses{ 216 { 217 License: &cyclonedx.License{ 218 Name: "RandomLicense", 219 }, 220 }, 221 }, 222 }, 223 expected: []pkg.License{ 224 { 225 Value: "RandomLicense", 226 // CycloneDX specification doesn't give a field for determining the license type 227 Type: license.Declared, 228 URLs: []string{}, 229 }, 230 }, 231 }, 232 { 233 name: "with SPDX license ID", 234 input: &cyclonedx.Component{ 235 Licenses: &cyclonedx.Licenses{ 236 { 237 License: &cyclonedx.License{ 238 ID: "MIT", 239 }, 240 }, 241 }, 242 }, 243 expected: []pkg.License{ 244 { 245 Value: "MIT", 246 SPDXExpression: "MIT", 247 Type: license.Declared, 248 URLs: []string{}, 249 }, 250 }, 251 }, 252 { 253 name: "with complex SPDX license expression", 254 input: &cyclonedx.Component{ 255 Licenses: &cyclonedx.Licenses{ 256 { 257 License: &cyclonedx.License{}, 258 Expression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", 259 }, 260 }, 261 }, 262 expected: []pkg.License{ 263 { 264 Value: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", 265 SPDXExpression: "MIT AND GPL-3.0-only WITH Classpath-exception-2.0", 266 Type: license.Declared, 267 URLs: []string{}, 268 }, 269 }, 270 }, 271 } 272 for _, test := range tests { 273 t.Run(test.name, func(t *testing.T) { 274 assert.Equal(t, test.expected, decodeLicenses(test.input)) 275 }) 276 } 277 }