diff --git a/linters/trivy/config_expected_issues.json b/linters/trivy/config_expected_issues.json new file mode 100644 index 000000000..3146e2434 --- /dev/null +++ b/linters/trivy/config_expected_issues.json @@ -0,0 +1,158 @@ +[ + { + "line": "1", + "code": "AVD-AWS-0028", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "terraform", + "isSecurity": true, + "message": "Instance does not require IMDS access to require a token", + "file": "test_data/aws.tf" + }, + { + "line": "1", + "code": "AVD-AWS-0131", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "terraform", + "isSecurity": true, + "message": "Root block device is not encrypted.", + "file": "test_data/aws.tf" + }, + { + "code": "DS026", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "docker", + "isSecurity": true, + "message": "Add HEALTHCHECK instruction in your Dockerfile", + "file": "test_data/basic.Dockerfile" + }, + { + "code": "DS002", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "docker", + "isSecurity": true, + "message": "Specify at least 1 USER command in Dockerfile with non-root user as argument", + "file": "test_data/basic.Dockerfile" + }, + { + "code": "KSV116", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "pod security-context-demo in default namespace should set spec.securityContext.runAsGroup, spec.securityContext.supplementalGroups[*] and spec.securityContext.fsGroup to integer greater than 0", + "file": "test_data/basic.yaml" + }, + { + "code": "KSV104", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "container sec-ctx-demo of pod security-context-demo in default namespace should specify a seccomp profile", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV106", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "container should drop all", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV020", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsUser' > 10000", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV003", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should add 'ALL' to 'securityContext.capabilities.drop'", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV013", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should specify an image tag", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV021", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsGroup' > 10000", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV014", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.readOnlyRootFilesystem' to true", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV030", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Either Pod or Container should set 'securityContext.seccompProfile.type' to 'RuntimeDefault'", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV011", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.cpu'", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV012", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsNonRoot' to true", + "file": "test_data/basic.yaml" + }, + { + "line": "14", + "code": "KSV018", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "yaml", + "isSecurity": true, + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.memory'", + "file": "test_data/basic.yaml" + } +] diff --git a/linters/trivy/plugin.yaml b/linters/trivy/plugin.yaml index 7d234cdd6..5e68c4947 100644 --- a/linters/trivy/plugin.yaml +++ b/linters/trivy/plugin.yaml @@ -26,14 +26,16 @@ tools: lint: definitions: - name: trivy - files: [lockfile, docker, yaml, terraform] tools: [trivy] suggest_if: files_present known_good_version: 0.37.1 commands: - - name: fs + - name: fs-vuln + files: [lockfile] output: sarif - run: trivy fs ${target} --format json --no-progress --cache-dir ${shared_cachedir} + run: + trivy fs ${target} --scanners vuln --format json --no-progress --cache-dir + ${shared_cachedir} success_codes: [0] read_output_from: stdout # Trivy does not support batching @@ -41,8 +43,24 @@ lint: is_security: true parser: runtime: python - run: python3 ${plugin}/linters/trivy/trivy_fs_to_sarif.py + run: python3 ${plugin}/linters/trivy/trivy_fs_vuln_to_sarif.py + - name: fs-secret + files: [ALL] + output: sarif + run: + trivy fs ${target} --scanners secret --format json --no-progress --cache-dir + ${shared_cachedir} + success_codes: [0] + read_output_from: stdout + # Trivy does not support batching + batch: false + is_security: true + enabled: false + parser: + runtime: python + run: python3 ${plugin}/linters/trivy/trivy_fs_secret_to_sarif.py - name: config + files: [docker, yaml, terraform] output: sarif run: trivy config ${target} --format json --cache-dir ${shared_cachedir} success_codes: [0] @@ -53,6 +71,7 @@ lint: parser: runtime: python run: python3 ${plugin}/linters/trivy/trivy_config_to_sarif.py + direct_configs: [trivy-secret.yaml] version_command: parse_regex: Version ${semver} run: trivy --version diff --git a/linters/trivy/test_data/secrets.py b/linters/trivy/test_data/secrets.py new file mode 100644 index 000000000..437b74964 --- /dev/null +++ b/linters/trivy/test_data/secrets.py @@ -0,0 +1,50 @@ +# This aws key is an example and not a real key. +aws_access_key_id = "AKIAXYZDQCEN4EXAMPLE" +aws_secret_access_key = "Tg0pz8Jii8hkLx4+PnUisM8GmKs3a2DK+EXAMPLE" + +# The below keys are copied from https://github.com/dustin-decker/secretsandstuff +github_secret = "369963c1434c377428ca8531fbc46c0c43d037a0" + +basic_auth = "https://admin:admin@the-internet.herokuapp.com/basic_auth" + +priv_key = """ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +Hx7UPVlTK8dyvk1Z+Yw0nrfNClI= +-----END OPENSSH PRIVATE KEY----- +""" diff --git a/linters/trivy/test_data/trivy_v0.37.1_config.check.shot b/linters/trivy/test_data/trivy_v0.37.1_config.check.shot new file mode 100644 index 000000000..91efaf296 --- /dev/null +++ b/linters/trivy/test_data/trivy_v0.37.1_config.check.shot @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter trivy test config 1`] = ` +{ + "issues": [], + "lintActions": [ + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + ".trunk/trunk.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + "plugin.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "terraform", + "linter": "trivy", + "paths": [ + "test_data/aws.tf", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "docker", + "linter": "trivy", + "paths": [ + "test_data/basic.Dockerfile", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + "test_data/basic.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/trivy/test_data/trivy_v0.37.1_CUSTOM.check.shot b/linters/trivy/test_data/trivy_v0.37.1_fs-secret.check.shot similarity index 53% rename from linters/trivy/test_data/trivy_v0.37.1_CUSTOM.check.shot rename to linters/trivy/test_data/trivy_v0.37.1_fs-secret.check.shot index ba56324ca..955728b7c 100644 --- a/linters/trivy/test_data/trivy_v0.37.1_CUSTOM.check.shot +++ b/linters/trivy/test_data/trivy_v0.37.1_fs-secret.check.shot @@ -1,186 +1,181 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Testing linter trivy test CUSTOM 1`] = ` +exports[`Testing linter trivy test fs-secret 1`] = ` { - "issues": [], - "lintActions": [ - { - "command": "config", - "fileGroupName": "yaml", - "linter": "trivy", - "paths": [ - ".trunk/trunk.yaml", - ], - "verb": "TRUNK_VERB_CHECK", - }, + "issues": [ { - "command": "config", - "fileGroupName": "yaml", + "code": "private-key", + "file": "other_data/secrets.py", + "isSecurity": true, + "level": "LEVEL_HIGH", + "line": "11", "linter": "trivy", - "paths": [ - "plugin.yaml", - ], - "verb": "TRUNK_VERB_CHECK", + "message": "Asymmetric Private Key", + "targetType": "ALL", }, { - "command": "config", - "fileGroupName": "lockfile", + "code": "aws-access-key-id", + "file": "other_data/secrets.py", + "isSecurity": true, + "level": "LEVEL_HIGH", + "line": "2", "linter": "trivy", - "paths": [ - "test_data/Gemfile.lock", - ], - "verb": "TRUNK_VERB_CHECK", + "message": "AWS Access Key ID", + "targetType": "ALL", }, { - "command": "config", - "fileGroupName": "terraform", + "code": "aws-secret-access-key", + "file": "other_data/secrets.py", + "isSecurity": true, + "level": "LEVEL_HIGH", + "line": "3", "linter": "trivy", - "paths": [ - "test_data/aws.tf", - ], - "verb": "TRUNK_VERB_CHECK", + "message": "AWS Secret Access Key", + "targetType": "ALL", }, + ], + "lintActions": [ { - "command": "config", - "fileGroupName": "docker", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/basic.Dockerfile", + ".trunk/trunk.yaml", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "config", - "fileGroupName": "yaml", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/basic.yaml", + "other_data/secrets.py", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "config", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/go.mod", + "plugin.yaml", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "config", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/nested/requirements.txt", + "result.json", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "config", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/no_errors/requirements.txt", + "test_data/Gemfile.lock", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "config", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/requirements.txt", + "test_data/aws.tf", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "yaml", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - ".trunk/trunk.yaml", + "test_data/basic.Dockerfile", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "yaml", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "plugin.yaml", + "test_data/basic.yaml", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/Gemfile.lock", + "test_data/go.mod", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "terraform", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/aws.tf", + "test_data/nested/requirements.txt", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "docker", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/basic.Dockerfile", + "test_data/no_errors/requirements.txt", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "yaml", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/basic.yaml", + "test_data/requirements.txt", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/go.mod", + "trivy.test.ts", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/nested/requirements.txt", + "trivy_config_to_sarif.py", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/no_errors/requirements.txt", + "trivy_fs_secret_to_sarif.py", ], "verb": "TRUNK_VERB_CHECK", }, { - "command": "fs", - "fileGroupName": "lockfile", + "command": "fs-secret", + "fileGroupName": "ALL", "linter": "trivy", "paths": [ - "test_data/requirements.txt", + "trivy_fs_vuln_to_sarif.py", ], "verb": "TRUNK_VERB_CHECK", }, diff --git a/linters/trivy/test_data/trivy_v0.37.1_fs-vuln.check.shot b/linters/trivy/test_data/trivy_v0.37.1_fs-vuln.check.shot new file mode 100644 index 000000000..1d5a59177 --- /dev/null +++ b/linters/trivy/test_data/trivy_v0.37.1_fs-vuln.check.shot @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter trivy test fs-vuln 1`] = ` +{ + "issues": [], + "lintActions": [ + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/Gemfile.lock", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/go.mod", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/nested/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/no_errors/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/trivy/test_data/trivy_v0.44.1_config.check.shot b/linters/trivy/test_data/trivy_v0.44.1_config.check.shot new file mode 100644 index 000000000..91efaf296 --- /dev/null +++ b/linters/trivy/test_data/trivy_v0.44.1_config.check.shot @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter trivy test config 1`] = ` +{ + "issues": [], + "lintActions": [ + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + ".trunk/trunk.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + "plugin.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "terraform", + "linter": "trivy", + "paths": [ + "test_data/aws.tf", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "docker", + "linter": "trivy", + "paths": [ + "test_data/basic.Dockerfile", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "config", + "fileGroupName": "yaml", + "linter": "trivy", + "paths": [ + "test_data/basic.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/trivy/test_data/trivy_v0.44.1_fs-secret.check.shot b/linters/trivy/test_data/trivy_v0.44.1_fs-secret.check.shot new file mode 100644 index 000000000..07f0019fd --- /dev/null +++ b/linters/trivy/test_data/trivy_v0.44.1_fs-secret.check.shot @@ -0,0 +1,166 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter trivy test fs-secret 1`] = ` +{ + "issues": [ + { + "code": "private-key", + "file": "other_data/secrets.py", + "isSecurity": true, + "level": "LEVEL_HIGH", + "line": "11", + "linter": "trivy", + "message": "Asymmetric Private Key", + "targetType": "ALL", + }, + ], + "lintActions": [ + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + ".trunk/trunk.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "other_data/secrets.py", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "plugin.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "result.json", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/Gemfile.lock", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/aws.tf", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/basic.Dockerfile", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/basic.yaml", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/go.mod", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/nested/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/no_errors/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "trivy.test.ts", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "trivy_config_to_sarif.py", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "trivy_fs_secret_to_sarif.py", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "trivy_fs_vuln_to_sarif.py", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/trivy/test_data/trivy_v0.44.1_fs-vuln.check.shot b/linters/trivy/test_data/trivy_v0.44.1_fs-vuln.check.shot new file mode 100644 index 000000000..1d5a59177 --- /dev/null +++ b/linters/trivy/test_data/trivy_v0.44.1_fs-vuln.check.shot @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter trivy test fs-vuln 1`] = ` +{ + "issues": [], + "lintActions": [ + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/Gemfile.lock", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/go.mod", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/nested/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/no_errors/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "fs-vuln", + "fileGroupName": "lockfile", + "linter": "trivy", + "paths": [ + "test_data/requirements.txt", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/trivy/trivy.test.ts b/linters/trivy/trivy.test.ts index 2a4a32517..4c053acbc 100644 --- a/linters/trivy/trivy.test.ts +++ b/linters/trivy/trivy.test.ts @@ -1,16 +1,59 @@ import * as fs from "fs"; import path from "path"; -import { fuzzyLinterCheckTest } from "tests"; +import { customLinterCheckTest, fuzzyLinterCheckTest, TestCallback } from "tests"; +import { TrunkLintDriver } from "tests/driver"; import { FileIssue } from "tests/types"; +import { TEST_DATA } from "tests/utils"; import { createFuzzyMatcher } from "tests/utils/landing_state"; -// trunk-ignore(eslint/@typescript-eslint/no-unsafe-assignment) -const expectedFileIssues = JSON.parse( - fs.readFileSync(path.resolve(__dirname, "expected_issues.json")).toString() -); +const callbackGenerator = + (command: string, otherPreCheck?: TestCallback): TestCallback => + (driver) => { + const trunkYamlPath = ".trunk/trunk.yaml"; + const currentContents = driver.readFile(trunkYamlPath); + const trivyRegex = /- trivy@(.+)\n/; + const newContents = currentContents.replace( + trivyRegex, + `- trivy@$1:\n commands: [${command}]\n`, + ); + driver.writeFile(trunkYamlPath, newContents); + if (otherPreCheck) { + otherPreCheck(driver); + } + }; + +const configExpectedFileIssues = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "config_expected_issues.json")).toString(), +) as FileIssue[]; + +fuzzyLinterCheckTest({ + linterName: "trivy", + testName: "config", + args: "-a", + fileIssueAssertionCallback: createFuzzyMatcher(() => configExpectedFileIssues, 13), + preCheck: callbackGenerator("config"), +}); + +const vulnExpectedFileIssues = JSON.parse( + fs.readFileSync(path.resolve(__dirname, "vuln_expected_issues.json")).toString(), +) as FileIssue[]; fuzzyLinterCheckTest({ linterName: "trivy", - args: "-a -y", - fileIssueAssertionCallback: createFuzzyMatcher(() => expectedFileIssues as FileIssue[], 12), + testName: "fs-vuln", + args: "-a", + fileIssueAssertionCallback: createFuzzyMatcher(() => vulnExpectedFileIssues, 44), + preCheck: callbackGenerator("fs-vuln"), +}); + +// trivy won't scan files with "/test" in the filepath +const secretPreCheck = (driver: TrunkLintDriver) => { + driver.moveFile(path.join(TEST_DATA, "secrets.py"), path.join("other_data", "secrets.py")); +}; + +customLinterCheckTest({ + linterName: "trivy", + testName: "fs-secret", + args: "-a", + preCheck: callbackGenerator("fs-secret", secretPreCheck), }); diff --git a/linters/trivy/trivy_fs_secret_to_sarif.py b/linters/trivy/trivy_fs_secret_to_sarif.py new file mode 100644 index 000000000..ae4890ca2 --- /dev/null +++ b/linters/trivy/trivy_fs_secret_to_sarif.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +import json +import sys + +# Used to map OSV/GHSA severities to the corresponding SARIF severity +# OSV/GHSA: https://docs.github.com/en/code-security/security-advisories/global-security-advisories/about-the-github-advisory-database#about-cvss-levels +# SARIF: https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127839 +SARIF_SEVERITY_BY_OSV_SEVERITY = { + "CRITICAL": "error", + "HIGH": "error", + "MODERATE": "warning", + "MEDIUM": "warning", + "LOW": "note", +} + +DEFAULT_SARIF_SEVERITY = "error" + + +def get_sarif_severity(secret) -> str: + """Get the SARIF severity appropriate for a given OSV vulnerability entry.""" + if "Severity" not in secret: + return DEFAULT_SARIF_SEVERITY + + severity = secret["Severity"].upper() + + return SARIF_SEVERITY_BY_OSV_SEVERITY.get(severity, DEFAULT_SARIF_SEVERITY) + + +def to_result_sarif(path: str, severity: str, code: str, description: str, lineno: int): + return { + "level": severity, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": path, + }, + "region": { + "startColumn": 0, + "startLine": lineno, + }, + } + } + ], + "message": { + "text": description, + }, + "ruleId": code, + } + + +def main(argv): + trivy_json = json.load(sys.stdin) + results = [] + + for result in trivy_json.get("Results", []): + path = trivy_json["ArtifactName"] + for secret in result.get("Secrets", []): + code = secret["RuleID"] + description = secret["Title"] + lineno = secret.get("StartLine", 0) + + results.append( + to_result_sarif( + path, + get_sarif_severity(secret), + code, + description, + lineno, + ) + ) + + sarif = { + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"results": results}], + } + + print(json.dumps(sarif, indent=2)) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/linters/trivy/trivy_fs_to_sarif.py b/linters/trivy/trivy_fs_vuln_to_sarif.py similarity index 100% rename from linters/trivy/trivy_fs_to_sarif.py rename to linters/trivy/trivy_fs_vuln_to_sarif.py diff --git a/linters/trivy/expected_issues.json b/linters/trivy/vuln_expected_issues.json similarity index 80% rename from linters/trivy/expected_issues.json rename to linters/trivy/vuln_expected_issues.json index c3023b6e9..901231bd8 100644 --- a/linters/trivy/expected_issues.json +++ b/linters/trivy/vuln_expected_issues.json @@ -99,166 +99,6 @@ "message": "Vulnerability in 'rack': denial of service in header parsing. Current version is vulnerable: 2.2.6.2. Patch available: upgrade to ~> 2.0, >= 2.2.6.4, >= 3.0.6.1 or higher.", "file": "test_data/Gemfile.lock" }, - { - "line": "1", - "code": "AVD-AWS-0028", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "terraform", - "isSecurity": true, - "message": "Instance does not require IMDS access to require a token", - "file": "test_data/aws.tf" - }, - { - "line": "1", - "code": "AVD-AWS-0131", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "terraform", - "isSecurity": true, - "message": "Root block device is not encrypted.", - "file": "test_data/aws.tf" - }, - { - "line": null, - "code": "DS026", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "docker", - "isSecurity": true, - "message": "Add HEALTHCHECK instruction in your Dockerfile", - "file": "test_data/basic.Dockerfile" - }, - { - "line": null, - "code": "DS002", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "docker", - "isSecurity": true, - "message": "Specify at least 1 USER command in Dockerfile with non-root user as argument", - "file": "test_data/basic.Dockerfile" - }, - { - "line": null, - "code": "KSV116", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "pod security-context-demo in default namespace should set spec.securityContext.runAsGroup, spec.securityContext.supplementalGroups[*] and spec.securityContext.fsGroup to integer greater than 0", - "file": "test_data/basic.yaml" - }, - { - "line": null, - "code": "KSV104", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "container sec-ctx-demo of pod security-context-demo in default namespace should specify a seccomp profile", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV106", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "container should drop all", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV020", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsUser' > 10000", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV003", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should add 'ALL' to 'securityContext.capabilities.drop'", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV013", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should specify an image tag", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV021", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsGroup' > 10000", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV014", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.readOnlyRootFilesystem' to true", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV030", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Either Pod or Container should set 'securityContext.seccompProfile.type' to 'RuntimeDefault'", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV011", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.cpu'", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV012", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsNonRoot' to true", - "file": "test_data/basic.yaml" - }, - { - "line": "14", - "code": "KSV018", - "level": "LEVEL_HIGH", - "linter": "trivy", - "targetType": "yaml", - "isSecurity": true, - "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.memory'", - "file": "test_data/basic.yaml" - }, { "line": "7", "code": "CVE-2022-30781",