Add bandit linter for Python

This commit is contained in:
Martino Pilia 2019-01-26 11:48:03 +01:00
parent f12d312aa4
commit 0a5de2b42b
No known key found for this signature in database
GPG key ID: CDEE463095565E17
6 changed files with 191 additions and 2 deletions

View file

@ -175,7 +175,7 @@ formatting.
| proto | [protoc-gen-lint](https://github.com/ckaznocha/protoc-gen-lint) |
| Pug | [pug-lint](https://github.com/pugjs/pug-lint) |
| Puppet | [languageserver](https://github.com/lingua-pupuli/puppet-editor-services), [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) |
| Python | [autopep8](https://github.com/hhatto/autopep8), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](https://github.com/PyCQA/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pydocstyle](https://www.pydocstyle.org/), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture) !!, [yapf](https://github.com/google/yapf) |
| Python | [autopep8](https://github.com/hhatto/autopep8), [bandit](https://github.com/PyCQA/bandit), [black](https://github.com/ambv/black), [flake8](http://flake8.pycqa.org/en/latest/), [isort](https://github.com/timothycrosley/isort), [mypy](http://mypy-lang.org/), [prospector](https://github.com/PyCQA/prospector), [pycodestyle](https://github.com/PyCQA/pycodestyle), [pydocstyle](https://www.pydocstyle.org/), [pyls](https://github.com/palantir/python-language-server), [pyre](https://github.com/facebook/pyre-check), [pylint](https://www.pylint.org/) !!, [vulture](https://github.com/jendrikseipp/vulture) !!, [yapf](https://github.com/google/yapf) |
| QML | [qmlfmt](https://github.com/jesperhh/qmlfmt), [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint) |
| R | [lintr](https://github.com/jimhester/lintr) |
| Racket | [raco](https://docs.racket-lang.org/raco/) |

View file

@ -0,0 +1,58 @@
" Author: Martino Pilia <martino.pilia@gmail.com>
" Description: bandit linting for python files
call ale#Set('python_bandit_executable', 'bandit')
call ale#Set('python_bandit_options', '')
call ale#Set('python_bandit_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_bandit_auto_pipenv', 0)
function! ale_linters#python#bandit#GetExecutable(buffer) abort
if (ale#Var(a:buffer, 'python_auto_pipenv') ||
\ ale#Var(a:buffer, 'python_bandit_auto_pipenv'))
\ && ale#python#PipenvPresent(a:buffer)
return 'pipenv'
endif
return ale#python#FindExecutable(a:buffer, 'python_bandit', ['bandit'])
endfunction
function! ale_linters#python#bandit#GetCommand(buffer) abort
let l:executable = ale_linters#python#bandit#GetExecutable(a:buffer)
let l:flags = ' --format custom'
\ . ' --msg-template "{line}:{test_id}:{severity}:{msg}" '
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run bandit'
\ : ''
return ale#Escape(l:executable) . l:exec_args
\ . l:flags
\ . ale#Pad(ale#Var(a:buffer, 'python_bandit_options'))
\ . ' -'
endfunction
function! ale_linters#python#bandit#Handle(buffer, lines) abort
" Custom format defined in GetCommand via --msg-template
let l:pattern = '\v^([0-9]+):(B[0-9]+):([A-Z]+):(.*)$'
let l:severity = {'LOW': 'I', 'MEDIUM': 'W', 'HIGH': 'E'}
let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'bufnr': a:buffer,
\ 'lnum': str2nr(l:match[1]),
\ 'code': l:match[2],
\ 'type': l:severity[l:match[3]],
\ 'text': l:match[4],
\})
endfor
return l:output
endfunction
call ale#linter#Define('python', {
\ 'name': 'bandit',
\ 'executable_callback': 'ale_linters#python#bandit#GetExecutable',
\ 'command_callback': 'ale_linters#python#bandit#GetCommand',
\ 'callback': 'ale_linters#python#bandit#Handle',
\})

View file

@ -65,6 +65,45 @@ g:ale_python_autopep8_use_global *g:ale_python_autopep8_use_global*
See |ale-integrations-local-executables|
===============================================================================
bandit *ale-python-bandit*
g:ale_python_bandit_executable *g:ale_python_bandit_executable*
*b:ale_python_bandit_executable*
Type: |String|
Default: `'bandit'`
See |ale-integrations-local-executables|
Set this to `'pipenv'` to invoke `'pipenv` `run` `bandit'`.
g:ale_python_bandit_options *g:ale_python_bandit_options*
*b:ale_python_bandit_options*
Type: |String|
Default: `''`
This variable can be changed to add command-line arguments to the
bandit invocation.
g:ale_python_bandit_use_global *g:ale_python_bandit_use_global*
*b:ale_python_bandit_use_global*
Type: |Number|
Default: `get(g:, 'ale_use_global_executables', 0)`
See |ale-integrations-local-executables|
g:ale_python_bandit_auto_pipenv *g:ale_python_bandit_auto_pipenv*
*b:ale_python_bandit_auto_pipenv*
Type: |Number|
Default: `0`
Detect whether the file is inside a pipenv, and set the executable to `pipenv`
if true. This is overridden by a manually-set executable.
===============================================================================
black *ale-python-black*

View file

@ -257,6 +257,7 @@ CONTENTS *ale-contents*
cython..............................|ale-pyrex-cython|
python................................|ale-python-options|
autopep8............................|ale-python-autopep8|
bandit..............................|ale-python-bandit|
black...............................|ale-python-black|
flake8..............................|ale-python-flake8|
isort...............................|ale-python-isort|
@ -484,7 +485,7 @@ Notes:
* proto: `protoc-gen-lint`
* Pug: `pug-lint`
* Puppet: `languageserver`, `puppet`, `puppet-lint`
* Python: `autopep8`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pydocstyle`, `pyls`, `pyre`, `pylint`!!, `vulture`!!, `yapf`
* Python: `autopep8`, `bandit`, `black`, `flake8`, `isort`, `mypy`, `prospector`, `pycodestyle`, `pydocstyle`, `pyls`, `pyre`, `pylint`!!, `vulture`!!, `yapf`
* QML: `qmlfmt`, `qmllint`
* R: `lintr`
* Racket: `raco`

View file

@ -0,0 +1,49 @@
Before:
call ale#assert#SetUpLinterTest('python', 'bandit')
let b:bandit_flags = ' --format custom '
\ . '--msg-template "{line}:{test_id}:{severity}:{msg}" '
After:
call ale#assert#TearDownLinterTest()
unlet! b:bandit_flags
Execute(The bandit command callback should return default string):
AssertLinter 'bandit',
\ ale#Escape('bandit')
\ . b:bandit_flags
\ . ' -'
Execute(The bandit command callback should allow options):
let g:ale_python_bandit_options = '--configfile bandit.yaml'
AssertLinter 'bandit',
\ ale#Escape('bandit')
\ . b:bandit_flags
\ . ' --configfile bandit.yaml -'
Execute(The bandit executable should be configurable):
let g:ale_python_bandit_executable = '~/.local/bin/bandit'
AssertLinter '~/.local/bin/bandit',
\ ale#Escape('~/.local/bin/bandit')
\ . b:bandit_flags
\ . ' -'
Execute(Setting executable to 'pipenv' appends 'run bandit'):
let g:ale_python_bandit_executable = 'path/to/pipenv'
AssertLinter 'path/to/pipenv',
\ ale#Escape('path/to/pipenv')
\ . ' run bandit'
\ . b:bandit_flags
\ . ' -'
Execute(Pipenv is detected when python_bandit_auto_pipenv is set):
let g:ale_python_bandit_auto_pipenv = 1
call ale#test#SetFilename('/testplugin/test/python_fixtures/pipenv/whatever.py')
AssertLinter 'pipenv',
\ ale#Escape('pipenv')
\ . ' run bandit'
\ . b:bandit_flags
\ . ' -'

View file

@ -0,0 +1,42 @@
Before:
runtime ale_linters/python/bandit.vim
After:
call ale#linter#Reset()
Execute(The bandit handler for Python should parse input correctly):
AssertEqual
\ [
\ {
\ 'bufnr': 0,
\ 'lnum': 2,
\ 'code': 'B404',
\ 'type': 'I',
\ 'text': 'Consider possible security implications associated with subprocess module.',
\ },
\ {
\ 'bufnr': 0,
\ 'lnum': 4,
\ 'code': 'B305',
\ 'type': 'W',
\ 'text': 'Use of insecure cipher mode cryptography.hazmat.primitives.ciphers.modes.ECB.',
\ },
\ {
\ 'bufnr': 0,
\ 'lnum': 6,
\ 'code': 'B609',
\ 'type': 'E',
\ 'text': 'Possible wildcard injection in call: subprocess.Popen',
\ },
\ ],
\ ale_linters#python#bandit#Handle(0, [
\ '[main] INFO profile include tests: None',
\ '[main] INFO profile exclude tests: None',
\ '[main] INFO cli include tests: None',
\ '[main] INFO cli exclude tests: None',
\ '[main] INFO running on Python 3.7.2',
\ '[node_visitor] INFO Unable to find qualified name for module: <stdin>',
\ '2:B404:LOW:Consider possible security implications associated with subprocess module.',
\ '4:B305:MEDIUM:Use of insecure cipher mode cryptography.hazmat.primitives.ciphers.modes.ECB.',
\ '6:B609:HIGH:Possible wildcard injection in call: subprocess.Popen',
\ ])