diff --git a/linters/plugin.yaml b/linters/plugin.yaml index f5249f4a8..028b6b129 100644 --- a/linters/plugin.yaml +++ b/linters/plugin.yaml @@ -572,6 +572,10 @@ lint: extensions: - tf - tf.json + comments: + - hash + - slashes-inline + - slashes-block - name: textproto extensions: diff --git a/linters/trivy/config_expected_issues.json b/linters/trivy/config_expected_issues.json index 3146e2434..42cd243ec 100644 --- a/linters/trivy/config_expected_issues.json +++ b/linters/trivy/config_expected_issues.json @@ -1,158 +1,169 @@ [ { + "file": "test_data/aws.tf", "line": "1", + "message": "Instance does not require IMDS access to require a token", "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" + "isSecurity": true }, { + "file": "test_data/aws.tf", "line": "1", + "message": "Root block device is not encrypted.", "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" + "isSecurity": true }, { - "code": "DS026", + "file": "test_data/basic.Dockerfile", + "message": "Specify at least 1 USER command in Dockerfile with non-root user as argument", + "code": "DS002", "level": "LEVEL_HIGH", "linter": "trivy", "targetType": "docker", - "isSecurity": true, - "message": "Add HEALTHCHECK instruction in your Dockerfile", - "file": "test_data/basic.Dockerfile" + "isSecurity": true }, { - "code": "DS002", + "file": "test_data/basic.Dockerfile", + "message": "Add HEALTHCHECK instruction in your Dockerfile", + "code": "DS026", "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" + "isSecurity": true }, { - "code": "KSV116", + "file": "test_data/basic.yaml", + "message": "container \"sec-ctx-demo\" of pod \"security-context-demo\" in \"default\" namespace should specify a seccomp profile", + "code": "KSV104", "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" + "isSecurity": true }, { - "code": "KSV104", + "file": "test_data/basic.yaml", + "line": "14", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should add 'ALL' to 'securityContext.capabilities.drop'", + "code": "KSV003", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV106", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.cpu'", + "code": "KSV011", "level": "LEVEL_HIGH", "linter": "trivy", "targetType": "yaml", - "isSecurity": true, - "message": "container should drop all", - "file": "test_data/basic.yaml" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV020", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsNonRoot' to true", + "code": "KSV012", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV003", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should specify an image tag", + "code": "KSV013", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV013", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.readOnlyRootFilesystem' to true", + "code": "KSV014", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV021", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'resources.limits.memory'", + "code": "KSV018", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV014", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsUser' > 10000", + "code": "KSV020", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV030", + "message": "Container 'sec-ctx-demo' of Pod 'security-context-demo' should set 'securityContext.runAsGroup' > 10000", + "code": "KSV021", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV011", + "message": "Either Pod or Container should set 'securityContext.seccompProfile.type' to 'RuntimeDefault'", + "code": "KSV030", "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" + "isSecurity": true }, { + "file": "test_data/basic.yaml", "line": "14", - "code": "KSV012", + "message": "container should drop all", + "code": "KSV106", "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" + "isSecurity": true }, { - "line": "14", - "code": "KSV018", + "file": "test_data/main.tf", + "line": "17", + "message": "IAM policy document uses sensitive action 'logs:CreateLogGroup' on wildcarded resource '34c9c025-d988-4bcc-9d4a-4581749e7576:*'", + "code": "AVD-AWS-0057", "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" + "targetType": "terraform", + "isSecurity": true + }, + { + "file": "test_data/main.tf", + "line": "17", + "message": "Function does not have tracing enabled.", + "code": "AVD-AWS-0066", + "level": "LEVEL_HIGH", + "linter": "trivy", + "targetType": "terraform", + "isSecurity": true } ] diff --git a/linters/trivy/plugin.yaml b/linters/trivy/plugin.yaml index 05cb25422..9bb7e9f7c 100644 --- a/linters/trivy/plugin.yaml +++ b/linters/trivy/plugin.yaml @@ -30,6 +30,7 @@ lint: suggest_if: files_present description: A comprehensive and versatile security scanner known_good_version: 0.44.1 + # trivy supports --format template --template "@contrib/sarif.tpl", but it reports the wrong filepaths. commands: - name: fs-vuln files: [lockfile] diff --git a/linters/trivy/test_data/main.tf b/linters/trivy/test_data/main.tf new file mode 100644 index 000000000..66d9016c6 --- /dev/null +++ b/linters/trivy/test_data/main.tf @@ -0,0 +1,34 @@ +terraform { + required_version = ">= 1.4.6" + backend "local" { + path = "terraform.tfstate" + } + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.47.0" + } + } +} + +# AWS Provider +provider "aws" {} + +module "lambda_function" { + source = "terraform-aws-modules/lambda/aws" + version = "7.4.0" + function_name = "LambdaFunction" + handler = "lambda_handler.lambda_handler" + runtime = "python3.12" + local_existing_package = "lambda_package.zip" + create_package = false + create_current_version_allowed_triggers = false + attach_policy_json = false + timeout = 900 + ignore_source_code_hash = true + maximum_retry_attempts = 0 + # Because tc-db is private, this lambda function must run within the VPC + vpc_subnet_ids = ["subnet-1", "subnet-2", "subnet-3"] + vpc_security_group_ids = ["security-group-1", "security-group-2"] + attach_network_policy = true +} 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 index 91efaf296..bcf500d03 100644 --- a/linters/trivy/test_data/trivy_v0.44.1_config.check.shot +++ b/linters/trivy/test_data/trivy_v0.44.1_config.check.shot @@ -49,6 +49,15 @@ exports[`Testing linter trivy test config 1`] = ` ], "verb": "TRUNK_VERB_CHECK", }, + { + "command": "config", + "fileGroupName": "terraform", + "linter": "trivy", + "paths": [ + "test_data/main.tf", + ], + "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 index 07f0019fd..c5a910f63 100644 --- 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 @@ -96,6 +96,15 @@ exports[`Testing linter trivy test fs-secret 1`] = ` ], "verb": "TRUNK_VERB_CHECK", }, + { + "command": "fs-secret", + "fileGroupName": "ALL", + "linter": "trivy", + "paths": [ + "test_data/main.tf", + ], + "verb": "TRUNK_VERB_CHECK", + }, { "command": "fs-secret", "fileGroupName": "ALL", diff --git a/linters/trivy/trivy_config_to_sarif.py b/linters/trivy/trivy_config_to_sarif.py index 281d9a704..1bfa6d7f5 100755 --- a/linters/trivy/trivy_config_to_sarif.py +++ b/linters/trivy/trivy_config_to_sarif.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import json +import os import sys @@ -29,6 +30,7 @@ def to_result_sarif(path: str, vuln_id: str, description: str, line: int = 0): def main(argv): trivy_json = json.load(sys.stdin) + path = trivy_json["ArtifactName"] results = [] for result in trivy_json.get("Results", []): @@ -37,11 +39,15 @@ def main(argv): for vuln in result["Misconfigurations"]: vuln_id = vuln["ID"] message = vuln["Message"] - line_num = vuln.get("CauseMetadata", {}).get("StartLine", 0) - - results.append( - to_result_sarif(trivy_json["ArtifactName"], vuln_id, message, line_num) - ) + location_info = vuln.get("CauseMetadata", {}) + line_num = location_info.get("StartLine", 0) + for occurrence in location_info.get("Occurrences", []): + # Sometimes the original StartLine will reference a resource file + if occurrence.get("Filename") == os.path.basename(path): + line_num = occurrence["Location"]["StartLine"] + break + + results.append(to_result_sarif(path, vuln_id, message, line_num)) sarif = { "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",