/
githubmirror
/
trivy
Обзор
Документация
Войти
/
githubmirror
/
trivy
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
pkg/licensing/scanner.go
124 строки
4 KB
DmitriyLewen
fix(license): normalize licenses for PostAnalyzers (#9941)
29 дек 2025, 09:27
Не верифицирован
29 дек 2025, 09:27
11dd3fa
Код
Авторство
О чём код?
package licensing import ( "regexp" "strings" "github.com/samber/lo" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/licensing/expression" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/set" ) type Scanner struct { categories map[types.LicenseCategory]set.Set[string] } func NewScanner(categories map[types.LicenseCategory][]string) Scanner { return Scanner{ categories: lo.MapValues(categories, func(v []string, _ types.LicenseCategory) set.Set[string] { return set.NewCaseInsensitive(v...) }), } } func (s *Scanner) Scan(licenseName string) (types.LicenseCategory, string) { expr, err := expression.Normalize(licenseName, NormalizeLicenseExpression) if err != nil { return types.CategoryUnknown, "" } category := s.detectCategory(expr) return category, categoryToSeverity(category).String() } // detectCategory recursively parses license expression to detect correct license category: // For the simple expression - use category of license // For the compound expression: // - `AND` operator - use category with maximum severity // - `OR` operator - use category with minimum severity // - one of expression has `UNKNOWN` category - use `UNKNOWN` category func (s *Scanner) detectCategory(license expression.Expression) types.LicenseCategory { var category types.LicenseCategory switch e := license.(type) { case expression.SimpleExpr: category = s.licenseToCategory(e) case expression.CompoundExpr: // Detect license category for `WITH` operator as a simple expression if e.Conjunction() == expression.TokenWith { category = s.licenseToCategory(expression.SimpleExpr{ License: e.String(), }) break } left := s.detectCategory(e.Left()) right := s.detectCategory(e.Right()) if left == types.CategoryUnknown || right == types.CategoryUnknown { category = types.CategoryUnknown break } comparison := func(a, b types.LicenseCategory) bool { if e.Conjunction() == expression.TokenAnd { return int(categoryToSeverity(a)) > int(categoryToSeverity(b)) // Take the maximum severity for `AND` operator } return int(categoryToSeverity(a)) < int(categoryToSeverity(b)) // Take the minimum severity for `OR` operator } category = lo.MaxBy([]types.LicenseCategory{left, right}, comparison) } return category } // ScanTextLicense checks license names from `categories` as glob patterns and matches licenseText against those patterns. // If a match is found, it returns `unknown` category and severity. func (s *Scanner) ScanTextLicense(licenseText string) (types.LicenseCategory, string) { for cat, names := range s.categories { for name := range names.Iter() { n, ok := strings.CutPrefix(name, LicenseTextPrefix) if !ok { continue } match, err := regexp.MatchString(n, licenseText) if err != nil { log.WithPrefix("license").Debug("Failed to match license text", log.String("license_text", licenseText), log.Err(err)) continue } else if match { return cat, categoryToSeverity(cat).String() } } } return types.CategoryUnknown, dbTypes.SeverityUnknown.String() } func categoryToSeverity(category types.LicenseCategory) dbTypes.Severity { switch category { case types.CategoryForbidden: return dbTypes.SeverityCritical case types.CategoryRestricted: return dbTypes.SeverityHigh case types.CategoryReciprocal: return dbTypes.SeverityMedium case types.CategoryNotice, types.CategoryPermissive, types.CategoryUnencumbered: return dbTypes.SeverityLow } return dbTypes.SeverityUnknown } func (s *Scanner) licenseToCategory(se expression.SimpleExpr) types.LicenseCategory { normalizedNames := set.NewCaseInsensitive(se.String()) // The license name with suffix (e.g. AGPL-1.0-or-later) normalizedNames.Append(se.License) // Also accept the license name without suffix (e.g. AGPL-1.0) for category, names := range s.categories { if normalizedNames.Intersection(names).Size() > 0 { return category } } return types.CategoryUnknown }