github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/common/spdxhelpers/license.go (about) 1 package spdxhelpers 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "strings" 7 8 "github.com/anchore/syft/syft/license" 9 "github.com/anchore/syft/syft/pkg" 10 "github.com/lineaje-labs/syft/internal/spdxlicense" 11 ) 12 13 func License(p pkg.Package) (concluded, declared string) { 14 // source: https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license 15 // The options to populate this field are limited to: 16 // A valid SPDX License Expression as defined in Appendix IV; 17 // NONE, if the SPDX file creator concludes there is no license available for this package; or 18 // NOASSERTION if: 19 // (i) the SPDX file creator has attempted to but cannot reach a reasonable objective determination; 20 // (ii) the SPDX file creator has made no attempt to determine this field; or 21 // (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so). 22 23 if p.Licenses.Empty() { 24 return NOASSERTION, NOASSERTION 25 } 26 27 // take all licenses and assume an AND expression; 28 // for information about license expressions see: 29 // https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/ 30 pc, pd := parseLicenses(p.Licenses.ToSlice()) 31 32 return joinLicenses(pc), joinLicenses(pd) 33 } 34 35 func joinLicenses(licenses []spdxLicense) string { 36 if len(licenses) == 0 { 37 return NOASSERTION 38 } 39 40 var newLicenses []string 41 42 for _, l := range licenses { 43 v := l.id 44 // check if license does not start or end with parens 45 if !strings.HasPrefix(v, "(") && !strings.HasSuffix(v, ")") { 46 // if license contains AND, OR, or WITH, then wrap in parens 47 if strings.Contains(v, " AND ") || 48 strings.Contains(v, " OR ") || 49 strings.Contains(v, " WITH ") { 50 newLicenses = append(newLicenses, "("+v+")") 51 continue 52 } 53 } 54 newLicenses = append(newLicenses, v) 55 } 56 57 return strings.Join(newLicenses, " AND ") 58 } 59 60 type spdxLicense struct { 61 id string 62 value string 63 } 64 65 func parseLicenses(raw []pkg.License) (concluded, declared []spdxLicense) { 66 for _, l := range raw { 67 if l.Value == "" { 68 continue 69 } 70 71 candidate := spdxLicense{} 72 if l.SPDXExpression != "" { 73 candidate.id = l.SPDXExpression 74 } else { 75 // we did not find a valid SPDX license ID so treat as separate license 76 if len(l.Value) <= 64 { 77 // if the license text is less than the size of the hash, 78 // just use it directly so the id is more readable 79 candidate.id = spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value) 80 } else { 81 hash := sha256.Sum256([]byte(l.Value)) 82 candidate.id = fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash) 83 } 84 candidate.value = l.Value 85 } 86 87 switch l.Type { 88 case license.Concluded: 89 concluded = append(concluded, candidate) 90 case license.Declared: 91 declared = append(declared, candidate) 92 } 93 } 94 95 return concluded, declared 96 }