From 503d3abc15463af68b817d685982721f134256a5 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Thu, 21 Jul 2022 16:36:46 -0700 Subject: [PATCH 1/2] feat(yaml): Add support for trivy.yaml (#143) * feat(yaml): Add support for trivy.yaml Signed-off-by: Simar * chore: fixing test using trivy v 0.30.0 * chore(deps): Update to use Trivy v0.30.2 Signed-off-by: Simar Co-authored-by: carolina valencia --- .github/workflows/build.yaml | 13 ++--- Dockerfile | 2 +- README.md | 39 +++++++++++++-- action.yaml | 4 ++ entrypoint.sh | 21 ++++++-- test/data/config-sarif.test | 77 +++++++++++++++++++++++++++++ test/data/image-trivyignores.test | 10 ++++ test/data/image.test | 10 ++++ test/data/repo.test | 81 +++++++++++++++++++++++++++++-- test/data/trivy.yaml | 2 + test/data/yamlconfig.test | 29 +++++++++++ test/test.bats | 80 ++++++++++++++++++------------ 12 files changed, 313 insertions(+), 55 deletions(-) create mode 100644 test/data/config-sarif.test create mode 100644 test/data/trivy.yaml create mode 100644 test/data/yamlconfig.test diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7046ddc..fcae929 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,7 +1,8 @@ name: "build" on: [push, pull_request] env: - TRIVY_VERSION: 0.29.2 + TRIVY_VERSION: 0.30.2 + BATS_LIB_PATH: '/usr/lib/' jobs: build: name: build @@ -11,7 +12,7 @@ jobs: - name: Setup BATS uses: mig4/setup-bats@v1 with: - bats-version: 1.2.1 + bats-version: 1.7.0 - name: Setup Bats libs uses: brokenpip3/setup-bats-libs@0.1.0 @@ -24,10 +25,4 @@ jobs: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v${{ env.TRIVY_VERSION }} - name: Test - run: bats --recursive --timing . - - - name: Debug show artifacts - if: always() - run: | - cat ./config.test - cat ./fs-scheck.test \ No newline at end of file + run: BATS_LIB_PATH=${{ env.BATS_LIB_PATH }} bats --recursive --timing . \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f79d906..259fd89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/aquasecurity/trivy:0.29.2 +FROM ghcr.io/aquasecurity/trivy:0.30.2 COPY entrypoint.sh / RUN apk --no-cache add bash curl RUN chmod +x /entrypoint.sh diff --git a/README.md b/README.md index 5a41974..486c4a1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ## Usage -### Workflow +### Scan CI Pipeline ```yaml name: build @@ -31,15 +31,13 @@ on: jobs: build: name: Build - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - name: Checkout code uses: actions/checkout@v2 - - name: Build an image from Dockerfile run: | docker build -t docker.io/my-organization/my-app:${{ github.sha }} . - - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: @@ -51,6 +49,39 @@ jobs: severity: 'CRITICAL,HIGH' ``` +### Scan CI Pipeline (w/ Trivy Config) + +```yaml +name: build +on: + push: + branches: + - master + pull_request: +jobs: + build: + name: Build + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Run Trivy vulnerability scanner in repo mode + uses: aquasecurity/trivy-action@add-support-for-trivy-config + with: + scan-type: 'fs' + ignore-unfixed: true + trivy-config: ./trivy.yaml +``` + +In this case `trivy.yaml` is a YAML configuration that is checked in as part of the repo. Detailed information is available on the Trivy website but an example is as follows: +```yaml +format: json +exit-code: 1 +severity: CRITICAL +``` + +It is possible to define all options in the `trivy.yaml` file. Specifying individual options via the action are left for backward compatibility purposes. ### Scanning a Tarball ```yaml diff --git a/action.yaml b/action.yaml index b3ec43c..b9b7d8c 100644 --- a/action.yaml +++ b/action.yaml @@ -85,6 +85,9 @@ inputs: github-pat: description: 'GitHub Personal Access Token (PAT) for submitting SBOM to GitHub Dependency Snapshot API' required: false + trivy-config: + description: 'path to trivy.yaml config' + required: false runs: using: 'docker' @@ -111,3 +114,4 @@ runs: - '-s ${{ inputs.security-checks }}' - '-t ${{ inputs.trivyignores }}' - '-u ${{ inputs.github-pat }}' + - '-v ${{ inputs.trivy-config }}' diff --git a/entrypoint.sh b/entrypoint.sh index 191e815..cebbba7 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash set -e -while getopts "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:" o; do +while getopts "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:" o; do case "${o}" in a) export scanType=${OPTARG} @@ -65,9 +65,13 @@ while getopts "a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:" o; do u) export githubPAT=${OPTARG} ;; + v) + export trivyConfig=${OPTARG} + ;; esac done + scanType=$(echo $scanType | tr -d '\r') export artifactRef="${imageRef}" if [ "${scanType}" = "repo" ] || [ "${scanType}" = "fs" ] || [ "${scanType}" = "config" ] || [ "${scanType}" = "rootfs" ];then @@ -157,10 +161,17 @@ if [ "$skipFiles" ];then done fi -echo "Running trivy with options: ${ARGS}" "${artifactRef}" -echo "Global options: " "${GLOBAL_ARGS}" -trivy $GLOBAL_ARGS ${scanType} $ARGS ${artifactRef} -returnCode=$? +trivyConfig=$(echo $trivyConfig | tr -d '\r') +if [ $trivyConfig ]; then + echo "Running Trivy with trivy.yaml config from: " $trivyConfig + trivy --config $trivyConfig ${scanType} $ARGS ${artifactRef} + returnCode=$? +else + echo "Running trivy with options: ${ARGS}" "${artifactRef}" + echo "Global options: " "${GLOBAL_ARGS}" + trivy $GLOBAL_ARGS ${scanType} $ARGS ${artifactRef} + returnCode=$? +fi # SARIF is special. We output all vulnerabilities, # regardless of severity level specified in this report. diff --git a/test/data/config-sarif.test b/test/data/config-sarif.test new file mode 100644 index 0000000..af06a4e --- /dev/null +++ b/test/data/config-sarif.test @@ -0,0 +1,77 @@ +{ + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json", + "runs": [ + { + "tool": { + "driver": { + "fullName": "Trivy Vulnerability Scanner", + "informationUri": "https://github.com/aquasecurity/trivy", + "name": "Trivy", + "rules": [ + { + "id": "DS002", + "name": "Misconfiguration", + "shortDescription": { + "text": "DS002" + }, + "fullDescription": { + "text": "Running containers with \u0026#39;root\u0026#39; user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a \u0026#39;USER\u0026#39; statement to the Dockerfile." + }, + "defaultConfiguration": { + "level": "error" + }, + "helpUri": "https://avd.aquasec.com/misconfig/ds002", + "help": { + "text": "Misconfiguration DS002\nType: Dockerfile Security Check\nSeverity: HIGH\nCheck: Image user should not be 'root'\nMessage: Specify at least 1 USER command in Dockerfile with non-root user as argument\nLink: [DS002](https://avd.aquasec.com/misconfig/ds002)\nRunning containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.", + "markdown": "**Misconfiguration DS002**\n| Type | Severity | Check | Message | Link |\n| --- | --- | --- | --- | --- |\n|Dockerfile Security Check|HIGH|Image user should not be 'root'|Specify at least 1 USER command in Dockerfile with non-root user as argument|[DS002](https://avd.aquasec.com/misconfig/ds002)|\n\nRunning containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile." + }, + "properties": { + "precision": "very-high", + "security-severity": "8.0", + "tags": [ + "misconfiguration", + "security", + "HIGH" + ] + } + } + ], + "version": "0.30.2" + } + }, + "results": [ + { + "ruleId": "DS002", + "ruleIndex": 0, + "level": "error", + "message": { + "text": "Artifact: Dockerfile\nType: dockerfile\nVulnerability DS002\nSeverity: HIGH\nMessage: Specify at least 1 USER command in Dockerfile with non-root user as argument\nLink: [DS002](https://avd.aquasec.com/misconfig/ds002)" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Dockerfile", + "uriBaseId": "ROOTPATH" + }, + "region": { + "startLine": 1, + "startColumn": 1, + "endLine": 1, + "endColumn": 1 + } + } + } + ] + } + ], + "columnKind": "utf16CodeUnits", + "originalUriBaseIds": { + "ROOTPATH": { + "uri": "file:///" + } + } + } + ] +} \ No newline at end of file diff --git a/test/data/image-trivyignores.test b/test/data/image-trivyignores.test index 16013de..a7a4d7b 100644 --- a/test/data/image-trivyignores.test +++ b/test/data/image-trivyignores.test @@ -1,3 +1,8 @@ + +knqyf263/vuln-image:1.2.3 (alpine 3.7.1) +======================================== +Total: 19 (CRITICAL: 19) + ┌─────────────┬────────────────┬──────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├─────────────┼────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤ @@ -67,6 +72,11 @@ │ sqlite-libs │ CVE-2019-8457 │ CRITICAL │ 3.21.0-r1 │ 3.25.3-r1 │ sqlite: heap out-of-bound read in function rtreenode() │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2019-8457 │ └─────────────┴────────────────┴──────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────────┘ + +rust-app/Cargo.lock (cargo) +=========================== +Total: 1 (CRITICAL: 1) + ┌──────────┬────────────────┬──────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├──────────┼────────────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤ diff --git a/test/data/image.test b/test/data/image.test index 3a76eac..51c8d95 100644 --- a/test/data/image.test +++ b/test/data/image.test @@ -1,3 +1,8 @@ + +knqyf263/vuln-image:1.2.3 (alpine 3.7.1) +======================================== +Total: 19 (CRITICAL: 19) + ┌─────────────┬────────────────┬──────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├─────────────┼────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤ @@ -67,6 +72,11 @@ │ sqlite-libs │ CVE-2019-8457 │ CRITICAL │ 3.21.0-r1 │ 3.25.3-r1 │ sqlite: heap out-of-bound read in function rtreenode() │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2019-8457 │ └─────────────┴────────────────┴──────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────────┘ + +rust-app/Cargo.lock (cargo) +=========================== +Total: 4 (CRITICAL: 4) + ┌───────────┬────────────────┬──────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├───────────┼────────────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤ diff --git a/test/data/repo.test b/test/data/repo.test index 08692e8..85eb94a 100644 --- a/test/data/repo.test +++ b/test/data/repo.test @@ -1,5 +1,76 @@ -┌──────────┬──────────────────────────────┬──────────┬─────────┬─────────────────────────┐ -│ Category │ Description │ Severity │ Line No │ Match │ -├──────────┼──────────────────────────────┼──────────┼─────────┼─────────────────────────┤ -│ GitHub │ GitHub Personal Access Token │ CRITICAL │ 5 │ export GITHUB_PAT=***** │ -└──────────┴──────────────────────────────┴──────────┴─────────┴─────────────────────────┘ +{ + "SchemaVersion": 2, + "ArtifactName": "https://github.com/krol3/demo-trivy/", + "ArtifactType": "repository", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "env", + "Class": "secret", + "Secrets": [ + { + "RuleID": "github-pat", + "Category": "GitHub", + "Severity": "CRITICAL", + "Title": "GitHub Personal Access Token", + "StartLine": 5, + "EndLine": 5, + "Code": { + "Lines": [ + { + "Number": 3, + "Content": "export AWS_ACCESS_KEY_ID=1234567", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "Highlighted": "export AWS_ACCESS_KEY_ID=1234567", + "FirstCause": false, + "LastCause": false + }, + { + "Number": 4, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + }, + { + "Number": 5, + "Content": "export GITHUB_PAT=****************************************", + "IsCause": true, + "Annotation": "", + "Truncated": false, + "Highlighted": "export GITHUB_PAT=****************************************", + "FirstCause": true, + "LastCause": true + }, + { + "Number": 6, + "Content": "", + "IsCause": false, + "Annotation": "", + "Truncated": false, + "FirstCause": false, + "LastCause": false + } + ] + }, + "Match": "export GITHUB_PAT=****************************************" + } + ] + } + ] +} diff --git a/test/data/trivy.yaml b/test/data/trivy.yaml new file mode 100644 index 0000000..1bb57b4 --- /dev/null +++ b/test/data/trivy.yaml @@ -0,0 +1,2 @@ +format: json +severity: CRITICAL \ No newline at end of file diff --git a/test/data/yamlconfig.test b/test/data/yamlconfig.test new file mode 100644 index 0000000..ff3156f --- /dev/null +++ b/test/data/yamlconfig.test @@ -0,0 +1,29 @@ +{ + "SchemaVersion": 2, + "ArtifactName": ".", + "ArtifactType": "filesystem", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "Dockerfile", + "Class": "config", + "Type": "dockerfile", + "MisconfSummary": { + "Successes": 6, + "Failures": 0, + "Exceptions": 0 + } + } + ] +} diff --git a/test/test.bats b/test/test.bats index 25e9aef..31b5da3 100644 --- a/test/test.bats +++ b/test/test.bats @@ -1,61 +1,71 @@ #!/usr/bin/env bats -load '/usr/lib/bats-support/load.bash' -load '/usr/lib/bats-assert/load.bash' +bats_load_library bats-support +bats_load_library bats-assert +bats_load_library bats-file + +@test "trivy repo with securityCheck secret only" { + # trivy repo --format json --output repo.test --security-checks=secret https://github.com/krol3/demo-trivy/ + run ./entrypoint.sh '-b json' '-h repo.test' '-s secret' '-a repo' '-j https://github.com/krol3/demo-trivy/' + run diff repo.test ./test/data/repo.test + echo "$output" + assert_files_equal repo.test ./test/data/repo.test +} @test "trivy image" { # trivy image --severity CRITICAL --output image.test knqyf263/vuln-image:1.2.3 - ./entrypoint.sh '-a image' '-i knqyf263/vuln-image:1.2.3' '-h image.test' '-g CRITICAL' - result="$(diff ./test/data/image.test image.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a image' '-i knqyf263/vuln-image:1.2.3' '-h image.test' '-g CRITICAL' + run diff image.test ./test/data/image.test + echo "$output" + assert_files_equal image.test ./test/data/image.test } -@test "trivy image sarif report" { - # trivy image --severity CRITICAL -f sarif --output image-sarif.test knqyf263/vuln-image:1.2.3 - ./entrypoint.sh '-a image' '-b sarif' '-i knqyf263/vuln-image:1.2.3' '-h image-sarif.test' '-g CRITICAL' - result="$(diff ./test/data/image-sarif.test image-sarif.test)" - [ "$result" == '' ] +@test "trivy config sarif report" { + # trivy config --format sarif --output config-sarif.test . + run ./entrypoint.sh '-a config' '-b sarif' '-h config-sarif.test' '-j .' + run diff config-sarif.test ./test/data/config-sarif.test + echo "$output" + assert_files_equal config-sarif.test ./test/data/config-sarif.test } @test "trivy config" { # trivy config --format json --output config.test . - ./entrypoint.sh '-a config' '-b json' '-j .' '-h config.test' - result="$(diff ./test/data/config.test config.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a config' '-b json' '-j .' '-h config.test' + run diff config.test ./test/data/config.test + echo "$output" + assert_files_equal config.test ./test/data/config.test } @test "trivy rootfs" { # trivy rootfs --output rootfs.test . - ./entrypoint.sh '-a rootfs' '-j .' '-h rootfs.test' - result="$(diff ./test/data/rootfs.test rootfs.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a rootfs' '-j .' '-h rootfs.test' + run diff rootfs.test ./test/data/rootfs.test + echo "$output" + assert_files_equal rootfs.test ./test/data/rootfs.test } @test "trivy fs" { # trivy fs --output fs.test . - ./entrypoint.sh '-a fs' '-j .' '-h fs.test' - result="$(diff ./test/data/fs.test fs.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a fs' '-j .' '-h fs.test' + run diff fs.test ./test/data/fs.test + echo "$output" + assert_files_equal fs.test ./test/data/fs.test } @test "trivy fs with securityChecks option" { # trivy fs --format json --security-checks=vuln,config --output fs-scheck.test . - ./entrypoint.sh '-a fs' '-b json' '-j .' '-s vuln,config,secret' '-h fs-scheck.test' - result="$(diff ./test/data/fs-scheck.test fs-scheck.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a fs' '-b json' '-j .' '-s vuln,config,secret' '-h fs-scheck.test' + run diff fs-scheck.test ./test/data/fs-scheck.test + echo "$output" + assert_files_equal fs-scheck.test ./test/data/fs-scheck.test } -@test "trivy repo with securityCheck secret only" { - # trivy repo --output repo.test --security-checks=secret https://github.com/krol3/demo-trivy/ - ./entrypoint.sh '-h repo.test' '-s secret' '-a repo' '-j https://github.com/krol3/demo-trivy/' - result="$(diff ./test/data/repo.test repo.test)" - [ "$result" == '' ] -} @test "trivy image with trivyIgnores option" { # cat ./test/data/.trivyignore1 ./test/data/.trivyignore2 > ./trivyignores ; trivy image --severity CRITICAL --output image-trivyignores.test --ignorefile ./trivyignores knqyf263/vuln-image:1.2.3 - ./entrypoint.sh '-a image' '-i knqyf263/vuln-image:1.2.3' '-h image-trivyignores.test' '-g CRITICAL' '-t ./test/data/.trivyignore1,./test/data/.trivyignore2' - result="$(diff ./test/data/image-trivyignores.test image-trivyignores.test)" - [ "$result" == '' ] + run ./entrypoint.sh '-a image' '-i knqyf263/vuln-image:1.2.3' '-h image-trivyignores.test' '-g CRITICAL' '-t ./test/data/.trivyignore1,./test/data/.trivyignore2' + run diff image-trivyignores.test ./test/data/image-trivyignores.test + echo "$output" + assert_files_equal image-trivyignores.test ./test/data/image-trivyignores.test } @test "trivy image with sbom output" { @@ -63,3 +73,11 @@ load '/usr/lib/bats-assert/load.bash' run ./entrypoint.sh "-a image" "-b github" "-i knqyf263/vuln-image:1.2.3" assert_output --partial '"package_url": "pkg:apk/ca-certificates@20171114-r0",' # TODO: Output contains time, need to mock } + +@test "trivy repo with trivy.yaml config" { + # trivy --config=./data/trivy.yaml fs --security-checks=config,secret --output=yamlconfig.test . + run ./entrypoint.sh "-a fs" "-j ." "-s config,secret" "-v ./test/data/trivy.yaml" "-h yamlconfig.test" + run diff yamlconfig.test ./test/data/yamlconfig.test + echo "$output" + assert_files_equal yamlconfig.test ./test/data/yamlconfig.test +} \ No newline at end of file From 81b9a6f5abb1047d697af7a3ca18c13f55a97315 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:08:58 -0600 Subject: [PATCH 2/2] Update Dockerfile (#152) --- .github/workflows/build.yaml | 2 +- Dockerfile | 2 +- test/data/config-sarif.test | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fcae929..f28e7bd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,7 +1,7 @@ name: "build" on: [push, pull_request] env: - TRIVY_VERSION: 0.30.2 + TRIVY_VERSION: 0.30.4 BATS_LIB_PATH: '/usr/lib/' jobs: build: diff --git a/Dockerfile b/Dockerfile index 259fd89..5fff011 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/aquasecurity/trivy:0.30.2 +FROM ghcr.io/aquasecurity/trivy:0.30.4 COPY entrypoint.sh / RUN apk --no-cache add bash curl RUN chmod +x /entrypoint.sh diff --git a/test/data/config-sarif.test b/test/data/config-sarif.test index af06a4e..1bafc0a 100644 --- a/test/data/config-sarif.test +++ b/test/data/config-sarif.test @@ -37,7 +37,7 @@ } } ], - "version": "0.30.2" + "version": "0.30.4" } }, "results": [