From a3eece4fef9294bd1d80e66411a514d3ecc55aa1 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:42:59 -0800 Subject: [PATCH] feat(misconf): Add compliance check support (#3130) Signed-off-by: Simar Co-authored-by: knqyf263 --- docs/docs/cloud/aws/compliance.md | 402 ++++++++++++++++++ docs/docs/cloud/aws/scanning.md | 7 + docs/docs/compliance/compliance.md | 4 +- go.mod | 19 +- go.sum | 38 +- integration/testdata/helm.json.golden | 6 +- .../testdata/helm_testchart.json.golden | 6 +- .../helm_testchart.overridden.json.golden | 6 +- mkdocs.yml | 6 +- pkg/cloud/aws/commands/run.go | 50 ++- pkg/cloud/aws/commands/run_test.go | 63 ++- .../aws/commands/testdata/example-spec.yaml | 13 + pkg/cloud/aws/scanner/scanner.go | 22 +- pkg/cloud/report/convert.go | 3 +- pkg/cloud/report/convert_test.go | 6 +- pkg/cloud/report/report.go | 2 +- pkg/cloud/report/service_test.go | 4 + pkg/commands/app.go | 13 +- pkg/flag/report_flags.go | 2 +- pkg/types/report.go | 6 +- 20 files changed, 605 insertions(+), 73 deletions(-) create mode 100644 docs/docs/cloud/aws/compliance.md create mode 100644 pkg/cloud/aws/commands/testdata/example-spec.yaml diff --git a/docs/docs/cloud/aws/compliance.md b/docs/docs/cloud/aws/compliance.md new file mode 100644 index 0000000000..5a8fc11da8 --- /dev/null +++ b/docs/docs/cloud/aws/compliance.md @@ -0,0 +1,402 @@ +# AWS Compliance + +## CIS Compliance Report + +!!! warning "EXPERIMENTAL" + This feature might change without preserving backwards compatibility. + +The Trivy AWS CLI allows you to scan your AWS account resources and generate the `AWS CIS Foundations Benchmark` report + +[AWS CIS Foundations Benchmark v1.2](https://d0.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf) validates the following control checks: + +```shell ++--------------------------------------------+--------------------------------+ +| NAME | DESCRIPTION | ++--------------------------------------------+--------------------------------+ +| limit-root-account-usage | The "root" account has | +| | unrestricted access to all | +| | resources in the AWS account. | +| | It is highly recommended that | +| | the use of this account be | +| | avoided. | +| no-password-reuse | IAM Password policy should | +| | prevent password reuse. | +| set-max-password-age | IAM Password policy should | +| | have expiry less than or equal | +| | to 90 days. | +| no-root-access-keys | The root user has complete | +| | access to all services and | +| | resources in an AWS account. | +| | AWS Access Keys provide | +| | programmatic access to a given | +| | account. | +| enforce-root-mfa | The "root" account has | +| | unrestricted access to all | +| | resources in the AWS account. | +| | It is highly recommended that | +| | this account have MFA enabled. | +| no-user-attached-policies | IAM policies should not be | +| | granted directly to users. | +| enforce-user-mfa | IAM Users should have MFA | +| | enforcement activated. | +| disable-unused-credentials | Credentials which are | +| | no longer used should be | +| | disabled. | +| rotate-access-keys | Access keys should be rotated | +| | at least every 90 days | +| require-uppercase-in-passwords | IAM Password policy should | +| | have requirement for at least | +| | one uppercase character. | +| require-lowercase-in-passwords | IAM Password policy should | +| | have requirement for at least | +| | one lowercase character. | +| require-symbols-in-passwords | IAM Password policy should | +| | have requirement for at least | +| | one symbol in the password. | +| require-numbers-in-passwords | IAM Password policy should | +| | have requirement for at least | +| | one number in the password. | +| set-minimum-password-length | IAM Password policy should | +| | have minimum password length | +| | of 14 or more characters. | +| no-public-log-access | The S3 Bucket backing | +| | Cloudtrail should be private | +| ensure-cloudwatch-integration | CloudTrail logs should be | +| | stored in S3 and also sent to | +| | CloudWatch Logs | +| enable-all-regions | Cloudtrail should be enabled | +| | in all regions regardless of | +| | where your AWS resources are | +| | generally homed | +| require-bucket-access-logging | You should enable bucket | +| | access logging on the | +| | CloudTrail S3 bucket. | +| require-unauthorised-api-call-alarm | Ensure a log metric filter and | +| | alarm exist for unauthorized | +| | API calls | +| require-sg-change-alarms | Ensure a log metric filter and | +| | alarm exist for security group | +| | changes | +| require-nacl-changes-alarm | Ensure a log metric filter | +| | and alarm exist for changes to | +| | Network Access Control Lists | +| | (NACL) | +| require-network-gateway-changes-alarm | Ensure a log metric filter | +| | and alarm exist for changes to | +| | network gateways | +| require-network-gateway-changes-alarm | Ensure a log metric filter and | +| | alarm exist for route table | +| | changes | +| require-vpc-changes-alarm | Ensure a log metric filter and | +| | alarm exist for VPC changes | +| require-non-mfa-login-alarm | Ensure a log metric filter and | +| | alarm exist for AWS Management | +| | Console sign-in without MFA | +| require-root-user-usage-alarm | Ensure a log metric filter and | +| | alarm exist for usage of root | +| | user | +| require-iam-policy-change-alarm | Ensure a log metric filter | +| | and alarm exist for IAM policy | +| | changes | +| require-cloud-trail-change-alarm | Ensure a log metric filter | +| | and alarm exist for CloudTrail | +| | configuration changes | +| require-console-login-failures-alarm | Ensure a log metric filter and | +| | alarm exist for AWS Management | +| | Console authentication | +| | failures | +| require-cmk-disabled-alarm | Ensure a log metric filter and | +| | alarm exist for disabling or | +| | scheduled deletion of customer | +| | managed keys | +| require-s3-bucket-policy-change-alarm | Ensure a log metric filter | +| | and alarm exist for S3 bucket | +| | policy changes | +| require-config-configuration-changes-alarm | Ensure a log metric filter | +| | and alarm exist for AWS Config | +| | configuration changes | +| no-public-ingress-sgr | An ingress security group rule | +| | allows traffic from /0. | ++--------------------------------------------+--------------------------------+ +``` + + +[AWS CIS Foundations Benchmark v1.4](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls-1.4.0.html) validates the following control checks: +```shell ++--------------------------------------------+--------------------------------+ +| NAME | DESCRIPTION | ++--------------------------------------------+--------------------------------+ +| require-mfa-delete | Buckets should have MFA | +| | deletion protection enabled. | +| disable-unused-credentials-45-days | AWS IAM users can access AWS | +| | resources using different | +| | types of credentials, such | +| | as passwords or access keys. | +| | It is recommended that all | +| | credentials that have been | +| | unused in 45 or greater days | +| | be deactivated or removed. | +| limit-user-access-keys | No user should have more than | +| | one active access key. | +| rotate-access-keys | Access keys should be rotated | +| | at least every 90 days | +| no-user-attached-policies | IAM policies should not be | +| | granted directly to users. | +| no-policy-wildcards | IAM policy should avoid use | +| | of wildcards and instead | +| | apply the principle of least | +| | privilege | +| require-support-role | Missing IAM Role to allow | +| | authorized users to manage | +| | incidents with AWS Support. | +| remove-expired-certificates | Delete expired TLS | +| | certificates | +| enable-access-analyzer | Enable IAM Access analyzer | +| | for IAM policies about all | +| | resources in each region. | +| enforce-user-mfa | IAM Users should have MFA | +| | enforcement activated. | +| no-root-access-keys | The root user has complete | +| | access to all services and | +| | resources in an AWS account. | +| | AWS Access Keys provide | +| | programmatic access to a given | +| | account. | +| enforce-root-mfa | The "root" account has | +| | unrestricted access to all | +| | resources in the AWS account. | +| | It is highly recommended that | +| | this account have MFA enabled. | +| enforce-root-hardware-mfa | The "root" account has | +| | unrestricted access to all | +| | resources in the AWS account. | +| | It is highly recommended that | +| | this account have hardware MFA | +| | enabled. | +| limit-root-account-usage | The "root" account has | +| | unrestricted access to all | +| | resources in the AWS account. | +| | It is highly recommended that | +| | the use of this account be | +| | avoided. | +| set-minimum-password-length | IAM Password policy should | +| | have minimum password length | +| | of 14 or more characters. | +| no-password-reuse | IAM Password policy should | +| | prevent password reuse. | +| enable-object-write-logging | S3 object-level API | +| | operations such as GetObject, | +| | DeleteObject, and PutObject | +| | are called data events. By | +| | default, CloudTrail trails | +| | don't log data events and so | +| | it is recommended to enable | +| | Object-level logging for S3 | +| | buckets. | +| enable-object-read-logging | S3 object-level API | +| | operations such as GetObject, | +| | DeleteObject, and PutObject | +| | are called data events. By | +| | default, CloudTrail trails | +| | don't log data events and so | +| | it is recommended to enable | +| | Object-level logging for S3 | +| | buckets. | +| no-public-log-access | The S3 Bucket backing | +| | Cloudtrail should be private | +| ensure-cloudwatch-integration | CloudTrail logs should be | +| | stored in S3 and also sent to | +| | CloudWatch Logs | +| require-bucket-access-logging | You should enable bucket | +| | access logging on the | +| | CloudTrail S3 bucket. | +| require-sg-change-alarms | Ensure a log metric filter and | +| | alarm exist for security group | +| | changes | +| require-unauthorised-api-call-alarm | Ensure a log metric filter and | +| | alarm exist for unauthorized | +| | API calls | +| require-nacl-changes-alarm | Ensure a log metric filter | +| | and alarm exist for changes to | +| | Network Access Control Lists | +| | (NACL) | +| require-network-gateway-changes-alarm | Ensure a log metric filter | +| | and alarm exist for changes to | +| | network gateways | +| require-network-gateway-changes-alarm | Ensure a log metric filter and | +| | alarm exist for route table | +| | changes | +| require-vpc-changes-alarm | Ensure a log metric filter and | +| | alarm exist for VPC changes | +| require-org-changes-alarm | Ensure a log metric filter and | +| | alarm exist for organisation | +| | changes | +| require-non-mfa-login-alarm | Ensure a log metric filter and | +| | alarm exist for AWS Management | +| | Console sign-in without MFA | +| require-root-user-usage-alarm | Ensure a log metric filter and | +| | alarm exist for usage of root | +| | user | +| require-iam-policy-change-alarm | Ensure a log metric filter | +| | and alarm exist for IAM policy | +| | changes | +| require-cloud-trail-change-alarm | Ensure a log metric filter | +| | and alarm exist for CloudTrail | +| | configuration changes | +| require-console-login-failures-alarm | Ensure a log metric filter and | +| | alarm exist for AWS Management | +| | Console authentication | +| | failures | +| require-cmk-disabled-alarm | Ensure a log metric filter and | +| | alarm exist for disabling or | +| | scheduled deletion of customer | +| | managed keys | +| require-s3-bucket-policy-change-alarm | Ensure a log metric filter | +| | and alarm exist for S3 bucket | +| | policy changes | +| require-config-configuration-changes-alarm | Ensure a log metric filter | +| | and alarm exist for AWS Config | +| | configuration changes | +| restrict-all-in-default-sg | Default security group should | +| | restrict all traffic | ++--------------------------------------------+--------------------------------+ +``` + +[Differences between v1.2 and v1.4](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-cis1.4-vs-cis1.2.html) + +## CLI Commands + +Scan for misconfigurations in an AWS account based on AWS CIS 1.2 benchmark: + +```shell +$ trivy aws --compliance=awscis1.2 + +arn:aws:iam::123456789:user/DummyRoleManager (cloud) + +Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) + +LOW: One or more policies are attached directly to a user +══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ +CIS recommends that you apply IAM policies directly to groups and roles but not users. Assigning privileges at the group or role level reduces the complexity of access management as the number of users grow. Reducing access management complexity might in turn reduce opportunity for a principal to inadvertently receive or retain excessive privileges. + +See https://avd.aquasec.com/misconfig/avd-aws-0143 +────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── + + +``` + + + +You can also summarize the report to get a full compliance report with all the included checks. +```shell +$ trivy aws --compliance=awscis1.2 --report=summary +``` + +```shell +Summary Report for compliance: awscis1.2 +┌──────┬──────────┬────────────────────────────────────────────┬────────┬────────┐ +│ ID │ Severity │ Control Name │ Status │ Issues │ +├──────┼──────────┼────────────────────────────────────────────┼────────┼────────┤ +│ 1.1 │ LOW │ limit-root-account-usage │ PASS │ 0 │ +│ 1.10 │ MEDIUM │ no-password-reuse │ PASS │ 0 │ +│ 1.11 │ MEDIUM │ set-max-password-age │ PASS │ 0 │ +│ 1.12 │ CRITICAL │ no-root-access-keys │ PASS │ 0 │ +│ 1.13 │ CRITICAL │ enforce-root-mfa │ PASS │ 0 │ +│ 1.16 │ LOW │ no-user-attached-policies │ FAIL │ 5 │ +│ 1.2 │ MEDIUM │ enforce-user-mfa │ PASS │ 0 │ +│ 1.3 │ MEDIUM │ disable-unused-credentials │ FAIL │ 2 │ +│ 1.4 │ LOW │ rotate-access-keys │ FAIL │ 7 │ +│ 1.5 │ MEDIUM │ require-uppercase-in-passwords │ PASS │ 0 │ +│ 1.6 │ MEDIUM │ require-lowercase-in-passwords │ PASS │ 0 │ +│ 1.7 │ MEDIUM │ require-symbols-in-passwords │ PASS │ 0 │ +│ 1.8 │ MEDIUM │ require-numbers-in-passwords │ PASS │ 0 │ +│ 1.9 │ MEDIUM │ set-minimum-password-length │ FAIL │ 1 │ +│ 2.3 │ CRITICAL │ no-public-log-access │ PASS │ 0 │ +│ 2.4 │ LOW │ ensure-cloudwatch-integration │ PASS │ 0 │ +│ 2.5 │ MEDIUM │ enable-all-regions │ PASS │ 0 │ +│ 2.6 │ LOW │ require-bucket-access-logging │ PASS │ 0 │ +│ 3.1 │ LOW │ require-unauthorised-api-call-alarm │ PASS │ 0 │ +│ 3.10 │ LOW │ require-sg-change-alarms │ PASS │ 0 │ +│ 3.11 │ LOW │ require-nacl-changes-alarm │ PASS │ 0 │ +│ 3.12 │ LOW │ require-network-gateway-changes-alarm │ PASS │ 0 │ +│ 3.13 │ LOW │ require-network-gateway-changes-alarm │ PASS │ 0 │ +│ 3.14 │ LOW │ require-vpc-changes-alarm │ PASS │ 0 │ +│ 3.2 │ LOW │ require-non-mfa-login-alarm │ PASS │ 0 │ +│ 3.3 │ LOW │ require-root-user-usage-alarm │ PASS │ 0 │ +│ 3.4 │ LOW │ require-iam-policy-change-alarm │ PASS │ 0 │ +│ 3.5 │ LOW │ require-cloud-trail-change-alarm │ PASS │ 0 │ +│ 3.6 │ LOW │ require-console-login-failures-alarm │ PASS │ 0 │ +│ 3.7 │ LOW │ require-cmk-disabled-alarm │ PASS │ 0 │ +│ 3.8 │ LOW │ require-s3-bucket-policy-change-alarm │ PASS │ 0 │ +│ 3.9 │ LOW │ require-config-configuration-changes-alarm │ PASS │ 0 │ +│ 4.1 │ CRITICAL │ no-public-ingress-sgr │ PASS │ 0 │ +└──────┴──────────┴────────────────────────────────────────────┴────────┴────────┘ +``` + + +Furthermore, you can also get the report in a JSON format. +```shell +$ trivy aws --compliance=awscis1.2 --report=summary --format=json +``` + +```json +{ + "ID": "0001", + "Title": "awscis1.2", + "SummaryControls": [{ + "ID": "1.1", + "Name": "limit-root-account-usage", + "Severity": "LOW", + "TotalFail": 5 + }, + { + "ID": "1.10", + "Name": "no-password-reuse", + "Severity": "MEDIUM", + "TotalFail": 1 + } + ] +} +``` + + +## Custom compliance report + +The Trivy AWS CLI allows you to create a custom compliance specification and pass it to trivy for generating scan report. + +The report is generated based on scanning result mapping between users define controls and trivy checks ID. +The supported checks are from two types and can be found at [Aqua vulnerability DB](https://avd.aquasec.com/): +- [misconfiguration](https://avd.aquasec.com/misconfig/) + +### Compliance spec format +The compliance spec file format should be as follows: + + +```yaml +--- +spec: + id: "0001" + title: awscis1.2 + description: AWS CIS Foundations + version: "1.2" + relatedResources: + - https://www.cisecurity.org/benchmark/amazon_web_services + controls: + - id: "1.1" + name: limit-root-account-usage + description: |- + The "root" account has unrestricted access to all resources in the AWS account. It is highly + recommended that the use of this account be avoided. + checks: + - id: AVD-AWS-0140 + severity: LOW +``` + +## Custom report CLI Commands + +To use a custom spec, the file path should be passed to the `--compliance` flag with `@` prefix as follows: + +``` +$ trivy aws --compliance=@/spec/my_compliance.yaml +``` + diff --git a/docs/docs/cloud/aws/scanning.md b/docs/docs/cloud/aws/scanning.md index 9214e1e7ec..ae48eb19c6 100644 --- a/docs/docs/cloud/aws/scanning.md +++ b/docs/docs/cloud/aws/scanning.md @@ -50,6 +50,13 @@ 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. +## Compliance Spec +Trivy can also run specific checks by spec by specifying the compliance flag: +```shell +trivy aws --compliance=awscis1.2 +``` +Will only target the checks defined under the AWS CIS 1.2 spec. Currently, we support AWS CIS 1.2 and 1.4 specs. More details [here](compliance.md). + ## Cached Results By default, Trivy will cache a representation of each AWS service for 24 hours. This means you can filter and view results for a service without having to wait for the entire 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`.). Regardless of whether the cache is used or not, rules will be evaluated again with each run of `trivy aws`. diff --git a/docs/docs/compliance/compliance.md b/docs/docs/compliance/compliance.md index e70ba19695..0af42a81bd 100644 --- a/docs/docs/compliance/compliance.md +++ b/docs/docs/compliance/compliance.md @@ -1,8 +1,8 @@ # Compliance Reports -Trivy support producing compliance reports. +Trivy supports producing compliance reports. ## Supported reports - [NSA, CISA Kubernetes Hardening Guidance v1.0](../kubernetes/cli/compliance.md) - +- [AWS CIS v1.2 and v1.4](../cloud/aws/compliance.md) \ No newline at end of file diff --git a/go.mod b/go.mod index 85b2461320..29184a5b6c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/alicebob/miniredis/v2 v2.23.0 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 - github.com/aquasecurity/defsec v0.82.0 + github.com/aquasecurity/defsec v0.82.6 github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 @@ -103,14 +103,14 @@ require ( github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.1 // indirect github.com/aws/aws-sdk-go-v2/service/ebs v1.15.19 // indirect github.com/aws/aws-sdk-go-v2/service/ecr v1.17.18 // indirect - github.com/aws/aws-sdk-go-v2/service/ecs v1.18.23 // indirect + github.com/aws/aws-sdk-go-v2/service/ecs v1.18.26 // indirect github.com/aws/aws-sdk-go-v2/service/efs v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/eks v1.22.1 // indirect github.com/aws/aws-sdk-go-v2/service/elasticache v1.22.10 // indirect github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.16.10 // indirect github.com/aws/aws-sdk-go-v2/service/emr v1.20.11 // indirect - github.com/aws/aws-sdk-go-v2/service/iam v1.18.19 // indirect + github.com/aws/aws-sdk-go-v2/service/iam v1.18.23 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.17 // indirect @@ -118,9 +118,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect github.com/aws/aws-sdk-go-v2/service/kafka v1.17.19 // indirect github.com/aws/aws-sdk-go-v2/service/kinesis v1.15.19 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.18.11 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.18.15 // indirect github.com/aws/aws-sdk-go-v2/service/lambda v1.24.6 // indirect - github.com/aws/aws-sdk-go-v2/service/mq v1.13.13 // indirect + github.com/aws/aws-sdk-go-v2/service/mq v1.13.15 // indirect github.com/aws/aws-sdk-go-v2/service/neptune v1.17.12 // indirect github.com/aws/aws-sdk-go-v2/service/rds v1.26.1 // indirect github.com/aws/aws-sdk-go-v2/service/redshift v1.26.10 // indirect @@ -152,6 +152,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + go.etcd.io/etcd/api/v3 v3.5.4 // indirect go.mongodb.org/mongo-driver v1.10.0 // indirect ) @@ -170,7 +171,7 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -355,11 +356,11 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools v2.2.0+incompatible - helm.sh/helm/v3 v3.10.0 // indirect + helm.sh/helm/v3 v3.10.1 // indirect k8s.io/api v0.25.3 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect + k8s.io/apiextensions-apiserver v0.25.2 // indirect k8s.io/apimachinery v0.25.3 // indirect - k8s.io/apiserver v0.25.0 // indirect + k8s.io/apiserver v0.25.2 // indirect k8s.io/cli-runtime v0.25.3 // indirect k8s.io/client-go v0.25.3 // indirect k8s.io/component-base v0.25.3 // indirect diff --git a/go.sum b/go.sum index 2c57548b63..489313b16b 100644 --- a/go.sum +++ b/go.sum @@ -101,8 +101,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CycloneDX/cyclonedx-go v0.6.0 h1:SizWGbZzFTC/O/1yh072XQBMxfvsoWqd//oKCIyzFyE= github.com/CycloneDX/cyclonedx-go v0.6.0/go.mod h1:nQCiF4Tvrg5Ieu8qPhYMvzPGMu5I7fANZkrSsJjl5mg= @@ -190,8 +190,8 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM= github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8= -github.com/aquasecurity/defsec v0.82.0 h1:WUpPZ6IR0NgZqDK7CGaY5fOI799FNlqGvGMSboHEHlI= -github.com/aquasecurity/defsec v0.82.0/go.mod h1:4SMepRtX/F8bzTd8CucIpMiqpNOB6/BVPnRktDF5iN0= +github.com/aquasecurity/defsec v0.82.6 h1:whb9ygS+cANcvGSq51s44+hY3nU6OV3VOR2Q4dIz3kc= +github.com/aquasecurity/defsec v0.82.6/go.mod h1:sUdW6pzASralDcs+CDOE+QpWfBJt3/PY1Qbg8CS5flg= github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba h1:YJTAuz/SimQCplNoqSYuzH3XZYmgmdfgoGdOkjCDceE= github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba/go.mod h1:ZCiGJgdQxCateSw3nPMwZvp9J/+nU8/3DcGY/NO71e4= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM= @@ -283,8 +283,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1 h1:jSS5gynKz4XaGcs6m25idCTN+tvP github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1/go.mod h1:0+6fPoY0SglgzQUs2yml7X/fup12cMlVumJufh5npRQ= github.com/aws/aws-sdk-go-v2/service/ecr v1.17.18 h1:uiF/RI+Up8H2xdgT2GWa20YzxiKEalHieqNjm6HC3Xk= github.com/aws/aws-sdk-go-v2/service/ecr v1.17.18/go.mod h1:DQtDYmexqR+z+B6HBCvY7zK/tuXKv6Zy/IwOXOK3eow= -github.com/aws/aws-sdk-go-v2/service/ecs v1.18.23 h1:gdqm7KhgE1COL5CM2eUYD6XFtqOJC1bhOa5vwLGPP0g= -github.com/aws/aws-sdk-go-v2/service/ecs v1.18.23/go.mod h1:6bV2xEub6Vch19ZZASMbrNMNIpBPTwy64r9WIQ+wsSE= +github.com/aws/aws-sdk-go-v2/service/ecs v1.18.26 h1:EHJAYkUnlFJ/KwuFMvUs/bPbb0DaqAI+gTfXxffTPZ0= +github.com/aws/aws-sdk-go-v2/service/ecs v1.18.26/go.mod h1:NpR78BP2STxvF/R1GXLDM4gAEfjz68W/h0nC5b6Jk3s= github.com/aws/aws-sdk-go-v2/service/efs v1.17.15 h1:HgRxrC7KIUJW6gFf4AEjFPvrgh73aADaa1XZpiFsfgA= github.com/aws/aws-sdk-go-v2/service/efs v1.17.15/go.mod h1:xwXDmrVGNncQhSXhMbm7pE14Vcyc3QbzblKsvNms0/E= github.com/aws/aws-sdk-go-v2/service/eks v1.22.1 h1:f07Bk+xMm0Q8PCzvrBg8Bd6m67CTvZSxQWB0H7ZEJOU= @@ -297,8 +297,8 @@ github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.16.10 h1:R8edMXNKBa github.com/aws/aws-sdk-go-v2/service/elasticsearchservice v1.16.10/go.mod h1:VPuMdyWzqCRgv5qTww9yeauwGsOxVbtP2OyqXVyZB8g= github.com/aws/aws-sdk-go-v2/service/emr v1.20.11 h1:YpP+XtFfsJQoehZgCsbeaROtKFbAY1bWKId/KJu4JmU= github.com/aws/aws-sdk-go-v2/service/emr v1.20.11/go.mod h1:0/0//Fz5074ATb+b/Vdhs61Vqhxw5qAHu405lRLjZ4w= -github.com/aws/aws-sdk-go-v2/service/iam v1.18.19 h1:0DiDgcHWW0HtKlmqUEafLtOVOTFI2FT2M7/uQfcLskk= -github.com/aws/aws-sdk-go-v2/service/iam v1.18.19/go.mod h1:pDBRPE4AibneAh4P6fZuU3eUkAgYirM88o2M2MxIXlg= +github.com/aws/aws-sdk-go-v2/service/iam v1.18.23 h1:HOtW30EkfQevdv++mKguMyn8/agh1z2VuBGR4Hou/u8= +github.com/aws/aws-sdk-go-v2/service/iam v1.18.23/go.mod h1:yQ92mKfw/Gg5AvgxGmfdufKEyVoa9RNBsdnB9j5Gzkk= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 h1:Lh1AShsuIJTwMkoxVCAYPJgNG5H+eN6SmoUn8nOZ5wE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 h1:BBYoNQt2kUZUUK4bIPsKrCcjVPUMNsgQpNAwhznK/zo= @@ -314,12 +314,12 @@ github.com/aws/aws-sdk-go-v2/service/kafka v1.17.19 h1:H3U7TVZCYODgkXVYDGcNZWTjN github.com/aws/aws-sdk-go-v2/service/kafka v1.17.19/go.mod h1:wiajSLYucUJ6xcvo4gGGl+xBRgrXt75vXwa2Xr73eRA= github.com/aws/aws-sdk-go-v2/service/kinesis v1.15.19 h1:qVaBkJxFxm6o/9DPNnJU6L9O3V7ycEKhCvRm2BFBQTU= github.com/aws/aws-sdk-go-v2/service/kinesis v1.15.19/go.mod h1:9rLNg+J9SEe7rhge/YzKU3QTovlLqOmqH8akb0IB1ko= -github.com/aws/aws-sdk-go-v2/service/kms v1.18.11 h1:IxfVvdMedvCHXOWIuypaCjmNqGOP1uaXnaSVQzut7KE= -github.com/aws/aws-sdk-go-v2/service/kms v1.18.11/go.mod h1:DZtboupHLNr0p6qHw9r3kR8MUnN/rc4AAVmNpe2ocuU= +github.com/aws/aws-sdk-go-v2/service/kms v1.18.15 h1:hWPFd4GjCZLTb9Nvw+GuzZ4qTnvWoaqcLcrgofQGkhw= +github.com/aws/aws-sdk-go-v2/service/kms v1.18.15/go.mod h1:kZodDPTQjSH/qM6/OvyTfM5mms5JHB/EKYp5dhn/vI4= github.com/aws/aws-sdk-go-v2/service/lambda v1.24.6 h1:N7RkXX2SJbN+TCp295J3LdMR0KRFd2Bhi5nIO+svLQY= github.com/aws/aws-sdk-go-v2/service/lambda v1.24.6/go.mod h1:oTJIIluTaJCRT6xP1AZpuU3JwRHBC0Q5O4Hg+SUxFHw= -github.com/aws/aws-sdk-go-v2/service/mq v1.13.13 h1:EwRZG7zO6f81vd3xfe3dgSQcGHu3LjsFXezx6sfmWpA= -github.com/aws/aws-sdk-go-v2/service/mq v1.13.13/go.mod h1:8+R6AD/LfQTdn4fhUdb5/z7LonKkJI2fsIJZz+bJs3s= +github.com/aws/aws-sdk-go-v2/service/mq v1.13.15 h1:K0UbxNfaPHoIXo0T4L4jrbE+TKcVPYp55nEnMLxVRPk= +github.com/aws/aws-sdk-go-v2/service/mq v1.13.15/go.mod h1:ycghPMXYItx5Y74iehFgGwDNUMXdq0xCxLaYC5uYZO0= github.com/aws/aws-sdk-go-v2/service/neptune v1.17.12 h1:QxMwblYXBaAUnQsSbGGmGlqj5/lHJKaEr1HcMXnnaok= github.com/aws/aws-sdk-go-v2/service/neptune v1.17.12/go.mod h1:0arQRjGdCQgRNLiCIv5FEFCgQkDMUiLkv0mkrUbSrNE= github.com/aws/aws-sdk-go-v2/service/rds v1.26.1 h1:tiXsw36GaRUWMcH5uRM2uM7vo+bNsa1mEOn68ZOBjWA= @@ -1581,6 +1581,8 @@ go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= @@ -2289,8 +2291,8 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -helm.sh/helm/v3 v3.10.0 h1:y/MYONZ/bsld9kHwqgBX2uPggnUr5hahpjwt9/jrHlI= -helm.sh/helm/v3 v3.10.0/go.mod h1:paPw0hO5KVfrCMbi1M8+P8xdfBri3IiJiVKATZsFR94= +helm.sh/helm/v3 v3.10.1 h1:uTnNlYx8QcTSNA4ZJ50Llwife4CSohUY4ehumyVf2QE= +helm.sh/helm/v3 v3.10.1/go.mod h1:CXOcs02AYvrlPMWARNYNRgf2rNP7gLJQsi/Ubd4EDrI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2303,8 +2305,8 @@ k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= +k8s.io/apiextensions-apiserver v0.25.2 h1:8uOQX17RE7XL02ngtnh3TgifY7EhekpK+/piwzQNnBo= +k8s.io/apiextensions-apiserver v0.25.2/go.mod h1:iRwwRDlWPfaHhuBfQ0WMa5skdQfrE18QXJaJvIDLvE8= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= @@ -2313,8 +2315,8 @@ k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqw k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4= -k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo= +k8s.io/apiserver v0.25.2 h1:YePimobk187IMIdnmsMxsfIbC5p4eX3WSOrS9x6FEYw= +k8s.io/apiserver v0.25.2/go.mod h1:30r7xyQTREWCkG2uSjgjhQcKVvAAlqoD+YyrqR6Cn+I= k8s.io/cli-runtime v0.25.3 h1:Zs7P7l7db/5J+KDePOVtDlArAa9pZXaDinGWGZl0aM8= k8s.io/cli-runtime v0.25.3/go.mod h1:InHHsjkyW5hQsILJGpGjeruiDZT/R0OkROQgD6GzxO4= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= diff --git a/integration/testdata/helm.json.golden b/integration/testdata/helm.json.golden index 20e90147a0..beae3cf68e 100644 --- a/integration/testdata/helm.json.golden +++ b/integration/testdata/helm.json.golden @@ -20,7 +20,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 77, + "Successes": 80, "Failures": 2, "Exceptions": 0 }, @@ -270,7 +270,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } @@ -280,7 +280,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } diff --git a/integration/testdata/helm_testchart.json.golden b/integration/testdata/helm_testchart.json.golden index 81d29b9562..76305990e2 100644 --- a/integration/testdata/helm_testchart.json.golden +++ b/integration/testdata/helm_testchart.json.golden @@ -20,7 +20,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 77, + "Successes": 80, "Failures": 2, "Exceptions": 0 }, @@ -270,7 +270,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } @@ -280,7 +280,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } diff --git a/integration/testdata/helm_testchart.overridden.json.golden b/integration/testdata/helm_testchart.overridden.json.golden index 98d6fbad76..c01396ead4 100644 --- a/integration/testdata/helm_testchart.overridden.json.golden +++ b/integration/testdata/helm_testchart.overridden.json.golden @@ -20,7 +20,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 75, + "Successes": 78, "Failures": 4, "Exceptions": 0 }, @@ -481,7 +481,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } @@ -491,7 +491,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 79, + "Successes": 82, "Failures": 0, "Exceptions": 0 } diff --git a/mkdocs.yml b/mkdocs.yml index 4c59f80006..20e6da52d9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -90,12 +90,12 @@ nav: - Operator: - Overview: docs/kubernetes/operator/index.md - Cloud: - - AWS: docs/cloud/aws/scanning.md + - AWS: + - Scanning: docs/cloud/aws/scanning.md + - Compliance: docs/cloud/aws/compliance.md - Virtual Machine Image: - Overview: docs/vm/index.md - AWS EC2: docs/vm/aws.md - - Compliance: - - Reports: docs/compliance/compliance.md - SBOM: - Overview: docs/sbom/index.md - CycloneDX: docs/sbom/cyclonedx.md diff --git a/pkg/cloud/aws/commands/run.go b/pkg/cloud/aws/commands/run.go index 19c323cfac..5d64320ae8 100644 --- a/pkg/cloud/aws/commands/run.go +++ b/pkg/cloud/aws/commands/run.go @@ -6,23 +6,22 @@ import ( "fmt" "strings" - "github.com/aquasecurity/defsec/pkg/errs" - - cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" - - "github.com/aquasecurity/trivy/pkg/cloud" - - "github.com/aquasecurity/trivy/pkg/flag" - "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/sts" + "golang.org/x/xerrors" + "gopkg.in/yaml.v3" + "github.com/aquasecurity/defsec/pkg/errs" + awsScanner "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws" + "github.com/aquasecurity/trivy/pkg/cloud" "github.com/aquasecurity/trivy/pkg/cloud/aws/scanner" "github.com/aquasecurity/trivy/pkg/cloud/report" - + cmd "github.com/aquasecurity/trivy/pkg/commands/artifact" + cr "github.com/aquasecurity/trivy/pkg/compliance/report" + "github.com/aquasecurity/trivy/pkg/compliance/spec" + "github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/log" - - awsScanner "github.com/aquasecurity/defsec/pkg/scanners/cloud/aws" + "github.com/aquasecurity/trivy/pkg/types" ) func getAccountIDAndRegion(ctx context.Context, region string) (string, string, error) { @@ -124,9 +123,36 @@ func Run(ctx context.Context, opt flag.Options) error { return fmt.Errorf("aws scan error: %w", err) } } - r := report.New(cloud.ProviderAWS, opt.Account, opt.Region, results.GetFailed(), opt.Services) log.Logger.Debug("Writing report to output...") + if len(opt.Compliance) > 0 { + var complianceSpec spec.ComplianceSpec + cs, err := spec.GetComplianceSpec(opt.Compliance) + if err != nil { + return xerrors.Errorf("spec loading from file system error: %w", err) + } + if err = yaml.Unmarshal(cs, &complianceSpec); err != nil { + return xerrors.Errorf("yaml unmarshal error: %w", err) + } + + convertedResults := report.ConvertResults(results, cloud.ProviderAWS, opt.Services) + var crr []types.Results + for _, r := range convertedResults { + crr = append(crr, r.Results) + } + + complianceReport, err := cr.BuildComplianceReport(crr, complianceSpec) + if err != nil { + return xerrors.Errorf("compliance report build error: %w", err) + } + + return cr.Write(complianceReport, cr.Option{ + Format: opt.Format, + Report: opt.ReportFormat, + Output: opt.Output}) + } + + r := report.New(cloud.ProviderAWS, opt.Account, opt.Region, results.GetFailed(), opt.Services) if err := report.Write(r, opt, cached); err != nil { return fmt.Errorf("unable to write results: %w", err) } diff --git a/pkg/cloud/aws/commands/run_test.go b/pkg/cloud/aws/commands/run_test.go index b927f8daaf..6690ad196c 100644 --- a/pkg/cloud/aws/commands/run_test.go +++ b/pkg/cloud/aws/commands/run_test.go @@ -100,6 +100,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0086", + "AVDID": "AVD-AWS-0086", "Title": "S3 Access block should block public ACL", "Description": "S3 buckets should block public ACLs on buckets and any objects they contain. By blocking, PUTs with fail if the object has any public ACL a.", "Message": "No public access block so not blocking public acls", @@ -123,6 +124,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0087", + "AVDID": "AVD-AWS-0087", "Title": "S3 Access block should block public policy", "Description": "S3 bucket policy should have block public policy to prevent users from putting a policy that enable public access.", "Message": "No public access block so not blocking public policies", @@ -146,6 +148,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0088", + "AVDID": "AVD-AWS-0088", "Title": "Unencrypted S3 bucket.", "Description": "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.", "Message": "Bucket does not have encryption enabled", @@ -169,6 +172,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0089", + "AVDID": "AVD-AWS-0089", "Title": "S3 Bucket does not have logging enabled.", "Description": "Buckets should have logging enabled so that access can be audited.", "Message": "Bucket does not have logging enabled", @@ -192,6 +196,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0090", + "AVDID": "AVD-AWS-0090", "Title": "S3 Data should be versioned", "Description": "Versioning in Amazon S3 is a means of keeping multiple variants of an object in the same bucket. \nYou can use the S3 Versioning feature to preserve, retrieve, and restore every version of every object stored in your buckets. \nWith versioning you can recover more easily from both unintended user actions and application failures.", "Message": "Bucket does not have versioning enabled", @@ -215,6 +220,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0132", + "AVDID": "AVD-AWS-0132", "Title": "S3 encryption should use Customer Managed Keys", "Description": "Encryption using AWS keys provides protection for your S3 buckets. To increase control of the encryption and manage factors like rotation use customer managed keys.", "Message": "Bucket does not encrypt data with a customer managed key.", @@ -238,6 +244,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0091", + "AVDID": "AVD-AWS-0091", "Title": "S3 Access Block should Ignore Public Acl", "Description": "S3 buckets should ignore public ACLs on buckets and any objects they contain. By ignoring rather than blocking, PUT calls with public ACLs will still be applied but the ACL will be ignored.", "Message": "No public access block so not ignoring public acls", @@ -261,6 +268,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0093", + "AVDID": "AVD-AWS-0093", "Title": "S3 Access block should restrict public bucket to limit access", "Description": "S3 buckets should restrict public policies for the bucket. By enabling, the restrict_public_buckets, only the bucket owner and AWS Services can access if it has a public policy.", "Message": "No public access block so not restricting public buckets", @@ -284,6 +292,7 @@ func Test_Run(t *testing.T) { { "Type": "AWS", "ID": "AVD-AWS-0094", + "AVDID": "AVD-AWS-0094", "Title": "S3 buckets should each define an aws_s3_bucket_public_access_block", "Description": "The \"block public access\" settings in S3 override individual policies that apply to a given bucket, meaning that all public access can be controlled in one central types for that bucket. It is therefore good practice to define these settings for each bucket in order to clearly define the public access that can be allowed for it.", "Message": "Bucket does not have a corresponding public access block.", @@ -381,6 +390,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0086", + "AVDID": "AVD-AWS-0086", "Title": "S3 Access block should block public ACL", "Description": "S3 buckets should block public ACLs on buckets and any objects they contain. By blocking, PUTs with fail if the object has any public ACL a.", "Message": "No public access block so not blocking public acls", @@ -404,6 +414,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0087", + "AVDID": "AVD-AWS-0087", "Title": "S3 Access block should block public policy", "Description": "S3 bucket policy should have block public policy to prevent users from putting a policy that enable public access.", "Message": "No public access block so not blocking public policies", @@ -427,6 +438,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0088", + "AVDID": "AVD-AWS-0088", "Title": "Unencrypted S3 bucket.", "Description": "S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised.", "Message": "Bucket does not have encryption enabled", @@ -450,6 +462,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0089", + "AVDID": "AVD-AWS-0089", "Title": "S3 Bucket does not have logging enabled.", "Description": "Buckets should have logging enabled so that access can be audited.", "Message": "Bucket does not have logging enabled", @@ -473,6 +486,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0090", + "AVDID": "AVD-AWS-0090", "Title": "S3 Data should be versioned", "Description": "Versioning in Amazon S3 is a means of keeping multiple variants of an object in the same bucket. \nYou can use the S3 Versioning feature to preserve, retrieve, and restore every version of every object stored in your buckets. \nWith versioning you can recover more easily from both unintended user actions and application failures.", "Message": "Bucket does not have versioning enabled", @@ -496,6 +510,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0132", + "AVDID": "AVD-AWS-0132", "Title": "S3 encryption should use Customer Managed Keys", "Description": "Encryption using AWS keys provides protection for your S3 buckets. To increase control of the encryption and manage factors like rotation use customer managed keys.", "Message": "Bucket does not encrypt data with a customer managed key.", @@ -519,6 +534,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0091", + "AVDID": "AVD-AWS-0091", "Title": "S3 Access Block should Ignore Public Acl", "Description": "S3 buckets should ignore public ACLs on buckets and any objects they contain. By ignoring rather than blocking, PUT calls with public ACLs will still be applied but the ACL will be ignored.", "Message": "No public access block so not ignoring public acls", @@ -542,6 +558,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0093", + "AVDID": "AVD-AWS-0093", "Title": "S3 Access block should restrict public bucket to limit access", "Description": "S3 buckets should restrict public policies for the bucket. By enabling, the restrict_public_buckets, only the bucket owner and AWS Services can access if it has a public policy.", "Message": "No public access block so not restricting public buckets", @@ -565,6 +582,7 @@ deny[res] { { "Type": "AWS", "ID": "AVD-AWS-0094", + "AVDID": "AVD-AWS-0094", "Title": "S3 buckets should each define an aws_s3_bucket_public_access_block", "Description": "The \"block public access\" settings in S3 override individual policies that apply to a given bucket, meaning that all public access can be controlled in one central types for that bucket. It is therefore good practice to define these settings for each bucket in order to clearly define the public access that can be allowed for it.", "Message": "Bucket does not have a corresponding public access block.", @@ -613,6 +631,47 @@ deny[res] { } `, }, + { + name: "compliance report summary", + options: flag.Options{ + AWSOptions: flag.AWSOptions{ + Region: "us-east-1", + Services: []string{"s3"}, + Account: "12345678", + }, + CloudOptions: flag.CloudOptions{ + MaxCacheAge: time.Hour * 24 * 365 * 100, + }, + ReportOptions: flag.ReportOptions{Compliance: "@./testdata/example-spec.yaml", Format: "table", ReportFormat: "summary"}, + }, + cacheContent: exampleS3Cache, + want: ` +Summary Report for compliance: my-custom-spec +┌─────┬──────────┬───────────────────────┬────────┬────────┐ +│ ID │ Severity │ Control Name │ Status │ Issues │ +├─────┼──────────┼───────────────────────┼────────┼────────┤ +│ 1.1 │ HIGH │ Unencrypted S3 bucket │ FAIL │ 1 │ +└─────┴──────────┴───────────────────────┴────────┴────────┘ + + +`, + }, + { + name: "error loading compliance report", + expectErr: true, + options: flag.Options{ + AWSOptions: flag.AWSOptions{ + Region: "us-east-1", + Services: []string{"s3"}, + Account: "12345678", + }, + CloudOptions: flag.CloudOptions{ + MaxCacheAge: time.Hour * 24 * 365 * 100, + }, + ReportOptions: flag.ReportOptions{Compliance: "@./testdata/nosuchspec.yaml", Format: "table", ReportFormat: "summary"}, + }, + cacheContent: exampleS3Cache, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -620,7 +679,9 @@ deny[res] { test.options.Output = buffer test.options.Debug = true test.options.GlobalOptions.Timeout = time.Minute - test.options.Format = "json" + if test.options.Format == "" { + test.options.Format = "json" + } test.options.Severities = []dbTypes.Severity{ dbTypes.SeverityUnknown, dbTypes.SeverityLow, diff --git a/pkg/cloud/aws/commands/testdata/example-spec.yaml b/pkg/cloud/aws/commands/testdata/example-spec.yaml new file mode 100644 index 0000000000..19fbf0a3bf --- /dev/null +++ b/pkg/cloud/aws/commands/testdata/example-spec.yaml @@ -0,0 +1,13 @@ +spec: + id: "0001" + title: my-custom-spec + description: My fancy spec + version: "1.2" + controls: + - id: "1.1" + name: Unencrypted S3 bucket + description: |- + S3 Buckets should be encrypted to protect the data that is stored within them if access is compromised. + checks: + - id: AVD-AWS-0088 + severity: HIGH \ No newline at end of file diff --git a/pkg/cloud/aws/scanner/scanner.go b/pkg/cloud/aws/scanner/scanner.go index fec65170f1..3397c4a369 100644 --- a/pkg/cloud/aws/scanner/scanner.go +++ b/pkg/cloud/aws/scanner/scanner.go @@ -5,17 +5,14 @@ import ( "fmt" "strings" - "github.com/aquasecurity/defsec/pkg/state" - "github.com/aquasecurity/defsec/pkg/framework" - - "github.com/aquasecurity/trivy/pkg/cloud/aws/cache" - "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" + "github.com/aquasecurity/defsec/pkg/state" + "github.com/aquasecurity/trivy/pkg/cloud/aws/cache" + "github.com/aquasecurity/trivy/pkg/flag" + "github.com/aquasecurity/trivy/pkg/log" ) type AWSScanner struct { @@ -77,10 +74,13 @@ func (s *AWSScanner) Scan(ctx context.Context, option flag.Options) (scan.Result ) } - scannerOpts = append(scannerOpts, options.ScannerWithFrameworks( - framework.Default, - framework.CIS_AWS_1_2, - )) + if len(option.Compliance) > 0 { + scannerOpts = append(scannerOpts, options.ScannerWithSpec(option.Compliance)) + } else { + scannerOpts = append(scannerOpts, options.ScannerWithFrameworks( + framework.Default, + framework.CIS_AWS_1_2)) + } scanner := aws.New(scannerOpts...) diff --git a/pkg/cloud/report/convert.go b/pkg/cloud/report/convert.go index 0c4c531ad0..d1e41bcb2f 100644 --- a/pkg/cloud/report/convert.go +++ b/pkg/cloud/report/convert.go @@ -12,7 +12,7 @@ import ( "github.com/aquasecurity/trivy/pkg/types" ) -func convertResults(results scan.Results, provider string, scoped []string) map[string]ResultsAtTime { +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 { @@ -74,6 +74,7 @@ func convertResults(results scan.Results, provider string, scoped []string) map[ arnResult.Misconfigurations = append(arnResult.Misconfigurations, types.DetectedMisconfiguration{ Type: provider, ID: result.Rule().AVDID, + AVDID: result.Rule().AVDID, Title: result.Rule().Summary, Description: strings.TrimSpace(result.Rule().Explanation), Message: strings.TrimSpace(result.Description()), diff --git a/pkg/cloud/report/convert_test.go b/pkg/cloud/report/convert_test.go index 6700ec6ac0..6c257f19c4 100644 --- a/pkg/cloud/report/convert_test.go +++ b/pkg/cloud/report/convert_test.go @@ -117,6 +117,7 @@ func Test_ResultConversion(t *testing.T) { { Type: "AWS", ID: "AVD-AWS-9999", + AVDID: "AVD-AWS-9999", Title: "Do not use bad stuff", Description: "Bad stuff is... bad", Message: "something failed", @@ -146,6 +147,7 @@ func Test_ResultConversion(t *testing.T) { { Type: "AWS", ID: "AVD-AWS-9999", + AVDID: "AVD-AWS-9999", Title: "Do not use bad stuff", Description: "Bad stuff is... bad", Message: "something else failed", @@ -165,6 +167,7 @@ func Test_ResultConversion(t *testing.T) { { Type: "AWS", ID: "AVD-AWS-9999", + AVDID: "AVD-AWS-9999", Title: "Do not use bad stuff", Description: "Bad stuff is... bad", Message: "something else failed again", @@ -195,6 +198,7 @@ func Test_ResultConversion(t *testing.T) { { Type: "AWS", ID: "AVD-AWS-9999", + AVDID: "AVD-AWS-9999", Title: "Do not use bad stuff", Description: "Bad stuff is... bad", Message: "instance is bad", @@ -221,7 +225,7 @@ func Test_ResultConversion(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - converted := convertResults(test.results, test.provider, test.scoped) + converted := ConvertResults(test.results, test.provider, test.scoped) assertConvertedResultsMatch(t, test.expected, converted) }) } diff --git a/pkg/cloud/report/report.go b/pkg/cloud/report/report.go index a7fbd3f082..ae0d3d73b1 100644 --- a/pkg/cloud/report/report.go +++ b/pkg/cloud/report/report.go @@ -43,7 +43,7 @@ func New(provider, accountID, region string, defsecResults scan.Results, scopedS return &Report{ Provider: provider, AccountID: accountID, - Results: convertResults(defsecResults, provider, scopedServices), + Results: ConvertResults(defsecResults, provider, scopedServices), ServicesInScope: scopedServices, Region: region, } diff --git a/pkg/cloud/report/service_test.go b/pkg/cloud/report/service_test.go index d357d4262a..3b66f5d0fc 100644 --- a/pkg/cloud/report/service_test.go +++ b/pkg/cloud/report/service_test.go @@ -177,6 +177,7 @@ Scan Overview for AWS Account { "Type": "AWS", "ID": "AVD-AWS-9999", + "AVDID": "AVD-AWS-9999", "Title": "Do not use bad stuff", "Description": "Bad stuff is... bad", "Message": "instance is bad", @@ -212,6 +213,7 @@ Scan Overview for AWS Account { "Type": "AWS", "ID": "AVD-AWS-9999", + "AVDID": "AVD-AWS-9999", "Title": "Do not use bad stuff", "Description": "Bad stuff is... bad", "Message": "something failed", @@ -247,6 +249,7 @@ Scan Overview for AWS Account { "Type": "AWS", "ID": "AVD-AWS-9999", + "AVDID": "AVD-AWS-9999", "Title": "Do not use bad stuff", "Description": "Bad stuff is... bad", "Message": "something else failed", @@ -270,6 +273,7 @@ Scan Overview for AWS Account { "Type": "AWS", "ID": "AVD-AWS-9999", + "AVDID": "AVD-AWS-9999", "Title": "Do not use bad stuff", "Description": "Bad stuff is... bad", "Message": "something else failed again", diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 416c8ec503..f21bcf4488 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -753,6 +753,11 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { ) scanFlags.SecurityChecks = &securityChecks + reportFlagGroup := flag.NewReportFlagGroup() + compliance := flag.ComplianceFlag + compliance.Usage += fmt.Sprintf(" (%s)", types.ComplianceNsa) + reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand. + k8sFlags := &flag.Flags{ CacheFlagGroup: flag.NewCacheFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(), @@ -760,7 +765,7 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { LicenseFlagGroup: flag.NewLicenseFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(), - ReportFlagGroup: flag.NewReportFlagGroup(), + ReportFlagGroup: reportFlagGroup, ScanFlagGroup: scanFlags, SecretFlagGroup: flag.NewSecretFlagGroup(), VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), @@ -810,13 +815,17 @@ func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { } func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { + reportFlagGroup := flag.NewReportFlagGroup() + compliance := flag.ComplianceFlag + compliance.Usage += fmt.Sprintf(" (%s, %s)", types.ComplianceAWSCIS12, types.ComplianceAWSCIS14) + reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand. awsFlags := &flag.Flags{ AWSFlagGroup: flag.NewAWSFlagGroup(), CloudFlagGroup: flag.NewCloudFlagGroup(), MisconfFlagGroup: flag.NewMisconfFlagGroup(), RegoFlagGroup: flag.NewRegoFlagGroup(), - ReportFlagGroup: flag.NewReportFlagGroup(), + ReportFlagGroup: reportFlagGroup, } services := awsScanner.AllSupportedServices() diff --git a/pkg/flag/report_flags.go b/pkg/flag/report_flags.go index 5b3f6ed5a8..de5f9a3226 100644 --- a/pkg/flag/report_flags.go +++ b/pkg/flag/report_flags.go @@ -89,7 +89,7 @@ var ( Name: "compliance", ConfigName: "scan.compliance", Value: "", - Usage: "compliance report to generate (nsa)", + Usage: "compliance report to generate", } ) diff --git a/pkg/types/report.go b/pkg/types/report.go index 8f96d1ed86..9aea99a4b2 100644 --- a/pkg/types/report.go +++ b/pkg/types/report.go @@ -8,7 +8,7 @@ import ( ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" ) -var Compliances = []string{ComplianceNsa} +var Compliances = []string{ComplianceNsa, ComplianceAWSCIS12, ComplianceAWSCIS14} // Report represents a scan result type Report struct { @@ -51,7 +51,9 @@ const ( ClassCustom = "custom" // ComplianceNsa is the compliance checks for nsa - ComplianceNsa = Compliance("nsa") + ComplianceNsa = Compliance("nsa") + ComplianceAWSCIS12 = Compliance("awscis1.2") + ComplianceAWSCIS14 = Compliance("awscis1.4") ) // Result holds a target and detected vulnerabilities