From 079b5678ae929cf2b282a8c2c7427f558b5ffba7 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:51:48 -0500 Subject: [PATCH] feat: update check json output with custom details (#33) * feat: update check json output with custom details --------- Signed-off-by: Christopher Phillips --- .grant.yaml | 1 + cmd/grant/cli/command/check.go | 11 +++- cmd/grant/cli/internal/check/report.go | 70 ++++++++++++++++++++++---- grant/evalutation/reason.go | 7 +++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/.grant.yaml b/.grant.yaml index a13013c..6077460 100644 --- a/.grant.yaml +++ b/.grant.yaml @@ -10,6 +10,7 @@ rules: reason: "GPL licenses are not allowed per xxx-xx company policy" exceptions: - "alpine-baselayout" # We don't link against this package so we don't care about its license + - "alpine-baselayout-data" - "base-files" - "netbase" - "libssl3" diff --git a/cmd/grant/cli/command/check.go b/cmd/grant/cli/command/check.go index 7c97271..ec7c1f2 100644 --- a/cmd/grant/cli/command/check.go +++ b/cmd/grant/cli/command/check.go @@ -19,6 +19,8 @@ import ( "github.com/anchore/grant/internal/input" ) +var ErrPolicyFailure = errors.New("check failed") + type CheckConfig struct { Config string `json:"config" yaml:"config" mapstructure:"config"` option.Check `json:"" yaml:",inline" mapstructure:",squash"` @@ -129,5 +131,12 @@ func runCheck(cfg *CheckConfig, userInput []string) (errs error) { return errors.Wrap(err, fmt.Sprintf("unable to create report for inputs %s", userInput)) } - return rep.Render() + err = rep.Render() + if err != nil { + return errors.Wrap(err, fmt.Sprintf("unable to render report for inputs %s", userInput)) + } + if rep.HasFailures() { + return ErrPolicyFailure + } + return nil } diff --git a/cmd/grant/cli/internal/check/report.go b/cmd/grant/cli/internal/check/report.go index 7daece7..90e4215 100644 --- a/cmd/grant/cli/internal/check/report.go +++ b/cmd/grant/cli/internal/check/report.go @@ -80,6 +80,10 @@ func (r *Report) Render() error { } } +func (r *Report) HasFailures() bool { + return r.Results.IsFailed() +} + type Response struct { ReportID string `json:"report_id" yaml:"report_id"` Timestamp string `json:"timestamp" yaml:"timestamp"` @@ -89,30 +93,78 @@ type Response struct { type Evaluation struct { Input string `json:"input" yaml:"input"` - License string `json:"license" yaml:"license"` - Package string `json:"package" yaml:"package"` + License License `json:"license" yaml:"license"` + Package Package `json:"package" yaml:"package"` Passed bool `json:"passed" yaml:"passed"` Reasons []string `json:"reasons" yaml:"reasons"` } -func NewEvaluation(input string, le evalutation.LicenseEvaluation) Evaluation { - licenseName := le.License.SPDXExpression - if !le.License.IsSPDX() { - licenseName = le.License.Name +type License struct { + SPDXExpression string `json:"spdx_expression" yaml:"spdx_expression"` + Name string `json:"name" yaml:"name"` + Locations []string `json:"locations" yaml:"locations"` + Reference string `json:"reference" yaml:"reference"` + IsDeprecated bool `json:"is_deprecated" yaml:"is_deprecated"` + LicenseID string `json:"license_id" yaml:"license_id"` + SeeAlso []string `json:"see_also" yaml:"see_also"` + IsOsiApproved bool `json:"is_osi_approved" yaml:"is_osi_approved"` +} + +func newLicense(l grant.License) License { + return License{ + SPDXExpression: l.SPDXExpression, + Name: l.Name, + Locations: l.Locations, + Reference: l.Reference, + IsDeprecated: l.IsDeprecatedLicenseID, + LicenseID: l.LicenseID, + SeeAlso: l.SeeAlso, + IsOsiApproved: l.IsOsiApproved, + } +} + +type Package struct { + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + Locations []string `json:"locations" yaml:"locations"` +} + +func newPackage(p *grant.Package) Package { + if p == nil { + return Package{} + } + return Package{ + Name: p.Name, + Version: p.Version, + Locations: p.Locations, } +} +func NewEvaluation(input string, le evalutation.LicenseEvaluation) Evaluation { reasons := make([]string, 0) for _, r := range le.Reason { - details := r.Detail + if r.RuleName == "" { + reasons = append(reasons, r.Detail) + continue + } + details := fmt.Sprintf("%s: %s", r.RuleName, r.Detail) reasons = append(reasons, details) } + + license := newLicense(le.License) + var pkg Package + if le.Package != nil { + pkg = newPackage(le.Package) + } + re := Evaluation{ Input: input, - License: licenseName, - Package: le.Package.Name, + License: license, + Package: pkg, Passed: le.Pass, Reasons: reasons, } + return re } diff --git a/grant/evalutation/reason.go b/grant/evalutation/reason.go index 7fc2d08..1d73c06 100644 --- a/grant/evalutation/reason.go +++ b/grant/evalutation/reason.go @@ -15,3 +15,10 @@ var ( ReasonLicenseAllowed = "license allowed by policy" ReasonLicenseDeniedOSI = "license not OSI approved" ) + +func NewReason(detail, ruleName string) Reason { + return Reason{ + Detail: detail, + RuleName: ruleName, + } +}