diff --git a/go.mod b/go.mod index 7084709..25f459a 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/cel-go v0.13.0 github.com/google/osv-scanner v1.1.0 github.com/jedib0t/go-pretty/v6 v6.4.4 - github.com/safedep/dry v0.0.0-20230218045153-1a93b0397b55 + github.com/safedep/dry v0.0.0-20230222132026-c8b6cb976849 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 diff --git a/go.sum b/go.sum index a1dc880..afeb37d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safedep/dry v0.0.0-20230218045153-1a93b0397b55 h1:OBzggSWzjyEa7YaXp2DvpKDe1wYXtOEcFXQfDqkK7PI= github.com/safedep/dry v0.0.0-20230218045153-1a93b0397b55/go.mod h1:odFOtG1l46k23IaCY6kdNkkLW8L+NT+EUVYYVphP59I= +github.com/safedep/dry v0.0.0-20230222132026-c8b6cb976849 h1:5nO9ht1jn7XHFyNFRhUneDZbKmwh4kRr0w/EoWuOQQA= +github.com/safedep/dry v0.0.0-20230222132026-c8b6cb976849/go.mod h1:odFOtG1l46k23IaCY6kdNkkLW8L+NT+EUVYYVphP59I= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= diff --git a/pkg/analyzer/exceptions_generator.go b/pkg/analyzer/exceptions_generator.go new file mode 100644 index 0000000..0b7ae0c --- /dev/null +++ b/pkg/analyzer/exceptions_generator.go @@ -0,0 +1,109 @@ +package analyzer + +import ( + "io" + "os" + "time" + + "github.com/safedep/dry/utils" + "github.com/safedep/vet/gen/exceptionsapi" + "github.com/safedep/vet/pkg/analyzer/filter" + "github.com/safedep/vet/pkg/models" + "github.com/safedep/vet/pkg/readers" +) + +type exceptionsGenerator struct { + writer io.WriteCloser + filterEvaluator filter.Evaluator + expires time.Time + pkgCache map[string]*models.Package +} + +type ExceptionsGeneratorConfig struct { + Path string + Filter string + ExpiresOn string +} + +func NewExceptionsGenerator(config ExceptionsGeneratorConfig) (Analyzer, error) { + fd, err := os.Create(config.Path) + if err != nil { + return nil, err + } + + expiresOn, err := time.Parse("2006-01-02", config.ExpiresOn) + if err != nil { + return nil, err + } + + filterEvaluator, err := filter.NewEvaluator("exceptions-generator", true) + if err != nil { + return nil, err + } + + if utils.IsEmptyString(config.Filter) { + config.Filter = "true" + } + + err = filterEvaluator.AddFilter("exceptions-filter", config.Filter) + if err != nil { + return nil, err + } + + return &exceptionsGenerator{ + writer: fd, + filterEvaluator: filterEvaluator, + expires: expiresOn, + pkgCache: make(map[string]*models.Package), + }, nil +} + +func (f *exceptionsGenerator) Name() string { + return "Exceptions Generator" +} + +func (f *exceptionsGenerator) Analyze(manifest *models.PackageManifest, + handler AnalyzerEventHandler) error { + readers.NewManifestModelReader(manifest).EnumPackages(func(pkg *models.Package) error { + res, err := f.filterEvaluator.EvalPackage(pkg) + if err != nil { + return err + } + + if !res.Matched() { + return nil + } + + f.pkgCache[pkg.Id()] = pkg + return nil + }) + + return nil +} + +func (f *exceptionsGenerator) Finish() error { + defer f.writer.Close() + + suite := exceptionsapi.ExceptionSuite{ + Name: "Auto Generated Exceptions", + Description: "Exceptions file auto-generated using vet", + Exceptions: make([]*exceptionsapi.Exception, 0), + } + + for _, pkg := range f.pkgCache { + suite.Exceptions = append(suite.Exceptions, &exceptionsapi.Exception{ + Id: utils.NewUniqueId(), + Ecosystem: string(pkg.Ecosystem), + Name: pkg.Name, + Version: pkg.Version, + Expires: f.expires.Format(time.RFC3339), + }) + } + + err := utils.FromPbToYaml(f.writer, &suite) + if err != nil { + return err + } + + return nil +} diff --git a/query.go b/query.go index e8dd6ea..d795c24 100644 --- a/query.go +++ b/query.go @@ -1,6 +1,8 @@ package main import ( + "time" + "github.com/safedep/dry/utils" "github.com/safedep/vet/pkg/analyzer" "github.com/safedep/vet/pkg/reporter" @@ -16,6 +18,11 @@ var ( queryEnableConsoleReport bool queryEnableSummaryReport bool queryMarkdownReportPath string + queryExceptionsFile string + queryExceptionsTill string + queryExceptionsFilter string + + queryDefaultExceptionExpiry = time.Now().Add(90 * 24 * time.Hour) ) func newQueryCommand() *cobra.Command { @@ -36,6 +43,13 @@ func newQueryCommand() *cobra.Command { "Filter packages using CEL Filter Suite from file") cmd.Flags().BoolVarP(&queryFilterFailOnMatch, "filter-fail", "", false, "Fail the command if filter matches any package (for security gate)") + cmd.Flags().StringVarP(&queryExceptionsFile, "exceptions-generate", "", "", + "Generate exception records to file (YAML)") + cmd.Flags().StringVarP(&queryExceptionsTill, "exceptions-till", "", + queryDefaultExceptionExpiry.Format("2006-01-02"), + "Generated exceptions are valid till") + cmd.Flags().StringVarP(&queryExceptionsFilter, "exceptions-filter", "", "", + "Generate exception records for packages matching filter") cmd.Flags().BoolVarP(&queryEnableConsoleReport, "report-console", "", false, "Minimal summary of package manifest") cmd.Flags().BoolVarP(&queryEnableSummaryReport, "report-summary", "", false, @@ -74,6 +88,20 @@ func internalStartQuery() error { analyzers = append(analyzers, task) } + if !utils.IsEmptyString(queryExceptionsFile) { + task, err := analyzer.NewExceptionsGenerator(analyzer.ExceptionsGeneratorConfig{ + Path: queryExceptionsFile, + ExpiresOn: queryExceptionsTill, + Filter: queryExceptionsFilter, + }) + + if err != nil { + return err + } + + analyzers = append(analyzers, task) + } + if queryEnableConsoleReport { rp, err := reporter.NewConsoleReporter() if err != nil {