diff --git a/README.md b/README.md index fc568f1..a923d3c 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,14 @@ Now you will see a review with linting errors... ``` $ gitleaks --path=. -q | lintly --format=gitleaks ``` -- - [hadolint](https://github.com/hadolint/hadolint) +- [hadolint](https://github.com/hadolint/hadolint) ``` $ hadolint path/to/Dockerfile --format json |lintly --format=hadolint ``` +- [terrascan](https://github.com/accurics/terrascan) + ``` + $ terrascan scan -d path/to/terraform/file -o json |lintly --format=terrascan + ``` - [cfn-lint](https://github.com/aws-cloudformation/cfn-python-lint) ``` @@ -114,7 +118,7 @@ Options: (required) --commit-sha TEXT The commit Lintly is running against (required) - --format [unix|flake8|pylint-json|eslint|eslint-unix|stylelint|black|cfn-lint|cfn-nag] + --format [unix|flake8|pylint-json|eslint|eslint-unix|stylelint|black|cfn-lint|cfn-nag|bandit-json|gitleaks|hadolint|terrascan] The linting output format Lintly should expect to receive. Default "flake8" --context TEXT Override the commit status context diff --git a/lintly/parsers.py b/lintly/parsers.py index ba049b7..b73c315 100644 --- a/lintly/parsers.py +++ b/lintly/parsers.py @@ -412,6 +412,64 @@ def parse_violations(self, output): return violations +class TerrascanParser(BaseLintParser): + """ + Terrascan JSON format + { + "results": { + "violations": [ + { + "rule_name": "apiGatewayName", + "description": "Enable AWS CloudWatch Logs for APIs", + "rule_id": "AWS.API Gateway.Logging.Medium.0567", + "severity": "MEDIUM", + "category": "Logging", + "resource_name": "this", + "resource_type": "aws_api_gateway_stage", + "file": "api_gateway_config.tf", + "line": 15 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/path/to/the/file/location", + "iac_type": "terraform", + "scanned_at": "2021-03-17 18:46:52.24701 +0000 UTC", + "policies_validated": 562, + "violated_policies": 7, + "low": 4, + "medium": 1, + "high": 2 + } + } + } + + """ + + def parse_violations(self, output): + if not output: + return dict() + + json_data = json.loads(output) + + violations = collections.defaultdict(list) + + for violation_json in json_data["results"]["violations"]: + violation = Violation( + line=violation_json['line'], + column=0, + code="{} ({})".format( + violation_json["rule_id"], violation_json["rule_name"] + ), + message=violation_json["description"], + ) + + path = self._normalize_path(violation_json["file"]) + violations[path].append(violation) + + return violations + + DEFAULT_PARSER = LineRegexParser(r'^(?P.*):(?P\d+):(?P\d+): (?P\w\d+) (?P.*)$') @@ -458,4 +516,7 @@ def parse_violations(self, output): # hadolint JSON output "hadolint": HadolintParser(), + + # terrascan JSON output + "terrascan": TerrascanParser(), } diff --git a/setup.py b/setup.py index f53ebdb..05a448b 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def read(*parts): setup( name='ttam-lintly', - version='0.6.4', + version='0.6.5', url='https://github.com/23andMe/Lintly', license='MIT', author='Veda Nandusekar', diff --git a/tests/linters_output/terrascan.json b/tests/linters_output/terrascan.json new file mode 100644 index 0000000..d67c6da --- /dev/null +++ b/tests/linters_output/terrascan.json @@ -0,0 +1,39 @@ +{ + "results": { + "violations": [ + { + "rule_name": "lambdaNotEncryptedWithKms", + "description": "Lambda does not use KMS CMK key to protect environment variables.", + "rule_id": "AWS LambdaFunction", + "severity": "High", + "category": "Encryption and Key Management", + "resource_name": "local_zipfile", + "resource_type": "aws_lambda_function", + "file": "main.tf", + "line": 6 + }, + { + "rule_name": "apiGatewayName", + "description": "Enable AWS CloudWatch Logs for APIs", + "rule_id": "AWS.API Gateway.Logging.Medium.0567", + "severity": "MEDIUM", + "category": "Logging", + "resource_name": "this", + "resource_type": "aws_api_gateway_stage", + "file": "api_gateway_config.tf", + "line": 15 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/path/to/the/file/location", + "iac_type": "terraform", + "scanned_at": "2021-03-17 18:46:52.24701 +0000 UTC", + "policies_validated": 562, + "violated_policies": 7, + "low": 4, + "medium": 1, + "high": 2 + } + } +} diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 1a73d0d..2139d06 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -156,6 +156,21 @@ class HadolintParserTestCase(ParserTestCaseMixin, unittest.TestCase): } +class TerrascanParserTestCase(ParserTestCaseMixin, unittest.TestCase): + parser = PARSERS['terrascan'] + linter_output_file_name = 'terrascan.json' + expected_violations = { + 'main.tf': [ + {'line': 6, 'column': 0, 'code': 'AWS LambdaFunction (lambdaNotEncryptedWithKms)', + 'message': 'Lambda does not use KMS CMK key to protect environment variables.'} + ], + 'api_gateway_config.tf': [ + {'line': 15, 'column': 0, 'code': 'AWS.API Gateway.Logging.Medium.0567 (apiGatewayName)', + 'message': 'Enable AWS CloudWatch Logs for APIs'} + ] + } + + class PylintJSONParserTestCase(ParserTestCaseMixin, unittest.TestCase): parser = PARSERS['pylint-json'] linter_output_file_name = 'pylint-json.txt'