mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 18:46:41 +01:00
fix: stop omitting redundantly parenthesized licenses in CDX formatter (#3517)
Previously, a bug in the formatter would cause SPDX expressions that were surrounded in redundant parentheses to be dropped instead of normalized. Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
This commit is contained in:
parent
561ed50c2d
commit
445142886e
@ -1,7 +1,6 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/CycloneDX/cyclonedx-go"
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
@ -158,40 +157,42 @@ func mergeSPDX(ex []string) string {
|
|||||||
// if the expression does not have balanced parens add them
|
// if the expression does not have balanced parens add them
|
||||||
if !strings.HasPrefix(e, "(") && !strings.HasSuffix(e, ")") {
|
if !strings.HasPrefix(e, "(") && !strings.HasSuffix(e, ")") {
|
||||||
e = "(" + e + ")"
|
e = "(" + e + ")"
|
||||||
candidate = append(candidate, e)
|
|
||||||
}
|
}
|
||||||
|
candidate = append(candidate, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(candidate) == 1 {
|
if len(candidate) == 1 {
|
||||||
return reduceOuter(strings.Join(candidate, " AND "))
|
return reduceOuter(candidate[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(candidate, " AND ")
|
return reduceOuter(strings.Join(candidate, " AND "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func reduceOuter(expression string) string {
|
func reduceOuter(expression string) string {
|
||||||
var (
|
expression = strings.TrimSpace(expression)
|
||||||
sb strings.Builder
|
|
||||||
openCount int
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, c := range expression {
|
// If the entire expression is wrapped in parentheses, check if they are redundant.
|
||||||
if string(c) == "(" && openCount > 0 {
|
if strings.HasPrefix(expression, "(") && strings.HasSuffix(expression, ")") {
|
||||||
_, _ = fmt.Fprintf(&sb, "%c", c)
|
trimmed := expression[1 : len(expression)-1]
|
||||||
|
if isBalanced(trimmed) {
|
||||||
|
return reduceOuter(trimmed) // Recursively reduce the trimmed expression.
|
||||||
}
|
}
|
||||||
if string(c) == "(" {
|
|
||||||
openCount++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if string(c) == ")" && openCount > 1 {
|
|
||||||
_, _ = fmt.Fprintf(&sb, "%c", c)
|
|
||||||
}
|
|
||||||
if string(c) == ")" {
|
|
||||||
openCount--
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(&sb, "%c", c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.String()
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBalanced(expression string) bool {
|
||||||
|
count := 0
|
||||||
|
for _, c := range expression {
|
||||||
|
if c == '(' {
|
||||||
|
count++
|
||||||
|
} else if c == ')' {
|
||||||
|
count--
|
||||||
|
if count < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count == 0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,6 +164,29 @@ func Test_encodeLicense(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "single parenthesized SPDX expression",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues("(MIT OR Apache-2.0)")...),
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{
|
||||||
|
Expression: "MIT OR Apache-2.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single license AND to parenthesized SPDX expression",
|
||||||
|
// (LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues("(LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later")...),
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{
|
||||||
|
Expression: "(LGPL-3.0-or-later OR GPL-2.0-or-later OR (LGPL-3.0-or-later AND GPL-2.0-or-later)) AND GFDL-1.3-invariants-or-later",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
// TODO: do we drop the non SPDX ID license and do a single expression
|
// TODO: do we drop the non SPDX ID license and do a single expression
|
||||||
// OR do we keep the non SPDX ID license and do multiple licenses where the complex
|
// OR do we keep the non SPDX ID license and do multiple licenses where the complex
|
||||||
// expressions are set as the NAME field?
|
// expressions are set as the NAME field?
|
||||||
|
|||||||
@ -20,6 +20,11 @@ func TestParseExpression(t *testing.T) {
|
|||||||
expression: "MIT OR Apache-2.0",
|
expression: "MIT OR Apache-2.0",
|
||||||
want: "MIT OR Apache-2.0",
|
want: "MIT OR Apache-2.0",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "accept redundant parentheses",
|
||||||
|
expression: "(MIT OR Apache-2.0)",
|
||||||
|
want: "(MIT OR Apache-2.0)",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid SPDX expression returns error",
|
name: "Invalid SPDX expression returns error",
|
||||||
expression: "MIT OR Apache-2.0 OR invalid",
|
expression: "MIT OR Apache-2.0 OR invalid",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user