From b259b25ce40ce68a6d16ed5207a2e34b67c934a9 Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Thu, 11 Aug 2022 14:59:32 +0100 Subject: [PATCH] feat: Add AWS Cloud scanning (#2493) * feat: Added AWS Cloud scanning Co-authored-by: Owen Rumney --- .github/CODEOWNERS | 3 + docs/docs/cloud/aws/scanning.md | 55 +++ .../references/customization/config-file.md | 48 ++- docs/imgs/trivy-aws.png | Bin 0 -> 153658 bytes go.mod | 73 +++- go.sum | 150 +++++-- mkdocs.yml | 2 + pkg/cloud/aws/commands/run.go | 187 ++++++++ pkg/cloud/aws/scanner/progress.go | 79 ++++ pkg/cloud/aws/scanner/scanner.go | 74 ++++ pkg/cloud/cache/cache.go | 65 +++ pkg/cloud/cache/cache_test.go | 166 +++++++ pkg/cloud/cache/load.go | 59 +++ pkg/cloud/cache/save.go | 77 ++++ pkg/cloud/cache/schema.go | 24 ++ pkg/cloud/provider.go | 5 + pkg/cloud/report/convert.go | 95 ++++ pkg/cloud/report/convert_test.go | 241 +++++++++++ pkg/cloud/report/report.go | 175 ++++++++ pkg/cloud/report/resource.go | 89 ++++ pkg/cloud/report/resource_test.go | 123 ++++++ pkg/cloud/report/result.go | 37 ++ pkg/cloud/report/result_test.go | 82 ++++ pkg/cloud/report/service.go | 86 ++++ pkg/cloud/report/service_test.go | 407 ++++++++++++++++++ pkg/commands/app.go | 66 +++ pkg/commands/artifact/run.go | 2 +- pkg/fanal/types/artifact.go | 1 + pkg/fanal/types/const.go | 1 + pkg/flag/aws_flags.go | 78 ++++ pkg/flag/cloud_flags.go | 50 +++ pkg/flag/options.go | 19 + 32 files changed, 2569 insertions(+), 50 deletions(-) create mode 100644 docs/docs/cloud/aws/scanning.md create mode 100644 docs/imgs/trivy-aws.png create mode 100644 pkg/cloud/aws/commands/run.go create mode 100644 pkg/cloud/aws/scanner/progress.go create mode 100644 pkg/cloud/aws/scanner/scanner.go create mode 100644 pkg/cloud/cache/cache.go create mode 100644 pkg/cloud/cache/cache_test.go create mode 100644 pkg/cloud/cache/load.go create mode 100644 pkg/cloud/cache/save.go create mode 100644 pkg/cloud/cache/schema.go create mode 100644 pkg/cloud/provider.go create mode 100644 pkg/cloud/report/convert.go create mode 100644 pkg/cloud/report/convert_test.go create mode 100644 pkg/cloud/report/report.go create mode 100644 pkg/cloud/report/resource.go create mode 100644 pkg/cloud/report/resource_test.go create mode 100644 pkg/cloud/report/result.go create mode 100644 pkg/cloud/report/result_test.go create mode 100644 pkg/cloud/report/service.go create mode 100644 pkg/cloud/report/service_test.go create mode 100644 pkg/flag/aws_flags.go create mode 100644 pkg/flag/cloud_flags.go diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0e35c1f3175a..e6023593ce86 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,8 +7,11 @@ helm/trivy/ @krol3 # Misconfiguration scanning examples/misconf/ @owenrumney @liamg @knqyf263 docs/docs/misconfiguration @owenrumney @liamg @knqyf263 +docs/docs/cloud @owenrumney @liamg @knqyf263 pkg/fanal/analyzer/config @owenrumney @liamg @knqyf263 pkg/fanal/handler/misconf @owenrumney @liamg @knqyf263 +pkg/cloud @owenrumney @liamg @knqyf263 +pkg/flag @owenrumney @liamg @knqyf263 # Kubernetes scanning pkg/k8s/ @josedonizetti @chen-keinan @knqyf263 diff --git a/docs/docs/cloud/aws/scanning.md b/docs/docs/cloud/aws/scanning.md new file mode 100644 index 000000000000..8d178cb22cf1 --- /dev/null +++ b/docs/docs/cloud/aws/scanning.md @@ -0,0 +1,55 @@ +# Amazon Web Services + +!!! warning "EXPERIMENTAL" + This feature might change without preserving backwards compatibility. + +The Trivy AWS CLI allows you to scan your AWS account for misconfigurations. You can either run the CLI locally or integrate it into your CI/CD pipeline. + +Whilst you can already scan the infrastructure-as-code that defines your AWS resources with `trivy config`, you can now scan your live AWS account(s) directly too. + +The included checks cover all of the aspects of the [AWS CIS 1.2](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-cis.html) automated benchmarks. + +Trivy uses the same [authentication methods](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) as the AWS CLI to configure and authenticate your access to the AWS platform. + +You will need permissions configured to read all AWS resources - we recommend using a group/role with the `ReadOnlyAccess` and `SecurityAudit` policies attached. + +Once you've scanned your account, you can run additional commands to filter the results without having to run the entire scan again - results are cached locally per AWS account/region. + +## CLI Commands + +Scan a full AWS account (all supported services): + +```shell +trivy aws --region us-east-1 +``` + +You can allow Trivy to determine the AWS region etc. by using the standard AWS configuration files and environment variables. The `--region` flag overrides these. + +![AWS Summary Report](../../../imgs/trivy-aws.png) + +The summary view is the default when scanning multiple services. + +Scan a specific service: + +```shell +trivy aws --service s3 +``` + +Scan multiple services: + +```shell +# --service s3,ec2 works too +trivy aws --service s3 --service ec2 +``` + +Show results for a specific AWS resource: + +```shell +trivy aws --service s3 --arn arn:aws:s3:::example-bucket +``` + +All ARNs with detected issues will be displayed when showing results for their associated service. + +## Cached Results + +By default, Trivy will cache results for each service for 24 hours. This means you can filter and view results for a service without having to wait for the scan to run again. If you want to force the cache to be refreshed with the latest data, you can use `--update-cache`. Or if you'd like to use cached data for a different timeframe, you can specify `--max-cache-age` (e.g. `--max-cache-age 2h`.) diff --git a/docs/docs/references/customization/config-file.md b/docs/docs/references/customization/config-file.md index febd9fcd6502..e8943f899a64 100644 --- a/docs/docs/references/customization/config-file.md +++ b/docs/docs/references/customization/config-file.md @@ -6,7 +6,7 @@ An example is [here][example]. ## Global Options -``` +```yaml # Same as '--quiet' # Default is false quiet: false @@ -30,7 +30,7 @@ cache-dir: $HOME/.cache/trivy ## Report Options -``` +```yaml # Same as '--format' # Default is 'table' format: table @@ -80,7 +80,7 @@ severity: ## Scan Options Available in client/server mode -``` +```yaml scan: # Same as '--skip-dirs' # Default is empty @@ -107,7 +107,7 @@ scan: ## Cache Options -``` +```yaml cache: # Same as '--cache-backend' # Default is 'fs' @@ -134,7 +134,7 @@ cache: ## DB Options -``` +```yaml db: # Same as '--skip-db-update' # Default is false @@ -152,7 +152,7 @@ db: ## Image Options Available with container image scanning -``` +```yaml image: # Same as '--input' (available with 'trivy image') # Default is empty @@ -166,7 +166,7 @@ image: ## Vulnerability Options Available with vulnerability scanning -``` +```yaml vulnerability: # Same as '--vuln-type' # Default is 'os,library' @@ -182,7 +182,7 @@ vulnerability: ## Secret Options Available with secret scanning -``` +```yaml secret: # Same as '--secret-config' # Default is 'trivy-secret.yaml' @@ -193,7 +193,7 @@ secret: ## Misconfiguration Options Available with misconfiguration scanning -``` +```yaml misconfiguration: # Same as '--file-patterns' # Default is empty @@ -256,7 +256,7 @@ misconfiguration: ## Kubernetes Options Available with Kubernetes scanning -``` +```yaml kubernetes: # Same as '--context' # Default is empty @@ -270,7 +270,7 @@ kubernetes: ## Repository Options Available with git repository scanning (`trivy repo`) -``` +```yaml repository: # Same as '--branch' # Default is empty @@ -288,7 +288,7 @@ repository: ## Client/Server Options Available in client/server mode -``` +```yaml server: # Same as '--server' (available in client mode) # Default is empty @@ -313,4 +313,28 @@ server: listen: 0.0.0.0:10000 ``` +## Cloud Options + +Available for cloud scanning (currently only `trivy aws`) + +```yaml +cloud: + # whether to force a cache update for every scan + update-cache: false + + # how old cached results can be before being invalidated + max-cache-age: 24h + + # aws-specific cloud settings + aws: + # the aws region to use + region: us-east-1 + + # the aws endpoint to use (not required for general use) + endpoint: https://my.custom.aws.endpoint + + # the aws account to use (this will be determined from your environment when not set) + account: 123456789012 +``` + [example]: https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/trivy-conf/trivy.yaml diff --git a/docs/imgs/trivy-aws.png b/docs/imgs/trivy-aws.png new file mode 100644 index 0000000000000000000000000000000000000000..5e748fea57573db418c7201da17dcb80aa7fb670 GIT binary patch literal 153658 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
8!HI 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
5(`=kPaGSZTtg5Q1tOPTO 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 f=09I*=^iGP;zcZyt 0 { + log.Logger.Debugf("Scanning the following services using the AWS API: [%s]...", strings.Join(servicesToScan, ", ")) + opt.Services = servicesToScan + results, err := scanner.NewScanner().Scan(ctx, opt) + if err != nil { + var aerr errs.AdapterError + if errors.As(err, &aerr) { + for _, e := range aerr.Errors() { + log.Logger.Warnf("Adapter error: %s", e) + } + } + return fmt.Errorf("aws scan error: %w", err) + } + r = report.New(cloud.ProviderAWS, opt.Account, opt.Region, results.GetFailed(), opt.Services) + } else { + log.Logger.Debug("No more services to scan - everything was found in the cache.") + r = report.New(cloud.ProviderAWS, opt.Account, opt.Region, nil, opt.Services) + } + if len(servicesToLoadFromCache) > 0 { + log.Logger.Debug("Loading cached results...") + cachedReport, err := cached.LoadReport(servicesToLoadFromCache...) + if err != nil { + return err + } + for service, results := range cachedReport.Results { + log.Logger.Debugf("Adding cached results for '%s'...", service) + r.AddResultsForService(service, results.Results, results.CreationTime) + } + } + + if len(servicesToScan) > 0 { // don't write cache if we didn't scan anything new + log.Logger.Debugf("Writing results to cache for services [%s]...", strings.Join(r.ServicesInScope, ", ")) + if err := cached.Save(r); err != nil { + return err + } + } + + log.Logger.Debug("Writing report to output...") + if err := report.Write(r, opt, len(servicesToLoadFromCache) > 0); err != nil { + return fmt.Errorf("unable to write results: %w", err) + } + + cmd.Exit(opt, r.Failed()) + return nil +} diff --git a/pkg/cloud/aws/scanner/progress.go b/pkg/cloud/aws/scanner/progress.go new file mode 100644 index 000000000000..57bffa8c3e34 --- /dev/null +++ b/pkg/cloud/aws/scanner/progress.go @@ -0,0 +1,79 @@ +package scanner + +import ( + "fmt" + "os" + + "github.com/liamg/loading/pkg/bar" +) + +type progressTracker struct { + serviceBar *bar.Bar + serviceTotal int + serviceCurrent int + isTTY bool +} + +func newProgressTracker() *progressTracker { + var isTTY bool + if stat, err := os.Stdout.Stat(); err == nil { + isTTY = stat.Mode()&os.ModeCharDevice == os.ModeCharDevice + } + return &progressTracker{ + isTTY: isTTY, + } +} + +func (m *progressTracker) Finish() { + if !m.isTTY || m.serviceBar == nil { + return + } + m.serviceBar.Finish() +} + +func (m *progressTracker) IncrementResource() { + if !m.isTTY { + return + } + m.serviceBar.Increment() +} + +func (m *progressTracker) SetTotalResources(i int) { + if !m.isTTY { + return + } + m.serviceBar.SetTotal(i) +} + +func (m *progressTracker) SetTotalServices(i int) { + m.serviceTotal = i +} + +func (m *progressTracker) SetServiceLabel(label string) { + if !m.isTTY { + return + } + m.serviceBar.SetLabel("└╴" + label) + m.serviceBar.SetCurrent(0) +} + +func (m *progressTracker) FinishService() { + if !m.isTTY { + return + } + m.serviceCurrent++ + m.serviceBar.Finish() +} + +func (m *progressTracker) StartService(name string) { + if !m.isTTY { + return + } + fmt.Printf("[%d/%d] Scanning %s...\n", m.serviceCurrent+1, m.serviceTotal, name) + m.serviceBar = bar.New( + bar.OptionHideOnFinish(true), + bar.OptionWithAutoComplete(false), + bar.OptionWithRenderFunc(bar.RenderColoured(0xff, 0x66, 0x00)), + ) + m.SetServiceLabel("Initializing...") +} diff --git a/pkg/cloud/aws/scanner/scanner.go b/pkg/cloud/aws/scanner/scanner.go new file mode 100644 index 000000000000..b67f90d068e7 --- /dev/null +++ b/pkg/cloud/aws/scanner/scanner.go @@ -0,0 +1,74 @@ +package scanner + +import ( + "context" + "strings" + + "github.com/aquasecurity/defsec/pkg/framework" + + "github.com/aquasecurity/trivy/pkg/flag" + "github.com/aquasecurity/trivy/pkg/log" + + "github.com/aquasecurity/defsec/pkg/scan" + "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws" + "github.com/aquasecurity/defsec/pkg/scanners/options" +) + +type AWSScanner struct { +} + +func NewScanner() *AWSScanner { + return &AWSScanner{} +} + +func (s *AWSScanner) Scan(ctx context.Context, option flag.Options) (scan.Results, error) { + + var scannerOpts []options.ScannerOption + if !option.NoProgress { + tracker := newProgressTracker() + defer tracker.Finish() + scannerOpts = append(scannerOpts, aws.ScannerWithProgressTracker(tracker)) + } + + if len(option.Services) > 0 { + scannerOpts = append(scannerOpts, aws.ScannerWithAWSServices(option.Services...)) + } + + if option.Debug { + scannerOpts = append(scannerOpts, options.ScannerWithDebug(&defsecLogger{})) + } + + if option.Region != "" { + scannerOpts = append( + scannerOpts, + aws.ScannerWithAWSRegion(option.Region), + ) + } + + if option.Endpoint != "" { + scannerOpts = append( + scannerOpts, + aws.ScannerWithAWSEndpoint(option.Endpoint), + ) + } + + scannerOpts = append(scannerOpts, options.ScannerWithFrameworks( + framework.Default, + framework.CIS_AWS_1_2, + )) + + defsecResults, err := aws.New(scannerOpts...).Scan(ctx) + if err != nil { + return nil, err + } + + return defsecResults, nil +} + +type defsecLogger struct { +} + +func (d *defsecLogger) Write(p []byte) (n int, err error) { + log.Logger.Debug("[defsec] " + strings.TrimSpace(string(p))) + return len(p), nil +} diff --git a/pkg/cloud/cache/cache.go b/pkg/cloud/cache/cache.go new file mode 100644 index 000000000000..22ddd8ccaaa5 --- /dev/null +++ b/pkg/cloud/cache/cache.go @@ -0,0 +1,65 @@ +package cache + +import ( + "fmt" + "path" + "path/filepath" + "strings" + "time" +) + +const ( + metadataFilename = "metadata.json" + cacheFilename = "cache.json" + dataDirName = "data" + cacheSubDir = "cloud" +) + +var ErrCacheNotFound = fmt.Errorf("cache record not found") + +type Cache struct { + path string + provider string + accountID string + region string + maxAge time.Duration +} + +func New(basePath string, maxAge time.Duration, provider string, accountID string, region string) *Cache { + return &Cache{ + path: path.Join(basePath, cacheSubDir, strings.ToLower(provider), accountID, strings.ToLower(region)), + provider: provider, + accountID: accountID, + region: region, + maxAge: maxAge, + } +} + +func (c *Cache) ListAvailableServices(includeExpired bool) []string { + metadata, err := c.loadMetadata() + if err != nil { + return nil + } + r, err := c.LoadReport(metadata.ServicesInScope...) + if err != nil { + return nil + } + var available []string + for _, service := range metadata.ServicesInScope { + if entry, ok := r.Results[service]; ok { + if includeExpired || entry.CreationTime.Add(c.maxAge).After(time.Now()) { + available = append(available, service) + } + } + } + return available +} + +func (c *Cache) getServicePath(service string) string { + service = strings.NewReplacer(" ", "_", ".", "_").Replace(service) + return filepath.Join(c.path, dataDirName, service, cacheFilename) +} + +func (c *Cache) getMetadataPath() string { + return filepath.Join(c.path, metadataFilename) +} diff --git a/pkg/cloud/cache/cache_test.go b/pkg/cloud/cache/cache_test.go new file mode 100644 index 000000000000..c3f8cc73877b --- /dev/null +++ b/pkg/cloud/cache/cache_test.go @@ -0,0 +1,166 @@ +package cache + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/cloud/report" +) + +func TestCache(t *testing.T) { + + tests := []struct { + name string + input report.Report + services []string + }{ + { + name: "no services", + input: report.Report{ + Provider: "AWS", + AccountID: "1234567890", + Region: "us-east-1", + Results: make(map[string]report.ResultsAtTime), + ServicesInScope: nil, + }, + services: nil, + }, + { + name: "all services", + input: report.Report{ + Provider: "AWS", + AccountID: "1234567890", + Region: "us-east-1", + Results: map[string]report.ResultsAtTime{ + "s3": { + Results: nil, + CreationTime: time.Now(), + }, + "ec2": { + Results: nil, + CreationTime: time.Now(), + }, + }, + ServicesInScope: []string{"ec2", "s3"}, + }, + services: []string{"ec2", "s3"}, + }, + { + name: "partial services", + input: report.Report{ + Provider: "AWS", + AccountID: "1234567890", + Region: "us-east-1", + Results: map[string]report.ResultsAtTime{ + "s3": { + Results: nil, + CreationTime: time.Now(), + }, + "ec2": { + Results: nil, + CreationTime: time.Now(), + }, + }, + ServicesInScope: []string{"ec2", "s3"}, + }, + services: []string{"ec2"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + + baseDir := t.TempDir() + + // ensure saving doesn't error + cache := New(baseDir, time.Hour, test.input.Provider, test.input.AccountID, test.input.Region) + require.NoError(t, cache.Save(&test.input)) + + // ensure all scoped services were cached + available := cache.ListAvailableServices(false) + assert.Equal(t, test.input.ServicesInScope, available) + + // ensure all cached services are really available + fullReport, err := cache.LoadReport(available...) + require.NoError(t, err) + assert.Equal(t, available, fullReport.ServicesInScope) + + // ensure loading restores all (specified) data + loaded, err := cache.LoadReport(test.services...) + require.NoError(t, err) + + assert.Equal(t, test.input.Provider, loaded.Provider) + assert.Equal(t, test.input.AccountID, loaded.AccountID) + assert.Equal(t, test.input.Region, loaded.Region) + assert.ElementsMatch(t, test.services, loaded.ServicesInScope) + + var actualServices []string + for service := range loaded.Results { + actualServices = append(actualServices, service) + } + assert.ElementsMatch(t, test.services, actualServices) + + for _, service := range test.services { + assert.Equal(t, test.input.Results[service].CreationTime.Format(time.RFC3339), loaded.Results[service].CreationTime.Format(time.RFC3339)) + assert.Equal(t, test.input.Results[service].Results, loaded.Results[service].Results) + } + }) + } +} + +func TestPartialCacheOverwrite(t *testing.T) { + baseDir := t.TempDir() + + r1 := report.Report{ + Provider: "AWS", + AccountID: "1234567890", + Region: "us-east-1", + Results: map[string]report.ResultsAtTime{ + "a": { + Results: nil, + CreationTime: time.Now(), + }, + "b": { + Results: nil, + CreationTime: time.Now(), + }, + "c": { + Results: nil, + CreationTime: time.Now(), + }, + "d": { + Results: nil, + CreationTime: time.Now(), + }, + }, + ServicesInScope: []string{"a", "b", "c", "d"}, + } + + // ensure saving doesn't error + cache := New(baseDir, time.Hour, "AWS", "1234567890", "us-east-1") + require.NoError(t, cache.Save(&r1)) + + r2 := report.Report{ + Provider: "AWS", + AccountID: "1234567890", + Region: "us-east-1", + Results: map[string]report.ResultsAtTime{ + "a": { + Results: nil, + CreationTime: time.Now(), + }, + "b": { + Results: nil, + CreationTime: time.Now(), + }, + }, + ServicesInScope: []string{"a", "b"}, + } + require.NoError(t, cache.Save(&r2)) + + assert.ElementsMatch(t, []string{"a", "b", "c", "d"}, cache.ListAvailableServices(false)) +} diff --git a/pkg/cloud/cache/load.go b/pkg/cloud/cache/load.go new file mode 100644 index 000000000000..b1593e1a368d --- /dev/null +++ b/pkg/cloud/cache/load.go @@ -0,0 +1,59 @@ +package cache + +import ( + "encoding/json" + "os" + + "github.com/aquasecurity/trivy/pkg/cloud/report" +) + +func (c *Cache) loadMetadata() (*Metadata, error) { + metadataFile := c.getMetadataPath() + m, err := os.Open(metadataFile) + if err != nil { + return nil, ErrCacheNotFound + } + + var metadata Metadata + if err := json.NewDecoder(m).Decode(&metadata); err != nil { + return nil, err + } + return &metadata, nil +} + +func (c *Cache) LoadReport(services ...string) (*report.Report, error) { + + metadata, err := c.loadMetadata() + if err != nil { + return nil, err + } + + base := report.New(c.provider, c.accountID, c.region, nil, nil) + + for _, service := range services { + if !contains(metadata.ServicesInScope, service) { + continue + } + serviceFile := c.getServicePath(service) + s, err := os.Open(serviceFile) + if err != nil { + return nil, err + } + var serviceRecord Record + if err := json.NewDecoder(s).Decode(&serviceRecord); err != nil { + return nil, err + } + base.AddResultsForService(service, serviceRecord.Results, serviceRecord.CreationTime) + } + + return base, nil +} + +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/pkg/cloud/cache/save.go b/pkg/cloud/cache/save.go new file mode 100644 index 000000000000..f8f55c5679c6 --- /dev/null +++ b/pkg/cloud/cache/save.go @@ -0,0 +1,77 @@ +package cache + +import ( + "encoding/json" + "os" + "path/filepath" + + "github.com/aquasecurity/trivy/pkg/cloud/report" +) + +func (c *Cache) Save(r *report.Report) error { + + existingServices := c.ListAvailableServices(true) + + if err := os.MkdirAll( + filepath.Dir(c.getMetadataPath()), + 0700, + ); err != nil { // only the current user is allowed to see this report + return err + } + + var retainedServices []string + for _, existing := range existingServices { + var found bool + for _, service := range r.ServicesInScope { + if service == existing { + found = true + break + } + } + if found { + continue + } + retainedServices = append(retainedServices, existing) + } + + for _, service := range r.ServicesInScope { + serviceFile := c.getServicePath(service) + if err := os.MkdirAll( + filepath.Dir(serviceFile), + 0700, + ); err != nil { + return err + } + resultSet, err := r.GetResultsForService(service) + if err != nil { + return err + } + s, err := os.Create(serviceFile) + if err != nil { + return err + } + record := Record{ + SchemaVersion: SchemaVersion, + Service: service, + Results: resultSet.Results, + CreationTime: resultSet.CreationTime, + } + if err := json.NewEncoder(s).Encode(record); err != nil { + return err + } + } + + metadataFile := c.getMetadataPath() + metadata := Metadata{ + SchemaVersion: SchemaVersion, + Provider: c.provider, + AccountID: c.accountID, + Region: c.region, + ServicesInScope: append(r.ServicesInScope, retainedServices...), + } + m, err := os.Create(metadataFile) + if err != nil { + return err + } + return json.NewEncoder(m).Encode(metadata) +} diff --git a/pkg/cloud/cache/schema.go b/pkg/cloud/cache/schema.go new file mode 100644 index 000000000000..5966b314a192 --- /dev/null +++ b/pkg/cloud/cache/schema.go @@ -0,0 +1,24 @@ +package cache + +import ( + "time" + + "github.com/aquasecurity/trivy/pkg/types" +) + +const SchemaVersion = 1 + +type Metadata struct { + SchemaVersion int `json:"schema_version"` + Provider string `json:"provider"` + AccountID string `json:"account_id"` + Region string `json:"region"` + ServicesInScope []string `json:"services"` +} + +type Record struct { + SchemaVersion int `json:"schema_version"` + Service string `json:"service"` + Results types.Results `json:"results"` + CreationTime time.Time `json:"creation_time"` +} diff --git a/pkg/cloud/provider.go b/pkg/cloud/provider.go new file mode 100644 index 000000000000..f495e15e2095 --- /dev/null +++ b/pkg/cloud/provider.go @@ -0,0 +1,5 @@ +package cloud + +const ( + ProviderAWS = "AWS" +) diff --git a/pkg/cloud/report/convert.go b/pkg/cloud/report/convert.go new file mode 100644 index 000000000000..b9b1dedc8487 --- /dev/null +++ b/pkg/cloud/report/convert.go @@ -0,0 +1,95 @@ +package report + +import ( + "fmt" + "strings" + "time" + + "github.com/aquasecurity/defsec/pkg/scan" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/types" +) + +func convertResults(results scan.Results, provider string, scoped []string) map[string]ResultsAtTime { + convertedResults := make(map[string]ResultsAtTime) + resultsByServiceAndARN := make(map[string]map[string]scan.Results) + for _, result := range results { + existingService, ok := resultsByServiceAndARN[result.Rule().Service] + if !ok { + existingService = make(map[string]scan.Results) + } + resource := result.Flatten().Resource + + existingService[resource] = append(existingService[resource], result) + resultsByServiceAndARN[result.Rule().Service] = existingService + } + // ensure we have entries for all scoped services, even if there are no results + for _, service := range scoped { + if _, ok := resultsByServiceAndARN[service]; !ok { + resultsByServiceAndARN[service] = nil + } + } + for service, arnResults := range resultsByServiceAndARN { + + var convertedArnResults []types.Result + + for arn, serviceResults := range arnResults { + + arnResult := types.Result{ + Target: arn, + Class: types.ClassConfig, + Type: ftypes.Cloud, + } + + for _, result := range serviceResults { + + var primaryURL string + + // empty namespace implies a go rule from defsec, "builtin" refers to a built-in rego rule + // this ensures we don't generate bad links for custom policies + if result.RegoNamespace() == "" || strings.HasPrefix(result.RegoNamespace(), "builtin.") { + primaryURL = fmt.Sprintf("https://avd.aquasec.com/misconfig/%s", strings.ToLower(result.Rule().AVDID)) + } + + status := types.StatusFailure + switch result.Status() { + case scan.StatusPassed: + status = types.StatusPassed + case scan.StatusIgnored: + status = types.StatusException + } + + flat := result.Flatten() + + arnResult.Misconfigurations = append(arnResult.Misconfigurations, types.DetectedMisconfiguration{ + Type: provider, + ID: result.Rule().AVDID, + Title: result.Rule().Summary, + Description: strings.TrimSpace(result.Rule().Explanation), + Message: strings.TrimSpace(result.Description()), + Namespace: result.RegoNamespace(), + Query: result.RegoRule(), + Resolution: result.Rule().Resolution, + Severity: string(result.Severity()), + PrimaryURL: primaryURL, + References: []string{primaryURL}, + Status: status, + CauseMetadata: ftypes.CauseMetadata{ + Resource: flat.Resource, + Provider: string(flat.RuleProvider), + Service: flat.RuleService, + StartLine: flat.Location.StartLine, + EndLine: flat.Location.EndLine, + }, + }) + } + + convertedArnResults = append(convertedArnResults, arnResult) + } + convertedResults[service] = ResultsAtTime{ + Results: convertedArnResults, + CreationTime: time.Now(), + } + } + return convertedResults +} diff --git a/pkg/cloud/report/convert_test.go b/pkg/cloud/report/convert_test.go new file mode 100644 index 000000000000..6700ec6ac00b --- /dev/null +++ b/pkg/cloud/report/convert_test.go @@ -0,0 +1,241 @@ +package report + +import ( + "sort" + "testing" + + fanaltypes "github.com/aquasecurity/trivy/pkg/fanal/types" + + "github.com/aws/aws-sdk-go-v2/aws/arn" + + defsecTypes "github.com/aquasecurity/defsec/pkg/types" + "github.com/aquasecurity/trivy/pkg/types" + + "github.com/stretchr/testify/assert" + + "github.com/aquasecurity/defsec/pkg/scan" +) + +func Test_ResultConversion(t *testing.T) { + + tests := []struct { + name string + results scan.Results + provider string + scoped []string + expected map[string]ResultsAtTime + }{ + { + name: "no results", + results: scan.Results{}, + provider: "AWS", + expected: make(map[string]ResultsAtTime), + }, + { + name: "no results, multiple scoped services", + results: scan.Results{}, + provider: "AWS", + scoped: []string{"s3", "ec2"}, + expected: map[string]ResultsAtTime{ + "s3": {}, + "ec2": {}, + }, + }, + { + name: "multiple results", + results: func() scan.Results { + + baseRule := scan.Rule{ + AVDID: "AVD-AWS-9999", + Aliases: []string{"AWS999"}, + ShortCode: "no-bad-stuff", + Summary: "Do not use bad stuff", + Explanation: "Bad stuff is... bad", + Impact: "Bad things", + Resolution: "Remove bad stuff", + Provider: "AWS", + Severity: "HIGH", + } + + var s3Results scan.Results + s3Results.Add( + "something failed", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket1", + }).String()), + ) + s3Results.Add( + "something else failed", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket2", + }).String()), + ) + s3Results.Add( + "something else failed again", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket2", + }).String()), + ) + baseRule.Service = "s3" + s3Results.SetRule(baseRule) + var ec2Results scan.Results + ec2Results.Add( + "instance is bad", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "ec2", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "instance1", + }).String()), + ) + baseRule.Service = "ec2" + ec2Results.SetRule(baseRule) + return append(s3Results, ec2Results...) + }(), + provider: "AWS", + expected: map[string]ResultsAtTime{ + "s3": { + Results: types.Results{ + { + Target: "arn:aws:s3:us-east-1:1234567890:bucket1", + Class: "config", + Type: "cloud", + Misconfigurations: []types.DetectedMisconfiguration{ + { + Type: "AWS", + ID: "AVD-AWS-9999", + Title: "Do not use bad stuff", + Description: "Bad stuff is... bad", + Message: "something failed", + Resolution: "Remove bad stuff", + Severity: "HIGH", + PrimaryURL: "https://avd.aquasec.com/misconfig/avd-aws-9999", + References: []string{ + "https://avd.aquasec.com/misconfig/avd-aws-9999", + }, + Status: "FAIL", + CauseMetadata: fanaltypes.CauseMetadata{ + Resource: "arn:aws:s3:us-east-1:1234567890:bucket1", + Provider: "AWS", + Service: "s3", + StartLine: 0, + EndLine: 0, + Code: fanaltypes.Code{}, + }, + }, + }, + }, + { + Target: "arn:aws:s3:us-east-1:1234567890:bucket2", + Class: "config", + Type: "cloud", + Misconfigurations: []types.DetectedMisconfiguration{ + { + Type: "AWS", + ID: "AVD-AWS-9999", + Title: "Do not use bad stuff", + Description: "Bad stuff is... bad", + Message: "something else failed", + Resolution: "Remove bad stuff", + Severity: "HIGH", + PrimaryURL: "https://avd.aquasec.com/misconfig/avd-aws-9999", + References: []string{ + "https://avd.aquasec.com/misconfig/avd-aws-9999", + }, + Status: "FAIL", + CauseMetadata: fanaltypes.CauseMetadata{ + Resource: "arn:aws:s3:us-east-1:1234567890:bucket2", + Provider: "AWS", + Service: "s3", + }, + }, + { + Type: "AWS", + ID: "AVD-AWS-9999", + Title: "Do not use bad stuff", + Description: "Bad stuff is... bad", + Message: "something else failed again", + Resolution: "Remove bad stuff", + Severity: "HIGH", + PrimaryURL: "https://avd.aquasec.com/misconfig/avd-aws-9999", + References: []string{ + "https://avd.aquasec.com/misconfig/avd-aws-9999", + }, + Status: "FAIL", + CauseMetadata: fanaltypes.CauseMetadata{ + Resource: "arn:aws:s3:us-east-1:1234567890:bucket2", + Provider: "AWS", + Service: "s3", + }, + }, + }, + }, + }, + }, + "ec2": { + Results: types.Results{ + { + Target: "arn:aws:ec2:us-east-1:1234567890:instance1", + Class: "config", + Type: "cloud", + Misconfigurations: []types.DetectedMisconfiguration{ + { + Type: "AWS", + ID: "AVD-AWS-9999", + Title: "Do not use bad stuff", + Description: "Bad stuff is... bad", + Message: "instance is bad", + Resolution: "Remove bad stuff", + Severity: "HIGH", + PrimaryURL: "https://avd.aquasec.com/misconfig/avd-aws-9999", + References: []string{ + "https://avd.aquasec.com/misconfig/avd-aws-9999", + }, + Status: "FAIL", + CauseMetadata: fanaltypes.CauseMetadata{ + Resource: "arn:aws:ec2:us-east-1:1234567890:instance1", + Provider: "AWS", + Service: "ec2", + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + converted := convertResults(test.results, test.provider, test.scoped) + assertConvertedResultsMatch(t, test.expected, converted) + }) + } + +} + +func assertConvertedResultsMatch(t *testing.T, expected, actual map[string]ResultsAtTime) { + assert.Equal(t, len(expected), len(actual)) + for service, resultsAtTime := range expected { + _, ok := actual[service] + assert.True(t, ok) + sort.Slice(actual[service].Results, func(i, j int) bool { + return actual[service].Results[i].Target < actual[service].Results[j].Target + }) + assert.ElementsMatch(t, resultsAtTime.Results, actual[service].Results) + } +} diff --git a/pkg/cloud/report/report.go b/pkg/cloud/report/report.go new file mode 100644 index 000000000000..efdbe04c6579 --- /dev/null +++ b/pkg/cloud/report/report.go @@ -0,0 +1,175 @@ +package report + +import ( + "context" + "fmt" + "os" + "sort" + "time" + + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + + "github.com/liamg/tml" + + "github.com/aquasecurity/trivy/pkg/flag" + + "github.com/aquasecurity/trivy/pkg/report" + + "github.com/aquasecurity/trivy/pkg/result" + + "github.com/aquasecurity/defsec/pkg/scan" + pkgReport "github.com/aquasecurity/trivy/pkg/report" + "github.com/aquasecurity/trivy/pkg/types" +) + +const ( + tableFormat = "table" +) + +// Report represents a kubernetes scan report +type Report struct { + Provider string + AccountID string + Region string + Results map[string]ResultsAtTime + ServicesInScope []string +} + +type ResultsAtTime struct { + Results types.Results + CreationTime time.Time +} + +func New(provider, accountID, region string, defsecResults scan.Results, scopedServices []string) *Report { + return &Report{ + Provider: provider, + AccountID: accountID, + Results: convertResults(defsecResults, provider, scopedServices), + ServicesInScope: scopedServices, + Region: region, + } +} + +// Failed returns whether the aws report includes any "failed" results +func (r *Report) Failed() bool { + for _, set := range r.Results { + if set.Results.Failed() { + return true + } + } + return false +} + +// Write writes the results in the give format +func Write(rep *Report, opt flag.Options, fromCache bool) error { + + var filtered []types.Result + + ctx := context.Background() + + // filter results + for _, resultsAtTime := range rep.Results { + for _, res := range resultsAtTime.Results { + resCopy := res + if err := result.Filter( + ctx, + &resCopy, + opt.Severities, + false, + false, + "", + "", + nil, + ); err != nil { + return err + } + sort.Slice(resCopy.Misconfigurations, func(i, j int) bool { + return resCopy.Misconfigurations[i].CauseMetadata.Resource < resCopy.Misconfigurations[i].CauseMetadata.Resource + }) + filtered = append(filtered, resCopy) + } + } + sort.Slice(filtered, func(i, j int) bool { + return filtered[i].Target < filtered[j].Target + }) + + base := types.Report{ + ArtifactName: rep.AccountID, + ArtifactType: ftypes.ArtifactAWSAccount, + Results: filtered, + } + + switch opt.Format { + case tableFormat: + + // ensure color/formatting is disabled for pipes/non-pty + var useANSI bool + if opt.Output == os.Stdout { + if o, err := os.Stdout.Stat(); err == nil { + useANSI = (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice + } + } + if !useANSI { + tml.DisableFormatting() + } + + switch { + case len(opt.Services) == 1 && opt.ARN == "": + if err := writeResourceTable(rep, filtered, opt.Output, opt.Services[0]); err != nil { + return err + } + case len(opt.Services) == 1 && opt.ARN != "": + if err := writeResultsForARN(rep, filtered, opt.Output, opt.Services[0], opt.ARN, opt.Severities); err != nil { + return err + } + default: + if err := writeServiceTable(rep, filtered, opt.Output); err != nil { + return err + } + } + + // render cache info + if fromCache { + _ = tml.Fprintf(opt.Output, "\nThis scan report was loaded from cached results. If you'd like to run a fresh scan, use --update-cache.\n") + } + + return nil + default: + return report.Write(base, pkgReport.Option{ + Format: opt.Format, + Output: opt.Output, + Severities: opt.Severities, + OutputTemplate: opt.Template, + IncludeNonFailures: opt.IncludeNonFailures, + Trace: opt.Trace, + }) + } +} + +func (r *Report) GetResultsForService(service string) (*ResultsAtTime, error) { + if set, ok := r.Results[service]; ok { + return &set, nil + } + for _, scoped := range r.ServicesInScope { + if scoped == service { + return &ResultsAtTime{ + Results: nil, + CreationTime: time.Now(), + }, nil + } + } + return nil, fmt.Errorf("service %q not found", service) +} + +func (r *Report) AddResultsForService(service string, results types.Results, creation time.Time) { + r.Results[service] = ResultsAtTime{ + Results: results, + CreationTime: creation, + } + for _, exists := range r.ServicesInScope { + if exists == service { + return + } + } + r.ServicesInScope = append(r.ServicesInScope, service) +} diff --git a/pkg/cloud/report/resource.go b/pkg/cloud/report/resource.go new file mode 100644 index 000000000000..27dd83b1117c --- /dev/null +++ b/pkg/cloud/report/resource.go @@ -0,0 +1,89 @@ +package report + +import ( + "fmt" + "io" + "sort" + "strconv" + + "github.com/liamg/tml" + + "golang.org/x/term" + + "github.com/aquasecurity/table" + pkgReport "github.com/aquasecurity/trivy/pkg/report/table" + "github.com/aquasecurity/trivy/pkg/types" +) + +type sortableRow struct { + name string + counts map[string]int +} + +func writeResourceTable(report *Report, results types.Results, output io.Writer, service string) error { + + termWidth, _, err := term.GetSize(0) + if err != nil { + termWidth = 80 + } + maxWidth := termWidth - 48 + if maxWidth < 20 { + maxWidth = 20 + } + + t := table.New(output) + t.SetColumnMaxWidth(maxWidth) + t.SetHeaders("Resource", "Misconfigurations") + t.AddHeaders("Resource", "Critical", "High", "Medium", "Low", "Unknown") + t.SetHeaderVerticalAlignment(table.AlignBottom) + t.SetHeaderAlignment(table.AlignLeft, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter) + t.SetAlignment(table.AlignLeft, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight) + t.SetRowLines(false) + t.SetAutoMergeHeaders(true) + t.SetHeaderColSpans(0, 1, 5) + + // map resource -> severity -> count + grouped := make(map[string]map[string]int) + for _, result := range results { + for _, misconfiguration := range result.Misconfigurations { + if misconfiguration.CauseMetadata.Service != service { + continue + } + if _, ok := grouped[misconfiguration.CauseMetadata.Resource]; !ok { + grouped[misconfiguration.CauseMetadata.Resource] = make(map[string]int) + } + grouped[misconfiguration.CauseMetadata.Resource][misconfiguration.Severity]++ + } + } + + var sortable []sortableRow + for resource, severityCounts := range grouped { + sortable = append(sortable, sortableRow{ + name: resource, + counts: severityCounts, + }) + } + sort.Slice(sortable, func(i, j int) bool { return sortable[i].name < sortable[j].name }) + for _, row := range sortable { + t.AddRow( + row.name, + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["CRITICAL"]), "CRITICAL"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["HIGH"]), "HIGH"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["MEDIUM"]), "MEDIUM"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["LOW"]), "LOW"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["UNKNOWN"]), "UNKNOWN"), + ) + } + + // render scan title + _ = tml.Fprintf(output, "\nResource Summary for Service '%s' (%s Account %s)\n", service, report.Provider, report.AccountID) + + // render table + if len(sortable) > 0 { + t.Render() + } else { + _, _ = fmt.Fprint(output, "\nNo problems detected.\n") + } + + return nil +} diff --git a/pkg/cloud/report/resource_test.go b/pkg/cloud/report/resource_test.go new file mode 100644 index 000000000000..dbe070cff93b --- /dev/null +++ b/pkg/cloud/report/resource_test.go @@ -0,0 +1,123 @@ +package report + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/flag" +) + +func Test_ResourceReport(t *testing.T) { + tests := []struct { + name string + options flag.Options + fromCache bool + expected string + }{ + { + name: "simple table output", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"s3"}, + }, + }, + fromCache: false, + expected: ` +Resource Summary for Service 's3' (AWS Account ) +┌─────────────────────────────────────────┬──────────────────────────────────────────┐ +│ │ Misconfigurations │ +│ ├──────────┬──────┬────────┬─────┬─────────┤ +│ Resource │ Critical │ High │ Medium │ Low │ Unknown │ +├─────────────────────────────────────────┼──────────┼──────┼────────┼─────┼─────────┤ +│ arn:aws:s3:us-east-1:1234567890:bucket1 │ 0 │ 1 │ 0 │ 0 │ 0 │ +│ arn:aws:s3:us-east-1:1234567890:bucket2 │ 0 │ 2 │ 0 │ 0 │ 0 │ +└─────────────────────────────────────────┴──────────┴──────┴────────┴─────┴─────────┘ +`, + }, + { + name: "results from cache", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"s3"}, + }, + }, + fromCache: true, + expected: ` +Resource Summary for Service 's3' (AWS Account ) +┌─────────────────────────────────────────┬──────────────────────────────────────────┐ +│ │ Misconfigurations │ +│ ├──────────┬──────┬────────┬─────┬─────────┤ +│ Resource │ Critical │ High │ Medium │ Low │ Unknown │ +├─────────────────────────────────────────┼──────────┼──────┼────────┼─────┼─────────┤ +│ arn:aws:s3:us-east-1:1234567890:bucket1 │ 0 │ 1 │ 0 │ 0 │ 0 │ +│ arn:aws:s3:us-east-1:1234567890:bucket2 │ 0 │ 2 │ 0 │ 0 │ 0 │ +└─────────────────────────────────────────┴──────────┴──────┴────────┴─────┴─────────┘ + +This scan report was loaded from cached results. If you'd like to run a fresh scan, use --update-cache. +`, + }, + { + name: "no problems", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"s3"}, + }, + }, + fromCache: false, + expected: ` +Resource Summary for Service 's3' (AWS Account ) + +No problems detected. +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + report := New( + "AWS", + tt.options.AWSOptions.Account, + tt.options.AWSOptions.Region, + createTestResults(), + tt.options.AWSOptions.Services, + ) + + buffer := bytes.NewBuffer([]byte{}) + tt.options.Output = buffer + require.NoError(t, Write(report, tt.options, tt.fromCache)) + + assert.Equal(t, "AWS", report.Provider) + assert.Equal(t, tt.options.AWSOptions.Account, report.AccountID) + assert.Equal(t, tt.options.AWSOptions.Region, report.Region) + assert.ElementsMatch(t, tt.options.AWSOptions.Services, report.ServicesInScope) + assert.Equal(t, tt.expected, buffer.String()) + }) + } +} diff --git a/pkg/cloud/report/result.go b/pkg/cloud/report/result.go new file mode 100644 index 000000000000..e7ff103845a4 --- /dev/null +++ b/pkg/cloud/report/result.go @@ -0,0 +1,37 @@ +package report + +import ( + "fmt" + "io" + + "github.com/liamg/tml" + + renderer "github.com/aquasecurity/trivy/pkg/report/table" + + dbTypes "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/types" +) + +func writeResultsForARN(report *Report, results types.Results, output io.Writer, service, arn string, severities []dbTypes.Severity) error { + + // render scan title + _ = tml.Fprintf(output, "\nResults for '%s' (%s Account %s)\n\n", arn, report.Provider, report.AccountID) + + for _, result := range results { + var filtered []types.DetectedMisconfiguration + for _, misconfiguration := range result.Misconfigurations { + if arn != "" && misconfiguration.CauseMetadata.Resource != arn { + continue + } + if service != "" && misconfiguration.CauseMetadata.Service != service { + continue + } + filtered = append(filtered, misconfiguration) + } + if len(filtered) > 0 { + _, _ = fmt.Fprint(output, renderer.NewMisconfigRenderer(result, severities, false, false, true).Render()) + } + } + + return nil +} diff --git a/pkg/cloud/report/result_test.go b/pkg/cloud/report/result_test.go new file mode 100644 index 000000000000..e12f63b19fd8 --- /dev/null +++ b/pkg/cloud/report/result_test.go @@ -0,0 +1,82 @@ +package report + +import ( + "bytes" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy-db/pkg/types" + "github.com/aquasecurity/trivy/pkg/flag" +) + +func Test_ARNReport(t *testing.T) { + tests := []struct { + name string + options flag.Options + fromCache bool + expected string + }{ + { + name: "simple output", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"s3"}, + ARN: "arn:aws:s3:us-east-1:1234567890:bucket1", + Account: "1234567890", + }, + }, + fromCache: false, + expected: ` +Results for 'arn:aws:s3:us-east-1:1234567890:bucket1' (AWS Account 1234567890) + + +arn:aws:s3:us-east-1:1234567890:bucket1 (cloud) + +Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) + +HIGH: something failed +════════════════════════════════════════ +Bad stuff is... bad + +See https://avd.aquasec.com/misconfig/avd-aws-9999 +──────────────────────────────────────── + + +`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + report := New( + "AWS", + tt.options.AWSOptions.Account, + tt.options.AWSOptions.Region, + createTestResults(), + tt.options.AWSOptions.Services, + ) + + buffer := bytes.NewBuffer([]byte{}) + tt.options.Output = buffer + require.NoError(t, Write(report, tt.options, tt.fromCache)) + + assert.Equal(t, "AWS", report.Provider) + assert.Equal(t, tt.options.AWSOptions.Account, report.AccountID) + assert.Equal(t, tt.options.AWSOptions.Region, report.Region) + assert.ElementsMatch(t, tt.options.AWSOptions.Services, report.ServicesInScope) + assert.Equal(t, tt.expected, strings.ReplaceAll(buffer.String(), "\r\n", "\n")) + }) + } +} diff --git a/pkg/cloud/report/service.go b/pkg/cloud/report/service.go new file mode 100644 index 000000000000..f334418a5afe --- /dev/null +++ b/pkg/cloud/report/service.go @@ -0,0 +1,86 @@ +package report + +import ( + "fmt" + "io" + "sort" + "strconv" + "time" + + "github.com/liamg/tml" + + "github.com/aquasecurity/table" + pkgReport "github.com/aquasecurity/trivy/pkg/report/table" + "github.com/aquasecurity/trivy/pkg/types" +) + +func writeServiceTable(report *Report, results types.Results, output io.Writer) error { + + t := table.New(output) + + t.SetHeaders("Service", "Misconfigurations", "Last Scanned") + t.AddHeaders("Service", "Critical", "High", "Medium", "Low", "Unknown", "Last Scanned") + t.SetRowLines(false) + t.SetHeaderVerticalAlignment(table.AlignBottom) + t.SetHeaderAlignment(table.AlignLeft, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignLeft) + t.SetAlignment(table.AlignLeft, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignLeft) + t.SetAutoMergeHeaders(true) + t.SetHeaderColSpans(0, 1, 5, 1) + + // map service -> severity -> count + grouped := make(map[string]map[string]int) + // set zero counts for all services + for _, service := range report.ServicesInScope { + grouped[service] = make(map[string]int) + } + for _, result := range results { + for _, misconfiguration := range result.Misconfigurations { + service := misconfiguration.CauseMetadata.Service + if _, ok := grouped[service]; !ok { + grouped[service] = make(map[string]int) + } + grouped[service][misconfiguration.Severity]++ + } + } + + var sortable []sortableRow + for service, severityCounts := range grouped { + sortable = append(sortable, sortableRow{ + name: service, + counts: severityCounts, + }) + } + sort.Slice(sortable, func(i, j int) bool { return sortable[i].name < sortable[j].name }) + for _, row := range sortable { + var lastScanned string + scanAgo := time.Since(report.Results[row.name].CreationTime).Truncate(time.Minute) + switch { + case scanAgo.Hours() >= 48: + lastScanned = fmt.Sprintf("%d days ago", int(scanAgo.Hours()/24)) + case scanAgo.Hours() > 1: + lastScanned = fmt.Sprintf("%d hours ago", int(scanAgo.Hours())) + case scanAgo.Minutes() > 1: + lastScanned = fmt.Sprintf("%d minutes ago", int(scanAgo.Minutes())) + default: + lastScanned = "just now" + } + + t.AddRow( + row.name, + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["CRITICAL"]), "CRITICAL"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["HIGH"]), "HIGH"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["MEDIUM"]), "MEDIUM"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["LOW"]), "LOW"), + pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["UNKNOWN"]), "UNKNOWN"), + lastScanned, + ) + } + + // render scan title + _ = tml.Fprintf(output, "\nScan Overview for %s Account %s\n", report.Provider, report.AccountID) + + // render table + t.Render() + + return nil +} diff --git a/pkg/cloud/report/service_test.go b/pkg/cloud/report/service_test.go new file mode 100644 index 000000000000..d357d4262a48 --- /dev/null +++ b/pkg/cloud/report/service_test.go @@ -0,0 +1,407 @@ +package report + +import ( + "bytes" + "testing" + + "github.com/aquasecurity/trivy-db/pkg/types" + + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/flag" + + "github.com/stretchr/testify/assert" + + "github.com/aws/aws-sdk-go-v2/aws/arn" + + "github.com/aquasecurity/defsec/pkg/scan" + defsecTypes "github.com/aquasecurity/defsec/pkg/types" +) + +func Test_ServiceReport(t *testing.T) { + tests := []struct { + name string + options flag.Options + fromCache bool + expected string + }{ + { + name: "simple table output", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + }, + fromCache: false, + expected: ` +Scan Overview for AWS Account +┌─────────┬──────────────────────────────────────────────────┬──────────────┐ +│ │ Misconfigurations │ │ +│ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ +│ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ +├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ +│ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ +│ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ +└─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ +`, + }, + { + name: "results from cache", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + }, + fromCache: true, + expected: ` +Scan Overview for AWS Account +┌─────────┬──────────────────────────────────────────────────┬──────────────┐ +│ │ Misconfigurations │ │ +│ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ +│ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ +├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ +│ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ +│ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ +└─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ + +This scan report was loaded from cached results. If you'd like to run a fresh scan, use --update-cache. +`, + }, + { + name: "filter severities", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityMedium, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"s3", "ec2"}, + }, + }, + fromCache: false, + expected: ` +Scan Overview for AWS Account +┌─────────┬──────────────────────────────────────────────────┬──────────────┐ +│ │ Misconfigurations │ │ +│ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ +│ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ +├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ +│ ec2 │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ +│ s3 │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ +└─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ +`, + }, + { + name: "scoped services without results", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: tableFormat, + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + AWSOptions: flag.AWSOptions{ + Services: []string{"ec2", "s3", "iam"}, + }, + }, + fromCache: false, + expected: ` +Scan Overview for AWS Account +┌─────────┬──────────────────────────────────────────────────┬──────────────┐ +│ │ Misconfigurations │ │ +│ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ +│ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ +├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ +│ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ +│ iam │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ +│ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ +└─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ +`, + }, + { + name: "json output", + options: flag.Options{ + ReportOptions: flag.ReportOptions{ + Format: "json", + Severities: []types.Severity{ + types.SeverityLow, + types.SeverityMedium, + types.SeverityHigh, + types.SeverityCritical, + }, + }, + }, + fromCache: false, + expected: `{ + "ArtifactType": "aws_account", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "arn:aws:ec2:us-east-1:1234567890:instance1", + "Class": "config", + "Type": "cloud", + "MisconfSummary": { + "Successes": 0, + "Failures": 1, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "AWS", + "ID": "AVD-AWS-9999", + "Title": "Do not use bad stuff", + "Description": "Bad stuff is... bad", + "Message": "instance is bad", + "Resolution": "Remove bad stuff", + "Severity": "HIGH", + "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", + "References": [ + "https://avd.aquasec.com/misconfig/avd-aws-9999" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Resource": "arn:aws:ec2:us-east-1:1234567890:instance1", + "Provider": "AWS", + "Service": "ec2", + "Code": { + "Lines": null + } + } + } + ] + }, + { + "Target": "arn:aws:s3:us-east-1:1234567890:bucket1", + "Class": "config", + "Type": "cloud", + "MisconfSummary": { + "Successes": 0, + "Failures": 1, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "AWS", + "ID": "AVD-AWS-9999", + "Title": "Do not use bad stuff", + "Description": "Bad stuff is... bad", + "Message": "something failed", + "Resolution": "Remove bad stuff", + "Severity": "HIGH", + "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", + "References": [ + "https://avd.aquasec.com/misconfig/avd-aws-9999" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Resource": "arn:aws:s3:us-east-1:1234567890:bucket1", + "Provider": "AWS", + "Service": "s3", + "Code": { + "Lines": null + } + } + } + ] + }, + { + "Target": "arn:aws:s3:us-east-1:1234567890:bucket2", + "Class": "config", + "Type": "cloud", + "MisconfSummary": { + "Successes": 0, + "Failures": 2, + "Exceptions": 0 + }, + "Misconfigurations": [ + { + "Type": "AWS", + "ID": "AVD-AWS-9999", + "Title": "Do not use bad stuff", + "Description": "Bad stuff is... bad", + "Message": "something else failed", + "Resolution": "Remove bad stuff", + "Severity": "HIGH", + "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", + "References": [ + "https://avd.aquasec.com/misconfig/avd-aws-9999" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Resource": "arn:aws:s3:us-east-1:1234567890:bucket2", + "Provider": "AWS", + "Service": "s3", + "Code": { + "Lines": null + } + } + }, + { + "Type": "AWS", + "ID": "AVD-AWS-9999", + "Title": "Do not use bad stuff", + "Description": "Bad stuff is... bad", + "Message": "something else failed again", + "Resolution": "Remove bad stuff", + "Severity": "HIGH", + "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", + "References": [ + "https://avd.aquasec.com/misconfig/avd-aws-9999" + ], + "Status": "FAIL", + "Layer": {}, + "CauseMetadata": { + "Resource": "arn:aws:s3:us-east-1:1234567890:bucket2", + "Provider": "AWS", + "Service": "s3", + "Code": { + "Lines": null + } + } + } + ] + }, + { + "Target": "arn:aws:s3:us-east-1:1234567890:bucket3", + "Class": "config", + "Type": "cloud", + "MisconfSummary": { + "Successes": 1, + "Failures": 0, + "Exceptions": 0 + } + } + ] +}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + report := New( + "AWS", + tt.options.AWSOptions.Account, + tt.options.AWSOptions.Region, + createTestResults(), + tt.options.AWSOptions.Services, + ) + + buffer := bytes.NewBuffer([]byte{}) + tt.options.Output = buffer + require.NoError(t, Write(report, tt.options, tt.fromCache)) + + assert.Equal(t, "AWS", report.Provider) + assert.Equal(t, tt.options.AWSOptions.Account, report.AccountID) + assert.Equal(t, tt.options.AWSOptions.Region, report.Region) + assert.ElementsMatch(t, tt.options.AWSOptions.Services, report.ServicesInScope) + if tt.options.Format == "json" { + // json output can be formatted/ordered differently - we just care that the data matches + assert.JSONEq(t, tt.expected, buffer.String()) + } else { + assert.Equal(t, tt.expected, buffer.String()) + } + }) + } +} + +func createTestResults() scan.Results { + + baseRule := scan.Rule{ + AVDID: "AVD-AWS-9999", + Aliases: []string{"AWS999"}, + ShortCode: "no-bad-stuff", + Summary: "Do not use bad stuff", + Explanation: "Bad stuff is... bad", + Impact: "Bad things", + Resolution: "Remove bad stuff", + Provider: "AWS", + Severity: "HIGH", + } + + var s3Results scan.Results + s3Results.Add( + "something failed", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket1", + }).String()), + ) + s3Results.Add( + "something else failed", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket2", + }).String()), + ) + s3Results.Add( + "something else failed again", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket2", + }).String()), + ) + s3Results.AddPassed( + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "s3", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "bucket3", + }).String()), + ) + baseRule.Service = "s3" + s3Results.SetRule(baseRule) + var ec2Results scan.Results + ec2Results.Add( + "instance is bad", + defsecTypes.NewRemoteMetadata((arn.ARN{ + Partition: "aws", + Service: "ec2", + Region: "us-east-1", + AccountID: "1234567890", + Resource: "instance1", + }).String()), + ) + baseRule.Service = "ec2" + ec2Results.SetRule(baseRule) + return append(s3Results, ec2Results...) +} diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 89251033faf2..4994e108aeb9 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -6,12 +6,17 @@ import ( "fmt" "io" "os" + "strings" + "time" + + awsScanner "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/xerrors" "github.com/aquasecurity/trivy-db/pkg/metadata" + awscommands "github.com/aquasecurity/trivy/pkg/cloud/aws/commands" "github.com/aquasecurity/trivy/pkg/commands/artifact" "github.com/aquasecurity/trivy/pkg/commands/server" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" @@ -81,6 +86,7 @@ func NewApp(version string) *cobra.Command { NewKubernetesCommand(globalFlags), NewSBOMCommand(globalFlags), NewVersionCommand(globalFlags), + NewAWSCommand(globalFlags), ) rootCmd.AddCommand(loadPluginCommands()...) @@ -787,6 +793,66 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { return cmd } +func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { + + awsFlags := &flag.Flags{ + AWSFlagGroup: flag.NewAWSFlagGroup(), + CloudFlagGroup: flag.NewCloudFlagGroup(), + MisconfFlagGroup: flag.NewMisconfFlagGroup(), + ReportFlagGroup: flag.NewReportFlagGroup(), + } + + services := awsScanner.AllSupportedServices() + + cmd := &cobra.Command{ + Use: "aws [flags]", + Aliases: []string{}, + Args: cobra.ExactArgs(0), + Short: "scan aws account", + Long: fmt.Sprintf(`Scan an AWS account for misconfigurations. Trivy uses the same authentication methods as the AWS CLI. See https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html + +The following services are supported: +- %s +`, strings.Join(services, "\n- ")), + Example: ` # basic scanning + $ trivy aws --region us-east-1 + + # limit scan to a single service: + $ trivy aws --region us-east-1 --service s3 + + # limit scan to multiple services: + $ trivy aws --region us-east-1 --service s3 --service ec2 + + # force refresh of cache for fresh results + $ trivy aws --region us-east-1 --update-cache +`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := awsFlags.Bind(cmd); err != nil { + return xerrors.Errorf("flag bind error: %w", err) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + opts, err := awsFlags.ToOptions(cmd.Version, args, globalFlags, outputWriter) + if err != nil { + return xerrors.Errorf("flag error: %w", err) + } + if opts.Timeout < time.Hour { + opts.Timeout = time.Hour + log.Logger.Debug("Timeout is set to less than 1 hour - upgrading to 1 hour for this command.") + } + return awscommands.Run(cmd.Context(), opts) + }, + SilenceErrors: true, + SilenceUsage: true, + } + cmd.SetFlagErrorFunc(flagErrorFunc) + awsFlags.AddFlags(cmd) + cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, awsFlags.Usages(cmd))) + + return cmd +} + func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { reportFlagGroup := flag.NewReportFlagGroup() reportFlagGroup.DependencyTree = nil // disable '--dependency-tree' diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index f73c4ef9ccab..52bb0e08898a 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -533,7 +533,7 @@ func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeSc report, err := s.ScanArtifact(ctx, scanOptions) if err != nil { - return types.Report{}, xerrors.Errorf("image scan failed: %w", err) + return types.Report{}, xerrors.Errorf("scan failed: %w", err) } return report, nil } diff --git a/pkg/fanal/types/artifact.go b/pkg/fanal/types/artifact.go index a424f8e5f71e..c2699a014dc2 100644 --- a/pkg/fanal/types/artifact.go +++ b/pkg/fanal/types/artifact.go @@ -95,6 +95,7 @@ const ( ArtifactFilesystem ArtifactType = "filesystem" ArtifactRemoteRepository ArtifactType = "repository" ArtifactCycloneDX ArtifactType = "cyclonedx" + ArtifactAWSAccount ArtifactType = "aws_account" ) // ArtifactReference represents a reference of container image, local filesystem and repository diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index 5b1291097f7e..6594fd936fcb 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -38,6 +38,7 @@ const ( Ansible = "ansible" Helm = "helm" Rbac = "rbac" + Cloud = "cloud" // Licensing License = "license" diff --git a/pkg/flag/aws_flags.go b/pkg/flag/aws_flags.go new file mode 100644 index 000000000000..2ccb742a52cc --- /dev/null +++ b/pkg/flag/aws_flags.go @@ -0,0 +1,78 @@ +package flag + +var ( + awsRegionFlag = Flag{ + Name: "region", + ConfigName: "cloud.aws.region", + Value: "", + Usage: "AWS Region to scan", + } + awsEndpointFlag = Flag{ + Name: "endpoint", + ConfigName: "cloud.aws.endpoint", + Value: "", + Usage: "AWS Endpoint override", + } + awsServiceFlag = Flag{ + Name: "service", + ConfigName: "cloud.aws.service", + Value: []string{}, + Usage: "Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.", + } + awsAccountFlag = Flag{ + Name: "account", + ConfigName: "cloud.aws.account", + Value: "", + Usage: "The AWS account to scan. It's useful to specify this when reviewing cached results for multipel accounts.", + } + awsARNFlag = Flag{ + Name: "arn", + ConfigName: "cloud.aws.arn", + Value: "", + Usage: "The AWS ARN to show results for. Useful to filter results once a scan is cached.", + } +) + +type AWSFlagGroup struct { + Region *Flag + Endpoint *Flag + Services *Flag + Account *Flag + ARN *Flag +} + +type AWSOptions struct { + Region string + Endpoint string + Services []string + Account string + ARN string +} + +func NewAWSFlagGroup() *AWSFlagGroup { + return &AWSFlagGroup{ + Region: &awsRegionFlag, + Endpoint: &awsEndpointFlag, + Services: &awsServiceFlag, + Account: &awsAccountFlag, + ARN: &awsARNFlag, + } +} + +func (f *AWSFlagGroup) Name() string { + return "AWS" +} + +func (f *AWSFlagGroup) Flags() []*Flag { + return []*Flag{f.Region, f.Endpoint, f.Services, f.Account, f.ARN} +} + +func (f *AWSFlagGroup) ToOptions() AWSOptions { + return AWSOptions{ + Region: getString(f.Region), + Endpoint: getString(f.Endpoint), + Services: getStringSlice(f.Services), + Account: getString(f.Account), + ARN: getString(f.ARN), + } +} diff --git a/pkg/flag/cloud_flags.go b/pkg/flag/cloud_flags.go new file mode 100644 index 000000000000..4be13e9a81ec --- /dev/null +++ b/pkg/flag/cloud_flags.go @@ -0,0 +1,50 @@ +package flag + +import "time" + +var ( + cloudUpdateCacheFlag = Flag{ + Name: "update-cache", + ConfigName: "cloud.update-cache", + Value: false, + Usage: "Update the cache for the applicable cloud provider instead of using cached results.", + } + cloudMaxCacheAgeFlag = Flag{ + Name: "max-cache-age", + ConfigName: "cloud.max-cache-age", + Value: time.Hour * 24, + Usage: "The maximum age of the cloud cache. Cached data will be requeried from the cloud provider if it is older than this.", + } +) + +type CloudFlagGroup struct { + UpdateCache *Flag + MaxCacheAge *Flag +} + +type CloudOptions struct { + MaxCacheAge time.Duration + UpdateCache bool +} + +func NewCloudFlagGroup() *CloudFlagGroup { + return &CloudFlagGroup{ + UpdateCache: &cloudUpdateCacheFlag, + MaxCacheAge: &cloudMaxCacheAgeFlag, + } +} + +func (f *CloudFlagGroup) Name() string { + return "Cloud" +} + +func (f *CloudFlagGroup) Flags() []*Flag { + return []*Flag{f.UpdateCache, f.MaxCacheAge} +} + +func (f *CloudFlagGroup) ToOptions() CloudOptions { + return CloudOptions{ + UpdateCache: getBool(f.UpdateCache), + MaxCacheAge: getDuration(f.MaxCacheAge), + } +} diff --git a/pkg/flag/options.go b/pkg/flag/options.go index 9bd842c21859..a05ca131cd15 100644 --- a/pkg/flag/options.go +++ b/pkg/flag/options.go @@ -46,7 +46,9 @@ type FlagGroup interface { } type Flags struct { + AWSFlagGroup *AWSFlagGroup CacheFlagGroup *CacheFlagGroup + CloudFlagGroup *CloudFlagGroup DBFlagGroup *DBFlagGroup ImageFlagGroup *ImageFlagGroup K8sFlagGroup *K8sFlagGroup @@ -64,7 +66,9 @@ type Flags struct { // Options holds all the runtime configuration type Options struct { GlobalOptions + AWSOptions CacheOptions + CloudOptions DBOptions ImageOptions K8sOptions @@ -221,6 +225,12 @@ func (f *Flags) groups() []FlagGroup { if f.LicenseFlagGroup != nil { groups = append(groups, f.LicenseFlagGroup) } + if f.CloudFlagGroup != nil { + groups = append(groups, f.CloudFlagGroup) + } + if f.AWSFlagGroup != nil { + groups = append(groups, f.AWSFlagGroup) + } if f.K8sFlagGroup != nil { groups = append(groups, f.K8sFlagGroup) } @@ -279,6 +289,7 @@ func (f *Flags) Bind(cmd *cobra.Command) error { return nil } +//nolint: gocyclo func (f *Flags) ToOptions(appVersion string, args []string, globalFlags *GlobalFlagGroup, output io.Writer) (Options, error) { var err error opts := Options{ @@ -286,6 +297,14 @@ func (f *Flags) ToOptions(appVersion string, args []string, globalFlags *GlobalF GlobalOptions: globalFlags.ToOptions(), } + if f.AWSFlagGroup != nil { + opts.AWSOptions = f.AWSFlagGroup.ToOptions() + } + + if f.CloudFlagGroup != nil { + opts.CloudOptions = f.CloudFlagGroup.ToOptions() + } + if f.CacheFlagGroup != nil { opts.CacheOptions, err = f.CacheFlagGroup.ToOptions() if err != nil {