diff --git a/grant/evalutation/license_evalutation.go b/grant/evalutation/license_evalutation.go index e817a54..d1fde7a 100644 --- a/grant/evalutation/license_evalutation.go +++ b/grant/evalutation/license_evalutation.go @@ -5,7 +5,74 @@ import ( ) func NewLicenseEvaluations(ec EvaluationConfig, c grant.Case) LicenseEvaluations { - panic("not implemented") + evaluations := make([]LicenseEvaluation, 0) + // TODO: probably want to use some concurrency here + for _, sb := range c.SBOMS { + for pkg := range sb.Artifacts.Packages.Enumerate() { + grantPkg := convertSyftPackage(pkg) + // since we use syft as a library to generate the sbom we need to convert its packages/licenses to grant types + if len(grantPkg.Licenses) == 0 { + evaluations = append(evaluations, LicenseEvaluation{ + License: grant.License{}, + Package: grantPkg, + Policy: ec.Policy, + Reason: []Reason{ReasonNoLicenseFound}, + Pass: true, + }) + continue + } + + for _, l := range grantPkg.Licenses { + if !l.IsSPDX() { + // TODO: check if the config wants us to check for non-SPDX licenses + } + if ec.Policy.IsDenied(l) { + evaluations = append(evaluations, LicenseEvaluation{ + License: l, + Package: grantPkg, + Policy: ec.Policy, + Reason: []Reason{ReasonLicenseDenied}, + Pass: false, + }) + continue + } + // otherwise, the license is allowed + evaluations = append(evaluations, LicenseEvaluation{ + License: l, + Package: grantPkg, + Policy: ec.Policy, + Reason: []Reason{ReasonLicenseAllowed}, + Pass: true, + }) + } + } + } + + for _, l := range c.Licenses { + if !l.IsSPDX() { + // TODO: check if the config wants us to check for non-SPDX licenses + } + if ec.Policy.IsDenied(l) { + evaluations = append(evaluations, LicenseEvaluation{ + License: l, + Package: nil, + Policy: ec.Policy, + Reason: []Reason{ReasonLicenseDenied}, + Pass: false, + }) + continue + } + // otherwise, the license is allowed + evaluations = append(evaluations, LicenseEvaluation{ + License: l, + Package: nil, + Policy: ec.Policy, + Reason: []Reason{ReasonLicenseAllowed}, + Pass: true, + }) + } + + return evaluations } type LicenseEvaluations []LicenseEvaluation @@ -18,10 +85,10 @@ type LicenseEvaluation struct { Package *grant.Package // any artifact license is evaluated with // what's used to evaluate... - Policy *grant.Policy // what the determination was made against + Policy grant.Policy // what the determination was made against // the output of an evaluation... - Reason []string // reasons that the evaluation value the way it is + Reason []Reason // reasons that the evaluation value the way it is Pass bool // The final evaluation } diff --git a/grant/evalutation/license_evalutation_config.go b/grant/evalutation/license_evalutation_config.go index 72700f0..c1e4585 100644 --- a/grant/evalutation/license_evalutation_config.go +++ b/grant/evalutation/license_evalutation_config.go @@ -5,7 +5,7 @@ import "github.com/anchore/grant/grant" type EvaluationConfig struct { // Policy is the policy to evaluate against // if non is supplied, the default policy is used (grant.DefaultPolicy()) - Policy *grant.Policy + Policy grant.Policy // CheckNonSPDX is true if non-SPDX licenses should be checked CheckNonSPDX bool } diff --git a/grant/evalutation/reason.go b/grant/evalutation/reason.go new file mode 100644 index 0000000..ef04ea6 --- /dev/null +++ b/grant/evalutation/reason.go @@ -0,0 +1,9 @@ +package evalutation + +type Reason string + +var ( + ReasonNoLicenseFound Reason = "no license found" + ReasonLicenseDenied Reason = "license denied by policy" + ReasonLicenseAllowed Reason = "license allowed by policy" +) diff --git a/grant/evalutation/sbom.go b/grant/evalutation/sbom.go deleted file mode 100644 index 855d95a..0000000 --- a/grant/evalutation/sbom.go +++ /dev/null @@ -1,113 +0,0 @@ -package evalutation - -import ( - "github.com/anchore/grant/grant" - "github.com/anchore/syft/syft/sbom" -) - -func newSbomLicenseEvalIndex(ec EvaluationConfig, s sbom.SBOM) *sbomLicenseEvalIndex { - index := evalIndex(ec.Policy) - for pkg := range s.Artifacts.Packages.Enumerate() { - // since we use syft as a library to generate the sbom we need to convert its packages/licenses to grant types - grantPkg := convertSyftPackage(pkg) - if len(grantPkg.Licenses) == 0 { - // no licenses found for this package - index.addCompliant(grantPkg, nil) - } - for _, license := range grantPkg.Licenses { - // TODO: check if the license is in the config ignore list - // TODO: check if the config wants us to check for non-SPDX licenses - if !license.IsSPDX() { - index.addIgnored(grantPkg, license) - continue - } - - if ec.Policy.IsDenied(license) { - index.addViolation(grantPkg, license) - continue - } - // otherwise, the license is allowed - index.addCompliant(grantPkg, &license) - } - } - - return index -} - -func evalIndex(p grant.Policy) *sbomLicenseEvalIndex { - return &sbomLicenseEvalIndex{ - packages: make(map[grant.PackageID]grant.Package), - packageViolations: make(map[grant.PackageID][]grant.License), - compliantPackages: make(map[grant.PackageID][]grant.License), - ignoredPackages: make(map[grant.PackageID][]grant.License), - - licenses: make(map[grant.LicenseID]grant.License), - licenseViolations: make(map[grant.LicenseID][]grant.Package), - compliantLicenses: make(map[grant.LicenseID][]grant.Package), - ignoredLicenses: make(map[grant.LicenseID][]grant.Package), - policy: p, - } -} - -// sboms map packages and licenses together -// sbomLicenseEvalIndex is a more complex Evaluation that tracks packages and licenses -type sbomLicenseEvalIndex struct { - licenses map[grant.LicenseID]grant.License - - // licenseViolations not allowed by the policy and the packages that contained them - // The key for these is the all lowercase SPDX license ID found in internal/spdxlicense/license.go - licenseViolations map[grant.LicenseID][]grant.Package - // compliantLicenses that were allowed by the policy and the packages that contained them - compliantLicenses map[grant.LicenseID][]grant.Package - // ignoredLicenses that were not SPDX compliant and the packages that contained them - ignoredLicenses map[grant.LicenseID][]grant.Package - - packages map[grant.PackageID]grant.Package - - // packageViolations and their licenses that violated the policy - packageViolations map[grant.PackageID][]grant.License - // compliantPackages and their licenses that were compliant to the policy - compliantPackages map[grant.PackageID][]grant.License - // ignoredPackages tracks packages with licenses that were not SPDX compliant - ignoredPackages map[grant.PackageID][]grant.License - - // policy is the policy used to generate this evaluation - policy grant.Policy -} - -func (i *sbomLicenseEvalIndex) isFailed() bool { - return len(i.licenseViolations) > 0 -} - -func (i *sbomLicenseEvalIndex) addViolation(grantPkg grant.Package, license grant.License) { - if _, ok := i.packages[grantPkg.ID]; !ok { - i.packages[grantPkg.ID] = grantPkg - } - if _, ok := i.licenses[license.ID]; !ok { - i.licenses[license.ID] = license - } - - i.packageViolations[grantPkg.ID] = append(i.packageViolations[grantPkg.ID], license) - i.licenseViolations[license.ID] = append(i.licenseViolations[license.ID], grantPkg) -} - -func (i *sbomLicenseEvalIndex) addCompliant(grantPkg grant.Package, license *grant.License) { - i.packages[grantPkg.ID] = grantPkg - if license != nil { - i.licenses[license.ID] = *license - i.compliantPackages[grantPkg.ID] = append(i.compliantPackages[grantPkg.ID], *license) - i.compliantLicenses[license.ID] = append(i.compliantLicenses[license.ID], grantPkg) - return - } - - // no license was provided, so we'll just add the package - if _, ok := i.compliantPackages[grantPkg.ID]; !ok { - i.compliantPackages[grantPkg.ID] = make([]grant.License, 0) - } -} - -// Note: if a license has been ignored, it means the SPDX expression was invalid -// or the user config specified to ignore the specific license -func (i *sbomLicenseEvalIndex) addIgnored(grantPkg grant.Package, license grant.License) { - return -} diff --git a/grant/evalutation/syft.go b/grant/evalutation/syft.go index d7a3a80..e39d93d 100644 --- a/grant/evalutation/syft.go +++ b/grant/evalutation/syft.go @@ -9,14 +9,14 @@ import ( syftPkg "github.com/anchore/syft/syft/pkg" ) -func convertSyftPackage(p syftPkg.Package) grant.Package { +func convertSyftPackage(p syftPkg.Package) *grant.Package { locations := p.Locations.ToSlice() packageLocations := make([]string, 0) for _, location := range locations { packageLocations = append(packageLocations, location.RealPath) } - return grant.Package{ + return &grant.Package{ Name: p.Name, Version: p.Version, Licenses: convertSyftLicenses(p.Licenses), diff --git a/grant/policy.go b/grant/policy.go index 67c5f86..d129354 100644 --- a/grant/policy.go +++ b/grant/policy.go @@ -27,7 +27,7 @@ func DefaultPolicy() *Policy { } } -// NewPolicy builds a policy from lists of allow and deny glob patterns +// NewPolicy builds a policy from lists of allow, deny, and ignore glob patterns // It lower cases all patterns to make matching against the spdx license set case-insensitive func NewPolicy(allowLicenses, denyLicenses, ignoreLicenses []string) (p *Policy, err error) { if len(allowLicenses) == 0 && len(denyLicenses) == 0 { @@ -138,7 +138,7 @@ func (p Policy) IsDenied(license License) bool { return false } -// IsAllowed is a convenience function for library consumers +// IsAllowed is a convenience function for library usage of IsDenied negation func (p Policy) IsAllowed(license License) bool { return !p.IsDenied(license) }