Skip to content

Commit

Permalink
fix: terminal clobbering when commands return errors (#1505)
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Zantow <kzantow@gmail.com>
  • Loading branch information
kzantow authored Sep 20, 2023
1 parent cc76ab8 commit 3a6f3a3
Show file tree
Hide file tree
Showing 21 changed files with 365 additions and 169 deletions.
19 changes: 12 additions & 7 deletions cmd/grype/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import (

"github.com/anchore/clio"
"github.com/anchore/grype/cmd/grype/cli/commands"
handler "github.com/anchore/grype/cmd/grype/cli/ui"
grypeHandler "github.com/anchore/grype/cmd/grype/cli/ui"
"github.com/anchore/grype/cmd/grype/internal/ui"
"github.com/anchore/grype/grype/vulnerability"
"github.com/anchore/grype/internal/bus"
"github.com/anchore/grype/internal/log"
"github.com/anchore/grype/internal/redact"
"github.com/anchore/stereoscope"
syftHandler "github.com/anchore/syft/cmd/syft/cli/ui"
"github.com/anchore/syft/syft"
)

Expand All @@ -41,10 +42,11 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) {
return []clio.UI{noUI}, nil
}

h := handler.New(handler.DefaultHandlerConfig())

return []clio.UI{
ui.New(cfg.Log.Quiet, h),
ui.New(cfg.Log.Quiet,
grypeHandler.New(grypeHandler.DefaultHandlerConfig()),
syftHandler.New(syftHandler.DefaultHandlerConfig()),
),
noUI,
}, nil
},
Expand All @@ -59,13 +61,16 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) {

redact.Set(state.RedactStore)

stereoscope.SetLogger(state.Logger)
syft.SetLogger(state.Logger)
log.Set(state.Logger)
syft.SetLogger(state.Logger)
stereoscope.SetLogger(state.Logger)

return nil
},
)
).
WithPostRuns(func(state *clio.State, err error) {
stereoscope.Cleanup()
})

app := clio.New(*clioCfg)

Expand Down
3 changes: 0 additions & 3 deletions cmd/grype/cli/commands/db_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/anchore/clio"
"github.com/anchore/grype/cmd/grype/cli/options"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal/bus"
)

const (
Expand All @@ -30,8 +29,6 @@ func DBCheck(app clio.Application) *cobra.Command {
}

func runDBCheck(opts options.Database) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.ToCuratorConfig())
if err != nil {
return err
Expand Down
3 changes: 0 additions & 3 deletions cmd/grype/cli/commands/db_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/anchore/clio"
"github.com/anchore/grype/cmd/grype/cli/options"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal/bus"
)

