Skip to content

Commit

Permalink
refactor: reorient packages for easier library use (#6)
Browse files Browse the repository at this point in the history
refactor: reorient packages for easier library use (#6)
---------
Signed-off-by: Christopher Phillips <[email protected]>
  • Loading branch information
spiffcs authored Dec 8, 2023
1 parent 102131d commit f71f180
Show file tree
Hide file tree
Showing 20 changed files with 518 additions and 789 deletions.
38 changes: 37 additions & 1 deletion DEVELOPING.md
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
## Developing
## Developing

### Today
```
cmd/grant/
command/
check
list
grant/
license
package
policy
report/
...
```

### Future
```
cmd/grant/
command/
check
list
grant/
license
package
policy
report/
...
```
23 changes: 15 additions & 8 deletions cmd/grant/cli/command/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
"github.com/spf13/cobra"

"github.com/anchore/clio"
"github.com/anchore/grant/cmd/grant/cli/internal/check"
"github.com/anchore/grant/cmd/grant/cli/option"
"github.com/anchore/grant/grant"
"github.com/anchore/grant/grant/report"
"github.com/anchore/grant/internal/input"
)

Expand Down Expand Up @@ -44,22 +44,29 @@ func Check(app clio.Application) *cobra.Command {

// TODO: upgrade the ui a bit with monitors for SBOM generation and license checking
// Progress can be incremented used on a per package basis when grant.Check is called
func runCheck(cfg CheckConfig, sources []string) (errs error) {
func runCheck(cfg CheckConfig, userInput []string) (errs error) {
// check if user provided source by stdin
// note: cat sbom.json | grant check spdx.json - is supported
// it will generate results for both stdin and spdx.json
isStdin, _ := input.IsStdinPipeOrRedirect()
if isStdin && !slices.Contains(sources, "-") {
sources = append(sources, "-")
if isStdin && !slices.Contains(userInput, "-") {
userInput = append(userInput, "-")
}

policy, err := grant.NewPolicy(cfg.AllowLicenses, cfg.DenyLicenses)
policy, err := grant.NewPolicy(cfg.AllowLicenses, cfg.DenyLicenses, cfg.IgnoreLicenses)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("could not check licenses; could not build policy from config: %s", cfg.Config))
}

// TODO: we need to support the ability to write the report to a file without redirecting stdout
return report.NewReport(report.Format(cfg.Format), policy, sources...).
Run().
Render(os.Stdout)
checkConfig := check.Config{
Policy: policy,
}

rep, err := check.NewReport(check.Format(cfg.Format), checkConfig, userInput...)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("unable to create report for inputs %s", userInput))
}

return rep.Render(os.Stdout)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package report
package check

type Format string

Expand Down
82 changes: 31 additions & 51 deletions grant/report/report.go → cmd/grant/cli/internal/check/report.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,56 @@
package report
package check

import (
"encoding/json"
"errors"
"io"
"time"

"github.com/anchore/grant/grant"
"github.com/anchore/grant/grant/evalutation"
)

// Report tracks the requests/results of a license check.
// `grant alpine:latest ./foo` is a single report with two requests
// The first request is easy. Generate an SBOM for alpine:latest and run the policy against it.
// The second request is a little more complicated. Generate an SBOM for ./foo and run the policy against it.
// This is complex because the directory could contain multiple SBOMs, so we need to run the policy against each one.
// Requests have the string that generated the request along with a list of results for that request.
//
// Here is the summary of what multiple source inputs can be configured for a report.
// A source can be one of the following...
// Single Sources Provider:
// - a path to an sbom file (uses the given SBOM (spdx, cyclonedx, etc))
// - a path to a directory (generates an SBOM for the given directory) (no other sbom files in the directory)
// TODO: - a path to some archive (generates an SBOM for the given archive)
// TODO: - a path to a container image (generates an SBOM for the given image)
//
// Multiple Source Provider:
// - multiple paths to sbom files
// TODO: - a path to a directory containing sbom files
// TODO: - a path to a container image with sbom files
// TODO: - a path to a directory containing container images (1.tar.gz 2.tar.gz 3.tar.gz)
// TODO: - a path to a directory containing container images and sbom files
// Report presents the results of a grant check command `grant alpine:latest ./foo`
// The above command will have two results.
// The first result is easy. Generate an SBOM for alpine:latest and run the policy against it.
// The second result is a little more complicated. Visit each leaf of ./foo and check for licenses, sbom, or archives.
// Results are composed of a case its evaluations. The case is the total of SBOM/Licenses generated from the user request.
// The evaluations are the individual assessments of the policy against the packages/licenses in the case.
type Report struct {
ReportID string
Requests []Request `json:"results" yaml:"results"`
ReportID string
Results []evalutation.Result `json:"results" yaml:"results"`
Format Format `json:"format" yaml:"format"`
Timestamp string `json:"timestamp" yaml:"timestamp"`
errors []error
}

// Evaluation is a pass/fail for the entire report;
// It rolls up violations from all the requests
Evaluation Evaluation `json:"evaluation" yaml:"evaluation"`
Format Format `json:"format" yaml:"format"`
Timestamp string `json:"timestamp" yaml:"timestamp"`
errors []error
type Config struct {
Policy *grant.Policy
}

// NewReport will generate a new report for the given format.
// The supplied policy is applied to all user requests.
// If no policy is provided, the default policy will be used
// If no requests are provided, an empty report will be generated
// If a request is provided, but the sbom cannot be generated, the source will be ignored
// Results will be generated and evaluated for each user request that is successfully processed
func NewReport(f Format, p grant.Policy, userRequests ...string) *Report {
if p.IsEmpty() {
p = grant.DefaultPolicy()
// If a request is provided, but the sbom cannot be generated, the source will be ignored and an error will be returned
func NewReport(f Format, cc Config, userRequests ...string) (*Report, error) {
if cc.Policy.IsEmpty() {
cc.Policy = grant.DefaultPolicy()
}
format := validateFormat(f)

requests := make([]Request, 0)
errs := make([]error, 0)
for _, r := range userRequests {
request, err := NewRequest(r, p)
if err != nil {
errs = append(errs, err)
continue
}
requests = append(requests, request)
format := validateFormat(f)
cases := grant.NewCases(cc.Policy, userRequests...)
ec := evalutation.EvaluationConfig{
Policy: cc.Policy,
CheckNonSPDX: true,
}

results := evalutation.NewResults(ec, cases...)

return &Report{
Requests: requests,
Results: results,
Format: format,
Timestamp: time.Now().Format(time.RFC3339),
errors: errs,
}
}, nil
}

// Render will call Render on each result in the report and return the report
Expand All @@ -85,11 +65,11 @@ func (r *Report) Render(out io.Writer) error {
}

func (r *Report) renderJSON(out io.Writer) error {
return json.NewEncoder(out).Encode(r)
return errors.New("not implemented")
}

func (r *Report) renderTable(out io.Writer) error {
return nil
return errors.New("not implemented")
}

// l := list.NewWriter() // TODO: style me
Expand Down
7 changes: 4 additions & 3 deletions cmd/grant/cli/option/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
)

type Check struct {
AllowLicenses []string `json:"allow-licenses" yaml:"allow-licenses" mapstructure:"allow-licenses"`
DenyLicenses []string `json:"deny-licenses" yaml:"deny-licenses" mapstructure:"deny-licenses"`
AllowLicenses []string `json:"allow-licenses" yaml:"allow-licenses" mapstructure:"allow-licenses"`
DenyLicenses []string `json:"deny-licenses" yaml:"deny-licenses" mapstructure:"deny-licenses"`
IgnoreLicenses []string `json:"ignore-licenses" yaml:"ignore-licenses" mapstructure:"ignore-licenses"`
}

func DefaultCheck() Check {
Expand All @@ -17,5 +18,5 @@ func DefaultCheck() Check {
}

func (c Check) ToPolicy() (*grant.Policy, error) {
return grant.NewPolicy(c.AllowLicenses, c.DenyLicenses)
return grant.NewPolicy(c.AllowLicenses, c.DenyLicenses, c.IgnoreLicenses)
}
Loading

0 comments on commit f71f180

Please sign in to comment.