Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/add bandit linter support #204

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions .github/workflows/PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
run: poetry install -vvv
- name: Lint the Code
run: poetry run ni-python-styleguide lint
- name: (bandit) Lint the Code
run: poetry run ni-python-styleguide bandit

tests:
runs-on: ${{ matrix.os }}
Expand Down
20 changes: 16 additions & 4 deletions ni_python_styleguide/_cli.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import logging
import pathlib
import sys

import click
import toml

from ni_python_styleguide import _acknowledge_existing_errors
from ni_python_styleguide import _fix
from ni_python_styleguide import _Flake8Error
from ni_python_styleguide import _lint
from ni_python_styleguide import _acknowledge_existing_errors, _fix, _Flake8Error, _lint


def _qs_or_vs(verbosity):
Expand Down Expand Up @@ -190,3 +188,17 @@ def fix(obj, extend_ignore, file_or_dir, aggressive):
file_or_dir=file_or_dir or [pathlib.Path.cwd()],
aggressive=aggressive,
)


@main.command()
@click.argument("file_or_dir", nargs=-1)
@click.pass_obj
def bandit(obj, file_or_dir):
"""Run Bandit security linter on the file(s)/directory(s) given."""
logging.basicConfig(level=logging.DEBUG)
pyproj_bandit_config = obj.get("PYPROJECT", {}).get("tool", {}).get("bandit", {})
_lint.lint_bandit(
qs_or_vs=_qs_or_vs(obj["VERBOSITY"]),
pyproject_config=pyproj_bandit_config,
file_or_dir=file_or_dir or [pathlib.Path.cwd()],
)
1 change: 1 addition & 0 deletions ni_python_styleguide/_config_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
FLAKE8_CONFIG_FILE = __FILE_DIR / "config.ini"
BLACK_CONFIG_FILE = __FILE_DIR / "config.toml"
ISORT_CONFIG_FILE = BLACK_CONFIG_FILE
BANDIT_CONFIG_FILE = BLACK_CONFIG_FILE
79 changes: 77 additions & 2 deletions ni_python_styleguide/_lint.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
"""Linting methods."""

import contextlib
import copy
import io
import logging
import pathlib
import sys
import typing

import bandit.cli.main
import flake8.main.application
import toml

from ni_python_styleguide import _config_constants, _Flake8Error, _utils

from ni_python_styleguide import _config_constants
from ni_python_styleguide import _Flake8Error
_logger = logging.getLogger(__name__)
_logger.addHandler(logging.NullHandler())


def lint(qs_or_vs, exclude, app_import_names, format, extend_ignore, file_or_dir):
Expand Down Expand Up @@ -41,3 +51,68 @@ def get_lint_output(qs_or_vs, exclude, app_import_names, format, extend_ignore,
pass
capture.seek(0)
return capture.read()


@contextlib.contextmanager
def _temp_sys_argv(args):
old = sys.argv
sys.argv = list(args)
try:
yield
finally:
sys.argv = old


@contextlib.contextmanager
def _temp_merged_config(base_config: dict, override_config_file: pathlib.Path):
with _utils.temp_file.multi_access_tempfile(suffix=".toml") as temp:
target = copy.deepcopy(base_config)
merged = {"tool": {"bandit": target}}
override_config = toml.load(override_config_file)["tool"]["bandit"]
for key, value in override_config.items():
if key in target and isinstance(value, list):
_logger.debug("Merging %s: %s", key, value)
target[key].extend(value)
elif key in target and isinstance(value, dict):
_logger.debug("Merging %s: %s", key, value)
target[key].update(value)
else:
_logger.debug("Overriding %s: %s", key, value)
target[key] = value
_logger.debug("Merged config: %s", merged)

with temp.open("w") as fout:
toml.dump(merged, fout)

yield temp


def _relative_to_cwd(path: str) -> pathlib.Path:
try:
return (pathlib.Path(path)).relative_to(pathlib.Path.cwd())
except ValueError:
return path


def lint_bandit(qs_or_vs, file_or_dir: typing.Tuple[pathlib.Path], pyproject_config: dict):
"""Run the bandit linter."""
with _temp_merged_config(
pyproject_config, _config_constants.BANDIT_CONFIG_FILE
) as merged_config:
args_list = list(
filter(
None,
[
"bandit",
qs_or_vs,
"-c",
str(merged_config),
"-r",
*[str(_relative_to_cwd(p)) for p in file_or_dir],
],
)
)
_logger.debug("Running bandit with args: %s", args_list)
with _temp_sys_argv(args_list):
# return
bandit.cli.main.main()
15 changes: 15 additions & 0 deletions ni_python_styleguide/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,18 @@ line-length = 100
[tool.isort]
profile = "black"
combine_as_imports = true

[tool.bandit]
# Tests are expected to use assert, and are not shipped to users.
assert_used = {skips = ["*/tests/**/test_*.py", "*/tests/test_*.py"]}
# These are common dirs that are not part of the codebase.
exclude_dirs = [
"__pycache__",
".coverage",
".git",
".mypy_cache",
".pytest_cache",
".venv",
".vs",
".vscode",
]
Loading
Loading