diff --git a/ale_linters/cloudformation/checkov.vim b/ale_linters/cloudformation/checkov.vim new file mode 100644 index 00000000..2b3037ed --- /dev/null +++ b/ale_linters/cloudformation/checkov.vim @@ -0,0 +1,41 @@ +" Author: J. Handsel , Thyme-87 +" Description: use checkov for providing warnings for cloudformation via ale + +call ale#Set('cloudformation_checkov_executable', 'checkov') +call ale#Set('cloudformation_checkov_options', '') + +function! ale_linters#cloudformation#checkov#GetExecutable(buffer) abort + return ale#Var(a:buffer, 'cloudformation_checkov_executable') +endfunction + +function! ale_linters#cloudformation#checkov#GetCommand(buffer) abort + return '%e ' . '-f %t -o json --quiet --framework cloudformation ' . ale#Var(a:buffer, 'cloudformation_checkov_options') +endfunction + +function! ale_linters#cloudformation#checkov#Handle(buffer, lines) abort + let l:output = [] + + let l:results = get(get(ale#util#FuzzyJSONDecode(a:lines, {}), 'results', []), 'failed_checks', []) + + for l:violation in l:results + call add(l:output, { + \ 'filename': l:violation['file_path'], + \ 'lnum': l:violation['file_line_range'][0], + \ 'end_lnum': l:violation['file_line_range'][1], + \ 'text': l:violation['check_name'] . ' [' . l:violation['check_id'] . ']', + \ 'detail': l:violation['check_id'] . ': ' . l:violation['check_name'] . "\n" . + \ 'For more information, see: '. l:violation['guideline'], + \ 'type': 'W', + \ }) + endfor + + return l:output +endfunction + +call ale#linter#Define('cloudformation', { +\ 'name': 'checkov', +\ 'output_stream': 'stdout', +\ 'executable': function('ale_linters#cloudformation#checkov#GetExecutable'), +\ 'command': function('ale_linters#cloudformation#checkov#GetCommand'), +\ 'callback': 'ale_linters#cloudformation#checkov#Handle', +\}) diff --git a/doc/ale-cloudformation.txt b/doc/ale-cloudformation.txt index 56390149..0b9cd787 100644 --- a/doc/ale-cloudformation.txt +++ b/doc/ale-cloudformation.txt @@ -41,5 +41,51 @@ Just put the following in `ftdetect/cloudformation.vim`: > This will get both cloudformation and yaml linters to work on any file with `.template.yaml` extension. + +=============================================================================== +checkov *ale-cloudformation-checkov* + + *ale-options.cloudformation_checkov_executable* + *g:ale_cloudformation_checkov_executable* + *b:ale_cloudformation_checkov_executable* +cloudformation_checkov_executable +g:ale_cloudformation_checkov_executable + Type: |String| + Default: `'checkov'` + + This variable can be changed to use a different executable for checkov. + + *ale-options.cloudformation_checkov_options* + *g:ale_cloudformation_checkov_options* + *b:ale_cloudformation_checkov_options* +cloudformation_checkov_options +g:ale_cloudformation_checkov_options + Type: |String| + Default: `''` + + This variable can be changed to set additional options for checkov. + + +------------------------------------------------------------------------------- +Configuration + +To get chekov to work with cloudformation files (rather than general yaml +files) we must set the buffer |filetype| to `yaml.cloudformation`. This +causes ALE to lint the file with linters configured for cloudformation and +YAML files. + +One option is to put the following in `ftdetect/cloudformation.vim`: > + + au BufRead,BufNewFile *.template.yaml set filetype=yaml.cloudformation + +This will get both cloudformation and yaml linters to work on any file with +`.template.yaml` extension. + +Another option is to check for the presence of 'AWSTemplateFormatVersion' in +the yaml file: > + + au BufRead,BufNewFile *.yaml,*.yml if search('AWSTemplateFormatVersion', 'nw') | set filetype=yaml.cloudformation | endif +< + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index d2aa06cf..880527a1 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -121,6 +121,7 @@ Notes: * `joker` * CloudFormation * `cfn-python-lint` + * `checkov` * CMake * `cmake-format` * `cmake-lint` diff --git a/doc/ale.txt b/doc/ale.txt index 4db6f4f7..e919326c 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3419,6 +3419,7 @@ documented in additional help files. joker.................................|ale-clojure-joker| cloudformation..........................|ale-cloudformation-options| cfn-python-lint.......................|ale-cloudformation-cfn-python-lint| + checkov...............................|ale-cloudformation-checkov| cmake...................................|ale-cmake-options| cmakelint.............................|ale-cmake-cmakelint| cmake-lint............................|ale-cmake-cmake-lint| diff --git a/supported-tools.md b/supported-tools.md index 81305eac..7ad213e9 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -131,6 +131,7 @@ formatting. * [joker](https://github.com/candid82/joker) * CloudFormation * [cfn-python-lint](https://github.com/awslabs/cfn-python-lint) + * [checkov](https://github.com/bridgecrewio/checkov) * CMake * [cmake-format](https://github.com/cheshirekow/cmake_format) * [cmake-lint](https://github.com/cheshirekow/cmake_format) diff --git a/test/handler/test_cloudformation_checkov_handler.vader b/test/handler/test_cloudformation_checkov_handler.vader new file mode 100644 index 00000000..d2ecd493 --- /dev/null +++ b/test/handler/test_cloudformation_checkov_handler.vader @@ -0,0 +1,86 @@ +Before: + runtime ale_linters/cloudformation/checkov.vim + call ale#test#SetFilename('sample.template.yaml') + +After: + call ale#linter#Reset() + +Execute(Handle output for no findings correctly): + AssertEqual + \ [], + \ ale_linters#cloudformation#checkov#Handle(bufnr(''), [ + \'{', + \' "passed": 0,', + \' "failed": 0,', + \' "skipped": 0,', + \' "parsing_errors": 0,', + \' "resource_count": 0,', + \' "checkov_version": "3.2.415"', + \'}' + \]) + +Execute(Handle output for all tests passed): + AssertEqual + \ [], + \ ale_linters#cloudformation#checkov#Handle(bufnr(''), [ + \'{', + \' "check_type": "cloudformation",', + \' "results": {', + \' "failed_checks": []', + \' },', + \' "summary": {', + \' "passed": 18,', + \' "failed": 0,', + \' "skipped": 0,', + \' "parsing_errors": 0,', + \' "resource_count": 3,', + \' "checkov_version": "3.2.415"', + \' }', + \'}' + \]) + +Execute(The JSON output of checkov should be handled correctly): + AssertEqual + \ [ + \ { + \ 'filename': '/sample.template.yaml', + \ 'lnum': 57, + \ 'end_lnum': 79, + \ 'text': 'Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ) [CKV_AWS_116]', + \ 'detail': "CKV_AWS_116: Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)\n" . + \ 'For more information, see: https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-aws-lambda-function-is-configured-for-a-dead-letter-queue-dlq', + \ 'type': 'W', + \ } + \ ], + \ ale_linters#cloudformation#checkov#Handle(bufnr(''), [ + \'{', + \' "check_type": "cloudformation",', + \' "results": {', + \' "failed_checks": [', + \' {', + \' "check_id": "CKV_AWS_116",', + \' "bc_check_id": "BC_AWS_GENERAL_64",', + \' "check_name": "Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)",', + \' "check_result": {', + \' "result": "FAILED",', + \' "evaluated_keys": [', + \' "Properties/DeadLetterQueue/TargetArn"', + \' ]', + \' },', + \' "file_path": "/sample.template.yaml",', + \' "repo_file_path": "/sample.template.yaml",', + \' "file_line_range": [', + \' 57,', + \' 79', + \' ],', + \' "resource": "AWS::Serverless::Function.FunctionName",', + \' "evaluations": {},', + \' "check_class": "checkov.cloudformation.checks.resource.aws.LambdaDLQConfigured",', + \' "entity_tags": null,', + \' "resource_address": null,', + \' "guideline": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-aws-lambda-function-is-configured-for-a-dead-letter-queue-dlq"', + \' }', + \' ]', + \' }', + \'}' + \ ]) diff --git a/test/linter/test_cloudformation_checkov.vader b/test/linter/test_cloudformation_checkov.vader new file mode 100644 index 00000000..5c288447 --- /dev/null +++ b/test/linter/test_cloudformation_checkov.vader @@ -0,0 +1,15 @@ +Before: + call ale#assert#SetUpLinterTest('cloudformation', 'checkov') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be direct): + AssertLinter 'checkov', + \ ale#Escape('checkov') . ' -f %t -o json --quiet --framework cloudformation ' + +Execute(It should be possible to override the default command): + let b:ale_cloudformation_checkov_executable = '/bin/other/checkov' + AssertLinter '/bin/other/checkov', + \ ale#Escape('/bin/other/checkov') . ' -f %t -o json --quiet --framework cloudformation ' +