Projects STRLCPY syft Commits f987f432
🤬
  • ■ ■ ■ ■ ■
    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   
  • ■ ■ ■ ■ ■ ■
    syft/pkg/cataloger/portage/license_test.go
    skipped 14 lines
    15 15   tests := []struct {
    16 16   name string
    17 17   license string
    18  - want []string
     18 + want string
    19 19   }{
    20 20   {
    21 21   name: "empty",
    22 22   license: "",
    23  - want: []string{},
     23 + want: "",
    24 24   },
    25 25   {
    26 26   name: "single",
    27 27   license: "GPL-2",
    28  - want: []string{"GPL-2"},
     28 + want: "GPL-2",
    29 29   },
    30 30   {
    31 31   name: "multiple",
    32 32   license: "GPL-2 GPL-3 ", // note the extra space
    33  - want: []string{"GPL-2", "GPL-3"},
     33 + want: "GPL-2 AND GPL-3",
    34 34   },
    35 35   // the following cases are NOT valid interpretations, but capture the behavior today.
    36 36   // when we follow up later with SPDX license expressions, this can be fixed then.
    37 37   {
    38 38   name: "license choices",
    39 39   license: "|| ( GPL-2 GPL-3 )",
    40  - // should allow for expression of "NONE OR (GPL-2 OR GPL-3)" or "GPL-2 OR GPL-3",
    41  - // I'm not certain which is correct (NONE isn't allowed, right?)
    42  - want: []string{"GPL-2", "GPL-3"},
     40 + want: "GPL-2 OR GPL-3",
    43 41   },
    44 42   {
    45  - name: "license choices with use flag",
    46  - license: "LGPL-2.1+ tools? ( GPL-2+ )",
    47  - want: []string{"GPL-2+", "LGPL-2.1+"}, // should allow for expression of "LGPL-2.1+ OR (LGPL-2.1+ AND GPL-2+)"
    48  - },
    49  - {
    50  - name: "license choices with unknown suffix",
    51  - license: "GPL-3+ LGPL-3+ || ( GPL-3+ libgcc libstdc++ gcc-runtime-library-exception-3.1 ) FDL-1.3+",
    52  - want: []string{
    53  - "FDL-1.3+", // is it right to include this? what does this represent since a useflag was not specified?
    54  - "GPL-3+",
    55  - "LGPL-3+",
    56  - "gcc-runtime-library-exception-3.1",
    57  - "libgcc",
    58  - "libstdc++",
    59  - },
     43 + name: "license choices with missing useflag suffix",
     44 + license: "GPL-3+ LGPL-3+ || ( GPL-3+ libgcc libstdc++ gcc-runtime-library-exception-3.1 ) FDL-1.3+", // no use flag so what do we do with FDL here?
     45 + want: "GPL-3+ AND LGPL-3+ AND (GPL-3+ OR libgcc OR libstdc++ OR gcc-runtime-library-exception-3.1 OR FDL-1.3+)",
     46 + // GPL-3+ AND LGPL-3+ AND (GPL-3+ OR libgcc OR libstdc++ OR gcc-runtime-library-exception-3.1)
     47 + //want: []string{
     48 + // "FDL-1.3+", // is it right to include this? what does this represent since a useflag was not specified?
     49 + // "GPL-3+",
     50 + // "LGPL-3+",
     51 + // "gcc-runtime-library-exception-3.1",
     52 + // "libgcc",
     53 + // "libstdc++",
     54 + //},
    60 55   },
    61 56   }
    62 57   for _, tt := range tests {
    63 58   t.Run(tt.name, func(t *testing.T) {
    64  - assert.Equalf(t, tt.want, extractLicenses(strings.NewReader(tt.license)), "extractLicenses(%v)", tt.license)
     59 + expression, _ := extractLicenses(strings.NewReader(tt.license))
     60 + assert.Equalf(t, tt.want, expression, "extractLicenses(%v)", tt.license)
    65 61   })
    66 62   }
    67 63  }
    skipped 1 lines
  • ■ ■ ■ ■ ■
    syft/pkg/cataloger/portage/parse_portage_contents.go
    skipped 104 lines
    105 105   return
    106 106   }
    107 107   
    108  - licenseCandidates := extractLicenses(licenseReader)
    109  - p.Licenses = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(*location, licenseCandidates...)...)
     108 + expression, licenseCandidates := extractLicenses(licenseReader)
     109 + if expression == "" {
     110 + p.Licenses = pkg.NewLicenseSet(pkg.NewLicensesFromLocation(*location, licenseCandidates...)...)
     111 + } else {
     112 + p.Licenses = pkg.NewLicenseSet(pkg.NewLicense(expression))
     113 + }
    110 114   p.Locations.Add(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
    111 115  }
    112 116   
    skipped 33 lines
Please wait...
Page is in error, reload to recover