func DBDelete(app clio.Application) *cobra.Command {
Expand All @@ -25,8 +24,6 @@ func DBDelete(app clio.Application) *cobra.Command {
}

func runDBDelete(opts options.Database) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.ToCuratorConfig())
if err != nil {
return err
Expand Down
2 changes: 0 additions & 2 deletions cmd/grype/cli/commands/db_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ func DBDiff(app clio.Application) *cobra.Command {
}

func runDBDiff(opts *dbDiffOptions, base string, target string) (errs error) {
defer bus.Exit()

d, err := differ.NewDiffer(opts.DB.ToCuratorConfig())
if err != nil {
return err
Expand Down
3 changes: 0 additions & 3 deletions cmd/grype/cli/commands/db_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/anchore/grype/cmd/grype/cli/options"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal"
"github.com/anchore/grype/internal/bus"
)

func DBImport(app clio.Application) *cobra.Command {
Expand All @@ -27,8 +26,6 @@ func DBImport(app clio.Application) *cobra.Command {
}

func runDBImport(opts options.Database, dbArchivePath string) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.ToCuratorConfig())
if err != nil {
return err
Expand Down
3 changes: 0 additions & 3 deletions cmd/grype/cli/commands/db_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/anchore/clio"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal/bus"
)

type dbListOptions struct {
Expand Down Expand Up @@ -40,8 +39,6 @@ func DBList(app clio.Application) *cobra.Command {
}

func runDBList(opts *dbListOptions) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.DB.ToCuratorConfig())
if err != nil {
return err
Expand Down
3 changes: 0 additions & 3 deletions cmd/grype/cli/commands/db_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/anchore/clio"
"github.com/anchore/grype/cmd/grype/cli/options"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal/bus"
)

func DBStatus(app clio.Application) *cobra.Command {
Expand All @@ -25,8 +24,6 @@ func DBStatus(app clio.Application) *cobra.Command {
}

func runDBStatus(opts options.Database) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.ToCuratorConfig())
if err != nil {
return err
Expand Down
2 changes: 0 additions & 2 deletions cmd/grype/cli/commands/db_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ func DBUpdate(app clio.Application) *cobra.Command {
}

func runDBUpdate(opts options.Database) error {
defer bus.Exit()

dbCurator, err := db.NewCurator(opts.ToCuratorConfig())
if err != nil {
return err
Expand Down
186 changes: 75 additions & 111 deletions cmd/grype/cli/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"errors"
"fmt"
"strings"
"sync"

"github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
"github.com/wagoodman/go-partybus"

Expand Down Expand Up @@ -101,136 +99,102 @@ var ignoreVEXFixedNotAffected = []match.IgnoreRule{
}

//nolint:funlen
func runGrype(app clio.Application, opts *options.Grype, userInput string) error {
errs := make(chan error)
go func() {
defer close(errs)
defer bus.Exit()

writer, err := format.MakeScanResultWriter(opts.Outputs, opts.File, format.PresentationConfig{
TemplateFilePath: opts.OutputTemplateFile,
ShowSuppressed: opts.ShowSuppressed,
})
if err != nil {
errs <- err
return
}

checkForAppUpdate(app.ID(), opts)

var str *store.Store
var status *db.Status
var dbCloser *db.Closer
var packages []pkg.Package
var s *sbom.SBOM
var pkgContext pkg.Context
var wg = &sync.WaitGroup{}
var loadedDB, gatheredPackages bool

wg.Add(2)
func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs error) {
writer, err := format.MakeScanResultWriter(opts.Outputs, opts.File, format.PresentationConfig{
TemplateFilePath: opts.OutputTemplateFile,
ShowSuppressed: opts.ShowSuppressed,
})
if err != nil {
return err
}

go func() {
defer wg.Done()
var str *store.Store
var status *db.Status
var dbCloser *db.Closer
var packages []pkg.Package
var s *sbom.SBOM
var pkgContext pkg.Context

err = parallel(
func() error {
checkForAppUpdate(app.ID(), opts)
return nil
},
func() (err error) {
log.Debug("loading DB")
str, status, dbCloser, err = grype.LoadVulnerabilityDB(opts.DB.ToCuratorConfig(), opts.DB.AutoUpdate)
if err = validateDBLoad(err, status); err != nil {
errs <- err
return
}
loadedDB = true
}()

go func() {
defer wg.Done()
return validateDBLoad(err, status)
},
func() (err error) {
log.Debugf("gathering packages")
// packages are grype.Package, not syft.Package
// the SBOM is returned for downstream formatting concerns
// grype uses the SBOM in combination with syft formatters to produce cycloneDX
// with vulnerability information appended
packages, pkgContext, s, err = pkg.Provide(userInput, getProviderConfig(opts))
if err != nil {
errs <- fmt.Errorf("failed to catalog: %w", err)
return
return fmt.Errorf("failed to catalog: %w", err)
}
gatheredPackages = true
}()

wg.Wait()
if !loadedDB || !gatheredPackages {
return
}
return nil
},
)

if dbCloser != nil {
defer dbCloser.Close()
}
if err != nil {
return err
}

if opts.OnlyFixed {
opts.Ignore = append(opts.Ignore, ignoreNonFixedMatches...)
}
if dbCloser != nil {
defer dbCloser.Close()
}

if opts.OnlyNotFixed {
opts.Ignore = append(opts.Ignore, ignoreFixedMatches...)
}
if opts.OnlyFixed {
opts.Ignore = append(opts.Ignore, ignoreNonFixedMatches...)
}

if err := applyVexRules(opts); err != nil {
errs <- fmt.Errorf("applying vex rules: %w", err)
return
}
if opts.OnlyNotFixed {
opts.Ignore = append(opts.Ignore, ignoreFixedMatches...)
}

applyDistroHint(packages, &pkgContext, opts)

vulnMatcher := grype.VulnerabilityMatcher{
Store: *str,
IgnoreRules: opts.Ignore,
NormalizeByCVE: opts.ByCVE,
FailSeverity: opts.FailOnServerity(),
Matchers: getMatchers(opts),
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
Documents: opts.VexDocuments,
IgnoreRules: opts.Ignore,
}),
}
if err = applyVexRules(opts); err != nil {
return fmt.Errorf("applying vex rules: %w", err)
}

remainingMatches, ignoredMatches, err := vulnMatcher.FindMatches(packages, pkgContext)
if err != nil {
errs <- err
if !errors.Is(err, grypeerr.ErrAboveSeverityThreshold) {
return
}
}
applyDistroHint(packages, &pkgContext, opts)

vulnMatcher := grype.VulnerabilityMatcher{
Store: *str,
IgnoreRules: opts.Ignore,
NormalizeByCVE: opts.ByCVE,
FailSeverity: opts.FailOnServerity(),
Matchers: getMatchers(opts),
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
Documents: opts.VexDocuments,
IgnoreRules: opts.Ignore,
}),
}

if err := writer.Write(models.PresenterConfig{
Matches: *remainingMatches,
IgnoredMatches: ignoredMatches,
Packages: packages,
Context: pkgContext,
MetadataProvider: str,
SBOM: s,
AppConfig: opts,
DBStatus: status,
}); err != nil {
errs <- err
remainingMatches, ignoredMatches, err := vulnMatcher.FindMatches(packages, pkgContext)
if err != nil {
if !errors.Is(err, grypeerr.ErrAboveSeverityThreshold) {
return err
}
}()

return readAllErrors(errs)
}
errs = appendErrors(errs, err)
}

func readAllErrors(errs <-chan error) (out error) {
for {
if errs == nil {
break
}
err, isOpen := <-errs
if !isOpen {
errs = nil
continue
}
if err != nil {
out = multierror.Append(out, err)
}
if err = writer.Write(models.PresenterConfig{
Matches: *remainingMatches,
IgnoredMatches: ignoredMatches,
Packages: packages,
Context: pkgContext,
MetadataProvider: str,
SBOM: s,
AppConfig: opts,
DBStatus: status,
}); err != nil {
errs = appendErrors(errs, err)
}
return out

return errs
}

func applyDistroHint(pkgs []pkg.Package, context *pkg.Context, opts *options.Grype) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/grype/cli/commands/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var latestAppVersionURL = struct {
path string
}{
host: "https://toolbox-data.anchore.io",
path: fmt.Sprintf("/%s/releases/latest/VERSION", internal.Grype),
path: "/grype/releases/latest/VERSION",
}

func isProductionBuild(version string) bool {
Expand Down
Loading

0 comments on commit 3a6f3a3

Please sign in to comment.