diff --git a/cmd/trivy/main.go b/cmd/trivy/main.go index e3118ae8e97f..dbff5fa54ab1 100644 --- a/cmd/trivy/main.go +++ b/cmd/trivy/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "os" "golang.org/x/xerrors" @@ -9,12 +10,17 @@ import ( "github.com/aquasecurity/trivy/pkg/commands" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/plugin" + "github.com/aquasecurity/trivy/pkg/types" _ "modernc.org/sqlite" // sqlite driver for RPM DB and Java DB ) func main() { if err := run(); err != nil { + var exitError *types.ExitError + if errors.As(err, &exitError) { + os.Exit(exitError.Code) + } log.Fatal("Fatal error", log.Err(err)) } } diff --git a/pkg/cloud/aws/commands/run.go b/pkg/cloud/aws/commands/run.go index 58744e752c79..374abbd91289 100644 --- a/pkg/cloud/aws/commands/run.go +++ b/pkg/cloud/aws/commands/run.go @@ -18,6 +18,7 @@ import ( "github.com/aquasecurity/trivy/pkg/commands/operation" "github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" ) var allSupportedServicesFunc = awsScanner.AllSupportedServices @@ -170,6 +171,5 @@ func Run(ctx context.Context, opt flag.Options) error { return xerrors.Errorf("unable to write results: %w", err) } - operation.Exit(opt, r.Failed()) - return nil + return operation.Exit(opt, r.Failed(), types.Metadata{}) } diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 05e14ea87f82..af2902a14e0d 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -130,6 +130,8 @@ func loadPluginCommands() []*cobra.Command { return nil }, DisableFlagParsing: true, + SilenceUsage: true, + SilenceErrors: true, } commands = append(commands, cmd) } diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index f9e06a30f0a1..fbfe257312ba 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -452,10 +452,7 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err return xerrors.Errorf("report error: %w", err) } - operation.ExitOnEOL(opts, report.Metadata) - operation.Exit(opts, report.Results.Failed()) - - return nil + return operation.Exit(opts, report.Results.Failed(), report.Metadata) } func disabledAnalyzers(opts flag.Options) []analyzer.Type { diff --git a/pkg/commands/convert/run.go b/pkg/commands/convert/run.go index 34e799f7a061..428d6b5b0b4b 100644 --- a/pkg/commands/convert/run.go +++ b/pkg/commands/convert/run.go @@ -44,8 +44,5 @@ func Run(ctx context.Context, opts flag.Options) (err error) { return xerrors.Errorf("unable to write results: %w", err) } - operation.ExitOnEOL(opts, r.Metadata) - operation.Exit(opts, r.Results.Failed()) - - return nil + return operation.Exit(opts, r.Results.Failed(), r.Metadata) } diff --git a/pkg/commands/operation/operation.go b/pkg/commands/operation/operation.go index 2b4e2a7f5ffa..e97a9bd8f1ee 100644 --- a/pkg/commands/operation/operation.go +++ b/pkg/commands/operation/operation.go @@ -204,16 +204,15 @@ func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Cer return caCertPool, cert, nil } -func Exit(opts flag.Options, failedResults bool) { - if opts.ExitCode != 0 && failedResults { - os.Exit(opts.ExitCode) - } -} - -func ExitOnEOL(opts flag.Options, m types.Metadata) { +func Exit(opts flag.Options, failedResults bool, m types.Metadata) error { if opts.ExitOnEOL != 0 && m.OS != nil && m.OS.Eosl { log.Error("Detected EOL OS", log.String("family", string(m.OS.Family)), log.String("version", m.OS.Name)) - os.Exit(opts.ExitOnEOL) + return &types.ExitError{Code: opts.ExitOnEOL} } + + if opts.ExitCode != 0 && failedResults { + return &types.ExitError{Code: opts.ExitCode} + } + return nil } diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go index 179853f99121..01ebf1db645f 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -122,9 +122,7 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er return xerrors.Errorf("unable to write results: %w", err) } - operation.Exit(r.flagOpts, rpt.Failed()) - - return nil + return operation.Exit(r.flagOpts, rpt.Failed(), types.Metadata{}) } // Full-cluster scanning with '--format table' without explicit '--report all' is not allowed so that it won't mess up user's terminal. diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index a72419aceb5a..11e46a4488a0 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -2,6 +2,7 @@ package plugin import ( "context" + "errors" "fmt" "io" "os" @@ -15,6 +16,7 @@ import ( "github.com/aquasecurity/trivy/pkg/downloader" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/utils/fsutils" ) @@ -111,8 +113,11 @@ func (p Plugin) Run(ctx context.Context, opts RunOptions) error { // out if the error was from not being able to execute the plugin or // an error set by the plugin itself. if err = cmd.Run(); err != nil { - if _, ok := err.(*exec.ExitError); !ok { - return xerrors.Errorf("exit: %w", err) + var execError *exec.ExitError + if errors.As(err, &execError) { + return &types.ExitError{ + Code: execError.ExitCode(), + } } return xerrors.Errorf("plugin exec: %w", err) } diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index f9ee7aac2b89..d3f5aa1a0fec 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -140,7 +140,7 @@ func TestPlugin_Run(t *testing.T) { GOOS: "linux", GOARCH: "amd64", }, - wantErr: "plugin exec: exit status 1", + wantErr: "exit status 1", }, } for _, tt := range tests { diff --git a/pkg/types/error.go b/pkg/types/error.go new file mode 100644 index 000000000000..5a1614e07dd0 --- /dev/null +++ b/pkg/types/error.go @@ -0,0 +1,13 @@ +package types + +import ( + "fmt" +) + +type ExitError struct { + Code int +} + +func (e *ExitError) Error() string { + return fmt.Sprintf("exit status %d", e.Code) +}