feat(cli): convert JSON reports into a different format (#4452)

Co-authored-by: Aurelien LAJOIE <aurelien.lajoie@kili-technology.com>
This commit is contained in:
Teppei Fukuda
2023-05-24 11:45:26 +03:00
committed by GitHub
parent 09db1d4389
commit 50fe43f14c
15 changed files with 316 additions and 64 deletions

View File

@@ -1,5 +1,6 @@
# Reporting # Reporting
## Supported Formats
Trivy supports the following formats: Trivy supports the following formats:
- Table - Table
@@ -8,7 +9,7 @@ Trivy supports the following formats:
- Template - Template
- SBOM - SBOM
## Table (Default) ### Table (Default)
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
@@ -21,7 +22,7 @@ Trivy supports the following formats:
$ trivy image -f table golang:1.12-alpine $ trivy image -f table golang:1.12-alpine
``` ```
### Show origins of vulnerable dependencies #### Show origins of vulnerable dependencies
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
@@ -105,7 +106,7 @@ Also, **glob-parent@3.1.0** with some vulnerabilities is included through chain
Then, you can try to update **axios@0.21.4** and **cra-append-sw@2.7.0** to resolve vulnerabilities in **follow-redirects@1.14.6** and **glob-parent@3.1.0**. Then, you can try to update **axios@0.21.4** and **cra-append-sw@2.7.0** to resolve vulnerabilities in **follow-redirects@1.14.6** and **glob-parent@3.1.0**.
## JSON ### JSON
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
@@ -239,7 +240,7 @@ $ trivy image -f json -o results.json golang:1.12-alpine
`VulnerabilityID`, `PkgName`, `InstalledVersion`, and `Severity` in `Vulnerabilities` are always filled with values, but other fields might be empty. `VulnerabilityID`, `PkgName`, `InstalledVersion`, and `Severity` in `Vulnerabilities` are always filled with values, but other fields might be empty.
## SARIF ### SARIF
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
| Vulnerability | ✓ | | Vulnerability | ✓ |
@@ -255,7 +256,7 @@ $ trivy image --format sarif -o report.sarif golang:1.12-alpine
This SARIF file can be uploaded to GitHub code scanning results, and there is a [Trivy GitHub Action][action] for automating this process. This SARIF file can be uploaded to GitHub code scanning results, and there is a [Trivy GitHub Action][action] for automating this process.
## Template ### Template
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
@@ -264,7 +265,7 @@ This SARIF file can be uploaded to GitHub code scanning results, and there is a
| Secret | ✓ | | Secret | ✓ |
| License | ✓ | | License | ✓ |
### Custom Template #### Custom Template
{% raw %} {% raw %}
``` ```
@@ -301,18 +302,18 @@ Critical: 0, High: 2
For other features of sprig, see the official [sprig][sprig] documentation. For other features of sprig, see the official [sprig][sprig] documentation.
### Load templates from a file #### Load templates from a file
You can load templates from a file prefixing the template path with an @. You can load templates from a file prefixing the template path with an @.
``` ```
$ trivy image --format template --template "@/path/to/template" golang:1.12-alpine $ trivy image --format template --template "@/path/to/template" golang:1.12-alpine
``` ```
### Default Templates #### Default Templates
If Trivy is installed using rpm then default templates can be found at `/usr/local/share/trivy/templates`. If Trivy is installed using rpm then default templates can be found at `/usr/local/share/trivy/templates`.
#### JUnit ##### JUnit
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
| Vulnerability | ✓ | | Vulnerability | ✓ |
@@ -325,7 +326,7 @@ In the following example using the template `junit.tpl` XML can be generated.
$ trivy image --format template --template "@contrib/junit.tpl" -o junit-report.xml golang:1.12-alpine $ trivy image --format template --template "@contrib/junit.tpl" -o junit-report.xml golang:1.12-alpine
``` ```
#### ASFF ##### ASFF
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
| Vulnerability | ✓ | | Vulnerability | ✓ |
@@ -335,7 +336,7 @@ $ trivy image --format template --template "@contrib/junit.tpl" -o junit-report.
Trivy also supports an [ASFF template for reporting findings to AWS Security Hub][asff] Trivy also supports an [ASFF template for reporting findings to AWS Security Hub][asff]
#### HTML ##### HTML
| Scanner | Supported | | Scanner | Supported |
|:----------------:|:---------:| |:----------------:|:---------:|
| Vulnerability | ✓ | | Vulnerability | ✓ |
@@ -353,9 +354,34 @@ The following example shows use of default HTML template when Trivy is installed
$ trivy image --format template --template "@/usr/local/share/trivy/templates/html.tpl" -o report.html golang:1.12-alpine $ trivy image --format template --template "@/usr/local/share/trivy/templates/html.tpl" -o report.html golang:1.12-alpine
``` ```
## SBOM ### SBOM
See [here](../supply-chain/sbom.md) for details. See [here](../supply-chain/sbom.md) for details.
## Converting
To generate multiple reports, you can generate the JSON report first and convert it to other formats with the `convert` subcommand.
```shell
$ trivy image --format json -o result.json --list-all-pkgs debian:11
$ trivy convert --format cyclonedx --output result.cdx result.json
```
!!! note
Please note that if you want to convert to a format that requires a list of packages,
such as SBOM, you need to add the `--list-all-pkgs` flag when outputting in JSON.
[Filtering options](./filtering.md) such as `--severity` are also available with `convert`.
```shell
# Output all severities in JSON
$ trivy image --format json -o result.json --list-all-pkgs debian:11
# Output only critical issues in table format
$ trivy convert --format table --severity CRITICAL result.json
```
!!! note
JSON reports from "trivy aws" and "trivy k8s" are not yet supported.
[cargo-auditable]: https://github.com/rust-secure-code/cargo-auditable/ [cargo-auditable]: https://github.com/rust-secure-code/cargo-auditable/
[action]: https://github.com/aquasecurity/trivy-action [action]: https://github.com/aquasecurity/trivy-action
[asff]: ../../tutorials/integrations/aws-security-hub.md [asff]: ../../tutorials/integrations/aws-security-hub.md

View File

@@ -45,6 +45,7 @@ trivy [global flags] command [flags] target
* [trivy aws](trivy_aws.md) - [EXPERIMENTAL] Scan AWS account * [trivy aws](trivy_aws.md) - [EXPERIMENTAL] Scan AWS account
* [trivy config](trivy_config.md) - Scan config files for misconfigurations * [trivy config](trivy_config.md) - Scan config files for misconfigurations
* [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format
* [trivy filesystem](trivy_filesystem.md) - Scan local filesystem * [trivy filesystem](trivy_filesystem.md) - Scan local filesystem
* [trivy image](trivy_image.md) - Scan a container image * [trivy image](trivy_image.md) - Scan a container image
* [trivy kubernetes](trivy_kubernetes.md) - [EXPERIMENTAL] Scan kubernetes cluster * [trivy kubernetes](trivy_kubernetes.md) - [EXPERIMENTAL] Scan kubernetes cluster

View File

@@ -0,0 +1,52 @@
## trivy convert
Convert Trivy JSON report into a different format
```
trivy convert [flags] RESULT_JSON
```
### Examples
```
# report conversion
$ trivy image --format json --output result.json --list-all-pkgs debian:11
$ trivy convert --format cyclonedx --output result.cdx result.json
```
### Options
```
--compliance string compliance report to generate
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--exit-code int specify exit code when any security issues are found
--exit-on-eol int exit with the specified code when the OS reaches end of service/life
-f, --format string format (table, json, template, sarif, cyclonedx, spdx, spdx-json, github, cosign-vuln) (default "table")
-h, --help help for convert
--ignore-policy string specify the Rego file path to evaluate each vulnerability
--ignorefile string specify .trivyignore file (default ".trivyignore")
--list-all-pkgs enabling the option will output all packages regardless of vulnerability
-o, --output string output file name
--report string specify a report format for the output. (all,summary) (default "all")
-s, --severity string severities of security issues to be displayed (comma separated) (default "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL")
-t, --template string output template
```
### Options inherited from parent commands
```
--cache-dir string cache directory (default "/path/to/cache")
-c, --config string config path (default "trivy.yaml")
-d, --debug debug mode
--generate-default-config write the default config to trivy-default.yaml
--insecure allow insecure server connections
-q, --quiet suppress progress bar and log output
--timeout duration timeout (default 5m0s)
-v, --version show version
```
### SEE ALSO
* [trivy](trivy.md) - Unified security scanner

View File

@@ -3,3 +3,9 @@
### How to pronounce the name "Trivy"? ### How to pronounce the name "Trivy"?
`tri` is pronounced like **tri**gger, `vy` is pronounced like en**vy**. `tri` is pronounced like **tri**gger, `vy` is pronounced like en**vy**.
### How to generate multiple reports?
See [here](../docs/configuration/reporting.md#converting).
### How to run Trivy under air-gapped environment?
See [here](../docs/advanced/air-gap.md).

View File

@@ -15,7 +15,7 @@ import (
"github.com/aquasecurity/trivy/pkg/cloud" "github.com/aquasecurity/trivy/pkg/cloud"
"github.com/aquasecurity/trivy/pkg/cloud/aws/scanner" "github.com/aquasecurity/trivy/pkg/cloud/aws/scanner"
"github.com/aquasecurity/trivy/pkg/cloud/report" "github.com/aquasecurity/trivy/pkg/cloud/report"
cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" "github.com/aquasecurity/trivy/pkg/commands/operation"
cr "github.com/aquasecurity/trivy/pkg/compliance/report" cr "github.com/aquasecurity/trivy/pkg/compliance/report"
"github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
@@ -147,6 +147,6 @@ func Run(ctx context.Context, opt flag.Options) error {
return fmt.Errorf("unable to write results: %w", err) return fmt.Errorf("unable to write results: %w", err)
} }
cmd.Exit(opt, r.Failed()) operation.Exit(opt, r.Failed())
return nil return nil
} }

View File

@@ -19,7 +19,7 @@ const (
tableFormat = "table" tableFormat = "table"
) )
// Report represents a kubernetes scan report // Report represents an AWS scan report
type Report struct { type Report struct {
Provider string Provider string
AccountID string AccountID string

View File

@@ -19,6 +19,7 @@ import (
javadb "github.com/aquasecurity/trivy-java-db/pkg/db" javadb "github.com/aquasecurity/trivy-java-db/pkg/db"
awscommands "github.com/aquasecurity/trivy/pkg/cloud/aws/commands" awscommands "github.com/aquasecurity/trivy/pkg/cloud/aws/commands"
"github.com/aquasecurity/trivy/pkg/commands/artifact" "github.com/aquasecurity/trivy/pkg/commands/artifact"
"github.com/aquasecurity/trivy/pkg/commands/convert"
"github.com/aquasecurity/trivy/pkg/commands/server" "github.com/aquasecurity/trivy/pkg/commands/server"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/flag"
@@ -106,6 +107,7 @@ func NewApp(version string) *cobra.Command {
NewClientCommand(globalFlags), NewClientCommand(globalFlags),
NewServerCommand(globalFlags), NewServerCommand(globalFlags),
NewConfigCommand(globalFlags), NewConfigCommand(globalFlags),
NewConvertCommand(globalFlags),
NewPluginCommand(), NewPluginCommand(),
NewModuleCommand(globalFlags), NewModuleCommand(globalFlags),
NewKubernetesCommand(globalFlags), NewKubernetesCommand(globalFlags),
@@ -494,6 +496,47 @@ func NewRepositoryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
return cmd return cmd
} }
func NewConvertCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
convertFlags := &flag.Flags{
ScanFlagGroup: &flag.ScanFlagGroup{},
ReportFlagGroup: flag.NewReportFlagGroup(),
}
cmd := &cobra.Command{
Use: "convert [flags] RESULT_JSON",
Aliases: []string{"conv"},
GroupID: groupUtility,
Short: "Convert Trivy JSON report into a different format",
Example: ` # report conversion
$ trivy image --format json --output result.json --list-all-pkgs debian:11
$ trivy convert --format cyclonedx --output result.cdx result.json
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := convertFlags.Bind(cmd); err != nil {
return xerrors.Errorf("flag bind error: %w", err)
}
return validateArgs(cmd, args)
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := convertFlags.Bind(cmd); err != nil {
return xerrors.Errorf("flag bind error: %w", err)
}
opts, err := convertFlags.ToOptions(cmd.Version, args, globalFlags, outputWriter)
if err != nil {
return xerrors.Errorf("flag error: %w", err)
}
return convert.Run(cmd.Context(), opts)
},
SilenceErrors: true,
SilenceUsage: true,
}
cmd.SetFlagErrorFunc(flagErrorFunc)
convertFlags.AddFlags(cmd)
cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, convertFlags.Usages(cmd)))
return cmd
}
// NewClientCommand returns the 'client' subcommand that is deprecated // NewClientCommand returns the 'client' subcommand that is deprecated
func NewClientCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { func NewClientCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
remoteFlags := flag.NewClientFlags() remoteFlags := flag.NewClientFlags()
@@ -799,7 +842,7 @@ func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
if err != nil { if err != nil {
return xerrors.Errorf("flag error: %w", err) return xerrors.Errorf("flag error: %w", err)
} }
return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.Registry()) return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.RegistryOpts())
}, },
}, },
&cobra.Command{ &cobra.Command{

View File

@@ -4,9 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/spf13/viper" "github.com/spf13/viper"
@@ -26,6 +23,7 @@ import (
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/misconf" "github.com/aquasecurity/trivy/pkg/misconf"
"github.com/aquasecurity/trivy/pkg/module" "github.com/aquasecurity/trivy/pkg/module"
"github.com/aquasecurity/trivy/pkg/policy"
pkgReport "github.com/aquasecurity/trivy/pkg/report" pkgReport "github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/rpc/client" "github.com/aquasecurity/trivy/pkg/rpc/client"
@@ -273,15 +271,7 @@ func (r *runner) scanArtifact(ctx context.Context, opts flag.Options, initialize
func (r *runner) Filter(ctx context.Context, opts flag.Options, report types.Report) (types.Report, error) { func (r *runner) Filter(ctx context.Context, opts flag.Options, report types.Report) (types.Report, error) {
// Filter results // Filter results
err := result.Filter(ctx, report, result.FilterOption{ err := result.Filter(ctx, report, opts.FilterOpts())
Severities: opts.Severities,
IgnoreUnfixed: opts.IgnoreUnfixed,
IncludeNonFailures: opts.IncludeNonFailures,
IgnoreFile: opts.IgnoreFile,
PolicyFile: opts.IgnorePolicy,
IgnoreLicenses: opts.IgnoredLicenses,
VEXPath: opts.VEXPath,
})
if err != nil { if err != nil {
return types.Report{}, xerrors.Errorf("filtering error: %w", err) return types.Report{}, xerrors.Errorf("filtering error: %w", err)
} }
@@ -290,18 +280,7 @@ func (r *runner) Filter(ctx context.Context, opts flag.Options, report types.Rep
} }
func (r *runner) Report(opts flag.Options, report types.Report) error { func (r *runner) Report(opts flag.Options, report types.Report) error {
if err := pkgReport.Write(report, pkgReport.Option{ if err := pkgReport.Write(report, opts.ReportOpts()); err != nil {
AppVersion: opts.AppVersion,
Format: opts.Format,
Output: opts.Output,
Tree: opts.DependencyTree,
Severities: opts.Severities,
OutputTemplate: opts.Template,
IncludeNonFailures: opts.IncludeNonFailures,
Trace: opts.Trace,
Report: opts.ReportFormat,
Compliance: opts.Compliance,
}); err != nil {
return xerrors.Errorf("unable to write results: %w", err) return xerrors.Errorf("unable to write results: %w", err)
} }
@@ -320,7 +299,7 @@ func (r *runner) initDB(ctx context.Context, opts flag.Options) error {
// download the database file // download the database file
noProgress := opts.Quiet || opts.NoProgress noProgress := opts.Quiet || opts.NoProgress
if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.Registry()); err != nil { if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
return err return err
} }
@@ -475,8 +454,8 @@ func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err err
return xerrors.Errorf("report error: %w", err) return xerrors.Errorf("report error: %w", err)
} }
exitOnEOL(opts, report.Metadata) operation.ExitOnEOL(opts, report.Metadata)
Exit(opts, report.Results.Failed()) operation.Exit(opts, report.Results.Failed())
return nil return nil
} }
@@ -661,7 +640,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
// For image scanning // For image scanning
ImageOption: ftypes.ImageOptions{ ImageOption: ftypes.ImageOptions{
RegistryOptions: opts.Registry(), RegistryOptions: opts.RegistryOpts(),
DockerOptions: ftypes.DockerOptions{ DockerOptions: ftypes.DockerOptions{
Host: opts.DockerHost, Host: opts.DockerHost,
}, },
@@ -704,19 +683,6 @@ func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeSc
return report, nil return report, 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) {
if opts.ExitOnEOL != 0 && m.OS != nil && m.OS.Eosl {
log.Logger.Errorf("Detected EOL OS: %s %s", m.OS.Family, m.OS.Name)
os.Exit(opts.ExitOnEOL)
}
}
func canonicalVersion(ver string) string { func canonicalVersion(ver string) string {
if ver == devVersion { if ver == devVersion {
return ver return ver

View File

@@ -0,0 +1,48 @@
package convert
import (
"context"
"encoding/json"
"os"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/commands/operation"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/types"
)
func Run(ctx context.Context, opts flag.Options) (err error) {
f, err := os.Open(opts.Target)
if err != nil {
return xerrors.Errorf("file open error: %w", err)
}
defer f.Close()
var r types.Report
if err = json.NewDecoder(f).Decode(&r); err != nil {
return xerrors.Errorf("json decode error: %w", err)
}
// "convert" supports JSON results produced by Trivy scanning other than AWS and Kubernetes
if r.ArtifactName == "" && r.ArtifactType == "" {
return xerrors.New("AWS and Kubernetes scanning reports are not yet supported")
}
if err = result.Filter(ctx, r, opts.FilterOpts()); err != nil {
return xerrors.Errorf("unable to filter results: %w", err)
}
log.Logger.Debug("Writing report to output...")
if err = report.Write(r, opts.ReportOpts()); err != nil {
return xerrors.Errorf("unable to write results: %w", err)
}
operation.ExitOnEOL(opts, r.Metadata)
operation.Exit(opts, r.Results.Failed())
return nil
}

View File

@@ -16,10 +16,11 @@ import (
"github.com/aquasecurity/trivy-db/pkg/metadata" "github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/db" "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/cache" "github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/policy" "github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils/fsutils" "github.com/aquasecurity/trivy/pkg/utils/fsutils"
) )
@@ -109,7 +110,7 @@ func (c Cache) ClearArtifacts() error {
} }
// DownloadDB downloads the DB // DownloadDB downloads the DB
func DownloadDB(ctx context.Context, appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool, opt types.RegistryOptions) error { func DownloadDB(ctx context.Context, appVersion, cacheDir, dbRepository string, quiet, skipUpdate bool, opt ftypes.RegistryOptions) error {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@@ -201,3 +202,16 @@ func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Cer
return caCertPool, cert, nil 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) {
if opts.ExitOnEOL != 0 && m.OS != nil && m.OS.Eosl {
log.Logger.Errorf("Detected EOL OS: %s %s", m.OS.Family, m.OS.Name)
os.Exit(opts.ExitOnEOL)
}
}

View File

@@ -35,7 +35,7 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
// download the database file // download the database file
if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository,
true, opts.SkipDBUpdate, opts.Registry()); err != nil { true, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
return err return err
} }
@@ -58,6 +58,6 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
m.Register() m.Register()
server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader, server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader,
opts.DBRepository, opts.Registry()) opts.DBRepository, opts.RegistryOpts())
return server.ListenAndServe(cache, opts.SkipDBUpdate) return server.ListenAndServe(cache, opts.SkipDBUpdate)
} }

View File

@@ -18,6 +18,7 @@ import (
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report" "github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/result"
) )
type Flag struct { type Flag struct {
@@ -122,8 +123,8 @@ func (o *Options) Align() {
} }
} }
// Registry returns options for OCI registries // RegistryOpts returns options for OCI registries
func (o *Options) Registry() ftypes.RegistryOptions { func (o *Options) RegistryOpts() ftypes.RegistryOptions {
return ftypes.RegistryOptions{ return ftypes.RegistryOptions{
Credentials: o.Credentials, Credentials: o.Credentials,
RegistryToken: o.RegistryToken, RegistryToken: o.RegistryToken,
@@ -133,6 +134,34 @@ func (o *Options) Registry() ftypes.RegistryOptions {
} }
} }
// FilterOpts returns options for filtering
func (o *Options) FilterOpts() result.FilterOption {
return result.FilterOption{
Severities: o.Severities,
IgnoreUnfixed: o.IgnoreUnfixed,
IncludeNonFailures: o.IncludeNonFailures,
IgnoreFile: o.IgnoreFile,
PolicyFile: o.IgnorePolicy,
IgnoreLicenses: o.IgnoredLicenses,
VEXPath: o.VEXPath,
}
}
func (o *Options) ReportOpts() report.Option {
return report.Option{
AppVersion: o.AppVersion,
Format: o.Format,
Output: o.Output,
Tree: o.DependencyTree,
Severities: o.Severities,
OutputTemplate: o.Template,
IncludeNonFailures: o.IncludeNonFailures,
Trace: o.Trace,
Report: o.ReportFormat,
Compliance: o.Compliance,
}
}
func addFlag(cmd *cobra.Command, flag *Flag) { func addFlag(cmd *cobra.Command, flag *Flag) {
if flag == nil || flag.Name == "" { if flag == nil || flag.Name == "" {
return return

View File

@@ -10,6 +10,7 @@ import (
"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts" "github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
"github.com/aquasecurity/trivy-kubernetes/pkg/k8s" "github.com/aquasecurity/trivy-kubernetes/pkg/k8s"
cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" cmd "github.com/aquasecurity/trivy/pkg/commands/artifact"
"github.com/aquasecurity/trivy/pkg/commands/operation"
cr "github.com/aquasecurity/trivy/pkg/compliance/report" cr "github.com/aquasecurity/trivy/pkg/compliance/report"
"github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/k8s/report" "github.com/aquasecurity/trivy/pkg/k8s/report"
@@ -120,7 +121,7 @@ func (r *runner) run(ctx context.Context, artifacts []*artifacts.Artifact) error
return xerrors.Errorf("unable to write results: %w", err) return xerrors.Errorf("unable to write results: %w", err)
} }
cmd.Exit(r.flagOpts, rpt.Failed()) operation.Exit(r.flagOpts, rpt.Failed())
return nil return nil
} }

View File

@@ -139,6 +139,9 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) {
) )
for _, result := range r.Results { for _, result := range r.Results {
if len(result.Packages) == 0 {
continue
}
parentPackage, err := m.resultToSpdxPackage(result, r.Metadata.OS, pkgDownloadLocation) parentPackage, err := m.resultToSpdxPackage(result, r.Metadata.OS, pkgDownloadLocation)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to parse result: %w", err) return nil, xerrors.Errorf("failed to parse result: %w", err)

View File

@@ -761,6 +761,69 @@ func TestMarshaler_Marshal(t *testing.T) {
}, },
}, },
}, },
{
name: "happy path secret",
inputReport: types.Report{
SchemaVersion: report.SchemaVersion,
ArtifactName: "secret",
ArtifactType: ftypes.ArtifactFilesystem,
Results: types.Results{
{
Target: "key.pem",
Class: types.ClassSecret,
Secrets: []ftypes.SecretFinding{
{
RuleID: "private-key",
Category: "AsymmetricPrivateKey",
Severity: "HIGH",
Title: "Asymmetric Private Key",
StartLine: 1,
EndLine: 1,
},
},
},
},
},
wantSBOM: &spdx.Document{
SPDXVersion: spdx.Version,
DataLicense: spdx.DataLicense,
SPDXIdentifier: "DOCUMENT",
DocumentName: "secret",
DocumentNamespace: "http://aquasecurity.github.io/trivy/filesystem/secret-3ff14136-e09f-4df9-80ea-000000000001",
CreationInfo: &spdx.CreationInfo{
Creators: []common.Creator{
{
Creator: "aquasecurity",
CreatorType: "Organization",
},
{
Creator: fmt.Sprintf("trivy-0.38.1"),
CreatorType: "Tool",
},
},
Created: "2021-08-25T12:20:30Z",
},
Packages: []*spdx.Package{
{
PackageName: "secret",
PackageSPDXIdentifier: "Filesystem-5c08d34162a2c5d3",
PackageDownloadLocation: "NONE",
PackageAttributionTexts: []string{
"SchemaVersion: 2",
},
PrimaryPackagePurpose: tspdx.PackagePurposeSource,
},
},
Relationships: []*spdx.Relationship{
{
RefA: spdx.DocElementID{ElementRefID: "DOCUMENT"},
RefB: spdx.DocElementID{ElementRefID: "Filesystem-5c08d34162a2c5d3"},
Relationship: "DESCRIBES",
},
},
},
},
} }
clock := fake.NewFakeClock(time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) clock := fake.NewFakeClock(time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC))