■ ■ ■ ■ ■ ■
syft/pkg/cataloger/portage/license.go
| skipped 2 lines |
3 | 3 | | import ( |
4 | 4 | | "bufio" |
5 | 5 | | "io" |
6 | | - | "sort" |
7 | 6 | | "strings" |
8 | 7 | | |
9 | 8 | | "github.com/anchore/syft/internal" |
| 9 | + | "github.com/anchore/syft/syft/license" |
10 | 10 | | ) |
11 | 11 | | |
12 | 12 | | // the licenses files seems to conform to a custom format that is common to gentoo packages. |
| skipped 12 lines |
25 | 25 | | // |
26 | 26 | | // this does not conform to SPDX license expressions, which would be a great enhancement in the future. |
27 | 27 | | |
28 | | - | func extractLicenses(reader io.Reader) []string { |
| 28 | + | // extractLicenses attempts to parse the license field into a valid SPDX license expression |
| 29 | + | // if the expression cannot be parsed, the extract licenses will be returned as a slice of string |
| 30 | + | func extractLicenses(reader io.Reader) (spdxExpression string, licenses []string) { |
29 | 31 | | findings := internal.NewStringSet() |
30 | 32 | | scanner := bufio.NewScanner(reader) |
31 | 33 | | scanner.Split(bufio.ScanWords) |
| 34 | + | var ( |
| 35 | + | mandatoryLicenses, conditionalLicenses, useflagLicenses []string |
| 36 | + | pipe bool |
| 37 | + | useflag bool |
| 38 | + | ) |
| 39 | + | |
32 | 40 | | for scanner.Scan() { |
33 | 41 | | token := scanner.Text() |
| 42 | + | if token == "||" { |
| 43 | + | pipe = true |
| 44 | + | continue |
| 45 | + | } |
| 46 | + | // useflag |
| 47 | + | if strings.Contains(token, "?") { |
| 48 | + | useflag = true |
| 49 | + | continue |
| 50 | + | } |
34 | 51 | | if !strings.ContainsAny(token, "()|?") { |
35 | | - | findings.Add(token) |
| 52 | + | switch { |
| 53 | + | case useflag: |
| 54 | + | useflagLicenses = append(useflagLicenses, token) |
| 55 | + | case pipe: |
| 56 | + | conditionalLicenses = append(conditionalLicenses, token) |
| 57 | + | default: |
| 58 | + | mandatoryLicenses = append(mandatoryLicenses, token) |
| 59 | + | } |
36 | 60 | | } |
37 | 61 | | } |
38 | | - | licenses := findings.ToSlice() |
39 | | - | sort.Strings(licenses) |
40 | 62 | | |
41 | | - | return licenses |
| 63 | + | findings.Add(mandatoryLicenses...) |
| 64 | + | findings.Add(conditionalLicenses...) |
| 65 | + | findings.Add(useflagLicenses...) |
| 66 | + | |
| 67 | + | var mandatoryStatement, conditionalStatement string |
| 68 | + | // attempt to build valid SPDX license expression |
| 69 | + | if len(mandatoryLicenses) > 0 { |
| 70 | + | mandatoryStatement = strings.Join(mandatoryLicenses, " AND ") |
| 71 | + | } |
| 72 | + | if len(conditionalLicenses) > 0 { |
| 73 | + | conditionalStatement = strings.Join(conditionalLicenses, " OR ") |
| 74 | + | } |
| 75 | + | |
| 76 | + | if mandatoryStatement != "" && conditionalStatement != "" { |
| 77 | + | spdxExpression = mandatoryStatement + " AND (" + conditionalStatement + ")" |
| 78 | + | } else if mandatoryStatement != "" { |
| 79 | + | spdxExpression = mandatoryStatement |
| 80 | + | } else if conditionalStatement != "" { |
| 81 | + | spdxExpression = conditionalStatement |
| 82 | + | } |
| 83 | + | |
| 84 | + | if _, err := license.ParseExpression(spdxExpression); err != nil { |
| 85 | + | // the expression could not be parsed, return the licenses as a slice of strings |
| 86 | + | licenses = findings.ToSlice() |
| 87 | + | return |
| 88 | + | } |
| 89 | + | return spdxExpression, nil |
42 | 90 | | } |
43 | 91 | | |