diff options
-rw-r--r-- | ale_linters/terraform/tfsec.vim | 87 | ||||
-rw-r--r-- | doc/ale-supported-languages-and-tools.txt | 1 | ||||
-rw-r--r-- | doc/ale-terraform.txt | 19 | ||||
-rw-r--r-- | doc/ale.txt | 1 | ||||
-rw-r--r-- | supported-tools.md | 1 | ||||
-rw-r--r-- | test/handler/test_tfsec_handler.vader | 52 | ||||
-rw-r--r-- | test/linter/test_terraform_tfsec.vader | 38 | ||||
-rw-r--r-- | test/test-files/tfsec/json/.tfsec/config.json | 0 | ||||
-rw-r--r-- | test/test-files/tfsec/json/main.tf | 0 | ||||
-rw-r--r-- | test/test-files/tfsec/yml/.tfsec/config.yml | 0 | ||||
-rw-r--r-- | test/test-files/tfsec/yml/main.tf | 0 |
11 files changed, 199 insertions, 0 deletions
diff --git a/ale_linters/terraform/tfsec.vim b/ale_linters/terraform/tfsec.vim new file mode 100644 index 00000000..d29cdd13 --- /dev/null +++ b/ale_linters/terraform/tfsec.vim @@ -0,0 +1,87 @@ +" Description: tfsec for Terraform files +" +" See: https://www.terraform.io/ +" https://github.com/aquasecurity/tfsec + +call ale#Set('terraform_tfsec_options', '') +call ale#Set('terraform_tfsec_executable', 'tfsec') + +let s:separator = has('win32') ? '\' : '/' + +function! ale_linters#terraform#tfsec#Handle(buffer, lines) abort + let l:output = [] + let l:json = ale#util#FuzzyJSONDecode(a:lines, {}) + + " if there's no warning, 'result' is `null`. + if empty(get(l:json, 'results')) + return l:output + endif + + for l:result in get(l:json, 'results', []) + if l:result.severity is# 'LOW' + let l:type = 'I' + elseif l:result.severity is# 'CRITICAL' + let l:type = 'E' + else + let l:type = 'W' + endif + + call add(l:output, { + \ 'filename': l:result.location.filename, + \ 'lnum': l:result.location.start_line, + \ 'end_lnum': l:result.location.end_line, + \ 'text': l:result.description, + \ 'code': l:result.long_id, + \ 'type': l:type, + \}) + endfor + + return l:output +endfunction + +" Construct command arguments to tfsec with `terraform_tfsec_options`. +function! ale_linters#terraform#tfsec#GetCommand(buffer) abort + let l:cmd = '%e' + + let l:config = ale_linters#terraform#tfsec#FindConfig(a:buffer) + + if !empty(l:config) + let l:cmd .= ' --config-file ' . l:config + endif + + let l:opts = ale#Var(a:buffer, 'terraform_tfsec_options') + + if !empty(l:opts) + let l:cmd .= ' ' . l:opts + endif + + let l:cmd .= ' --format json' + + return l:cmd +endfunction + +" Find the nearest configuration file of tfsec. +function! ale_linters#terraform#tfsec#FindConfig(buffer) abort + let l:config_dir = ale#path#FindNearestDirectory(a:buffer, '.tfsec') + + if !empty(l:config_dir) + " https://aquasecurity.github.io/tfsec/v1.28.0/guides/configuration/config/ + for l:basename in ['config.yml', 'config.json'] + let l:config = ale#path#Simplify(join([l:config_dir, l:basename], s:separator)) + + if filereadable(l:config) + return ale#Escape(l:config) + endif + endfor + endif + + return '' +endfunction + +call ale#linter#Define('terraform', { +\ 'name': 'tfsec', +\ 'executable': {b -> ale#Var(b, 'terraform_tfsec_executable')}, +\ 'cwd': '%s:h', +\ 'command': function('ale_linters#terraform#tfsec#GetCommand'), +\ 'callback': 'ale_linters#terraform#tfsec#Handle', +\}) diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt index e0a2d557..476b57f6 100644 --- a/doc/ale-supported-languages-and-tools.txt +++ b/doc/ale-supported-languages-and-tools.txt @@ -597,6 +597,7 @@ Notes: * `terraform-ls` * `terraform-lsp` * `tflint` + * `tfsec` * Texinfo * `alex` * `cspell` diff --git a/doc/ale-terraform.txt b/doc/ale-terraform.txt index 6acf63ac..11422e64 100644 --- a/doc/ale-terraform.txt +++ b/doc/ale-terraform.txt @@ -115,5 +115,24 @@ g:ale_terraform_tflint_options *g:ale_terraform_tflint_options* =============================================================================== +tfsec *ale-terraform-tfsec* + +g:ale_terraform_tfsec_executable *g:ale_terraform_tfsec_executable* + *b:ale_terraform_tfsec_executable* + + Type: |String| + Default: `'tfsec'` + + This variable can be changed to use a different executable for tfsec. + +g:ale_terraform_tfsec_options *g:ale_terraform_tfsec_executable* + *b:ale_terraform_tfsec_executable* + + Type: |String| + Default: `''` + + This variable can be changed to pass custom CLI flags to tfsec. + +=============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index c4ffcf8d..b17e2e42 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3261,6 +3261,7 @@ documented in additional help files. terraform-ls..........................|ale-terraform-terraform-ls| terraform-lsp.........................|ale-terraform-terraform-lsp| tflint................................|ale-terraform-tflint| + tfsec.................................|ale-terraform-tfsec| tex.....................................|ale-tex-options| chktex................................|ale-tex-chktex| cspell................................|ale-tex-cspell| diff --git a/supported-tools.md b/supported-tools.md index a835ac2f..face9e39 100644 --- a/supported-tools.md +++ b/supported-tools.md @@ -606,6 +606,7 @@ formatting. * [terraform-ls](https://github.com/hashicorp/terraform-ls) * [terraform-lsp](https://github.com/juliosueiras/terraform-lsp) * [tflint](https://github.com/wata727/tflint) + * [tfsec](https://github.com/aquasecurity/tfsec) * Texinfo * [alex](https://github.com/get-alex/alex) * [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell) diff --git a/test/handler/test_tfsec_handler.vader b/test/handler/test_tfsec_handler.vader new file mode 100644 index 00000000..bf64b8fb --- /dev/null +++ b/test/handler/test_tfsec_handler.vader @@ -0,0 +1,52 @@ +Before: + runtime ale_linters/terraform/tfsec.vim + +After: + call ale#linter#Reset() + +Execute(The tfsec handler should handle empty outout): + AssertEqual + \ [], + \ ale_linters#terraform#tfsec#Handle(bufnr(''), ['{"results": null}']) + +Execute(The tfsec handler should parse results correctly): + AssertEqual + \ [ + \ { + \ 'filename': '/test/main.tf', + \ 'lnum': 10, + \ 'end_lnum': 12, + \ 'text': "IAM policy document uses sensitive action 'iam:PassRole' on wildcarded resource '*'", + \ 'code': 'aws-iam-no-policy-wildcards', + \ 'type': 'W', + \ }, + \], + \ ale_linters#terraform#tfsec#Handle(bufnr(''), json_encode( + \ { + \ "results": [ + \ { + \ "rule_id": "AVD-AWS-0057", + \ "long_id": "aws-iam-no-policy-wildcards", + \ "rule_description": "IAM policy should avoid use of wildcards and instead apply the principle of least privilege", + \ "rule_provider": "aws", + \ "rule_service": "iam", + \ "impact": "Overly permissive policies may grant access to sensitive resources", + \ "resolution": "Specify the exact permissions required, and to which resources they should apply instead of using wildcards.", + \ "links": [ + \ "https://aquasecurity.github.io/tfsec/v1.28.0/checks/aws/iam/no-policy-wildcards/", + \ "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document" + \ ], + \ "description": "IAM policy document uses sensitive action 'iam:PassRole' on wildcarded resource '*'", + \ "severity": "HIGH", + \ "warning": v:false, + \ "status": 0, + \ "resource": "data.aws_iam_policy_document.default", + \ "location": { + \ "filename": "/test/main.tf", + \ "start_line": 10, + \ "end_line": 12 + \ } + \ } + \ ] + \ } + \)) diff --git a/test/linter/test_terraform_tfsec.vader b/test/linter/test_terraform_tfsec.vader new file mode 100644 index 00000000..c3a7eae2 --- /dev/null +++ b/test/linter/test_terraform_tfsec.vader @@ -0,0 +1,38 @@ +Before: + call ale#assert#SetUpLinterTest('terraform', 'tfsec') + +After: + call ale#assert#TearDownLinterTest() + +Execute(The default command should be correct): + AssertLinter 'tfsec', ale#Escape('tfsec') . ' --format json' + +Execute(The default executable should be configurable): + let b:ale_terraform_tfsec_executable = '/usr/bin/tfsec' + + AssertLinter '/usr/bin/tfsec', ale#Escape('/usr/bin/tfsec') . ' --format json' + +Execute(Overriding options should work): + let g:ale_terraform_tfsec_executable = '/usr/local/bin/tfsec' + let g:ale_terraform_tfsec_options = '--minimum-severity MEDIUM' + + AssertLinter '/usr/local/bin/tfsec', + \ ale#Escape('/usr/local/bin/tfsec') . ' --minimum-severity MEDIUM --format json' + +Execute(Configuration yml file should be found): + call ale#test#SetFilename('../test-files/tfsec/yml/main.tf') + + AssertLinter 'tfsec', + \ ale#Escape('tfsec') + \ . ' --config-file ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/../test-files/tfsec/yml/.tfsec/config.yml')) + \ . ' --format json' + +Execute(Configuration json file should be found): + call ale#test#SetFilename('../test-files/tfsec/json/main.tf') + + AssertLinter 'tfsec', + \ ale#Escape('tfsec') + \ . ' --config-file ' + \ . ale#Escape(ale#path#Simplify(g:dir . '/../test-files/tfsec/json/.tfsec/config.json')) + \ . ' --format json' diff --git a/test/test-files/tfsec/json/.tfsec/config.json b/test/test-files/tfsec/json/.tfsec/config.json new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test-files/tfsec/json/.tfsec/config.json diff --git a/test/test-files/tfsec/json/main.tf b/test/test-files/tfsec/json/main.tf new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test-files/tfsec/json/main.tf diff --git a/test/test-files/tfsec/yml/.tfsec/config.yml b/test/test-files/tfsec/yml/.tfsec/config.yml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test-files/tfsec/yml/.tfsec/config.yml diff --git a/test/test-files/tfsec/yml/main.tf b/test/test-files/tfsec/yml/main.tf new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/test-files/tfsec/yml/main.tf |