mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 07:29:00 -08:00
refactor(license): use goyacc for license parser (#3824)
This commit is contained in:
@@ -1,74 +1,80 @@
|
||||
package expression
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/licensing/expression/lexer"
|
||||
"github.com/aquasecurity/trivy/pkg/licensing/expression/parser"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Operator string
|
||||
|
||||
const (
|
||||
AND Operator = "AND"
|
||||
OR Operator = "OR"
|
||||
WITH Operator = "WITH"
|
||||
var (
|
||||
ErrInvalidExpression = xerrors.New("invalid expression error")
|
||||
)
|
||||
|
||||
func (o Operator) String() string {
|
||||
return fmt.Sprintf(" %s ", string(o))
|
||||
type NormalizeFunc func(license string) string
|
||||
|
||||
func parse(license string) (Expression, error) {
|
||||
l := NewLexer(strings.NewReader(license))
|
||||
if yyParse(l) != 0 {
|
||||
return nil, xerrors.Errorf("license parse error: %w", l.Err())
|
||||
} else if err := l.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l.result, nil
|
||||
}
|
||||
|
||||
func Normalize(license string, fn ...parser.NormalizeFunc) string {
|
||||
lex := lexer.New(license)
|
||||
licenseParser := parser.New(lex).RegisterNormalizeFunc(
|
||||
fn...,
|
||||
)
|
||||
expression, err := licenseParser.Parse()
|
||||
func Normalize(license string, fn ...NormalizeFunc) (string, error) {
|
||||
expr, err := parse(license)
|
||||
if err != nil {
|
||||
return license
|
||||
return "", xerrors.Errorf("license (%s) parse error: %w", license, err)
|
||||
}
|
||||
return licenseParser.Normalize(expression)
|
||||
expr = normalize(expr, fn...)
|
||||
|
||||
return expr.String(), nil
|
||||
}
|
||||
|
||||
func Join(elems []string, sep Operator) string {
|
||||
var licenses []string
|
||||
for i, license := range elems {
|
||||
var mid Operator
|
||||
if sep == AND {
|
||||
mid = OR
|
||||
} else if sep == OR {
|
||||
mid = AND
|
||||
func normalize(expr Expression, fn ...NormalizeFunc) Expression {
|
||||
switch e := expr.(type) {
|
||||
case SimpleExpr:
|
||||
for _, f := range fn {
|
||||
e.license = f(e.license)
|
||||
}
|
||||
|
||||
if i != 0 && strings.Contains(strings.ToUpper(license), mid.String()) {
|
||||
license = fmt.Sprintf("(%s)", license)
|
||||
}
|
||||
licenses = append(licenses, license)
|
||||
return e
|
||||
case CompoundExpr:
|
||||
e.left = normalize(e.left, fn...)
|
||||
e.right = normalize(e.right, fn...)
|
||||
e.conjunction.literal = strings.ToUpper(e.conjunction.literal) // e.g. "and" => "AND"
|
||||
return e
|
||||
}
|
||||
|
||||
return strings.Join(licenses, sep.String())
|
||||
return expr
|
||||
}
|
||||
|
||||
// NormalizeForSPDX is normalized license-id replace ' ' to '-'.
|
||||
// NormalizeForSPDX replaces ' ' to '-' in license-id.
|
||||
// SPDX license MUST NOT be white space between a license-id.
|
||||
// There MUST be white space on either side of the operator "WITH".
|
||||
// ref: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions
|
||||
func NormalizeForSPDX(name string) string {
|
||||
i := strings.Index(strings.ToUpper(name), WITH.String())
|
||||
if i < 0 {
|
||||
return strings.Replace(name, " ", "-", -1)
|
||||
func NormalizeForSPDX(s string) string {
|
||||
var b strings.Builder
|
||||
for _, c := range s {
|
||||
// idstring = 1*(ALPHA / DIGIT / "-" / "." )
|
||||
if isAlphabet(c) || unicode.IsNumber(c) || c == '-' || c == '.' {
|
||||
_, _ = b.WriteRune(c)
|
||||
} else if c == ':' {
|
||||
// TODO: Support DocumentRef
|
||||
_, _ = b.WriteRune(c)
|
||||
} else {
|
||||
// Replace invalid characters with '-'
|
||||
_, _ = b.WriteRune('-')
|
||||
}
|
||||
}
|
||||
|
||||
// Convert "WITH" expression split by " " to "-".
|
||||
// examples:
|
||||
// GPL-2+ with distribution exception => GPL-2+ with distribution-exception
|
||||
// GPL-2 with Linux-syscall-note exception => GPL-2 with Linux-syscall-note-exception
|
||||
// AFL 2.0 with Linux-syscall-note exception => AFL-2.0 with Linux-syscall-note-exception
|
||||
withSection := strings.Replace(name[i+len(WITH.String()):], " ", "-", -1)
|
||||
if i > 0 {
|
||||
return strings.Replace(name[:i], " ", "-", -1) + WITH.String() + withSection
|
||||
}
|
||||
return name
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func isAlphabet(r rune) bool {
|
||||
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user