updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
252
Backend/venv/lib/python3.12/site-packages/bandit/cli/baseline.py
Normal file
252
Backend/venv/lib/python3.12/site-packages/bandit/cli/baseline.py
Normal file
@@ -0,0 +1,252 @@
|
||||
#
|
||||
# Copyright 2015 Hewlett-Packard Enterprise
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# #############################################################################
|
||||
# Bandit Baseline is a tool that runs Bandit against a Git commit, and compares
|
||||
# the current commit findings to the parent commit findings.
|
||||
# To do this it checks out the parent commit, runs Bandit (with any provided
|
||||
# filters or profiles), checks out the current commit, runs Bandit, and then
|
||||
# reports on any new findings.
|
||||
# #############################################################################
|
||||
"""Bandit is a tool designed to find common security issues in Python code."""
|
||||
import argparse
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess # nosec: B404
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
try:
|
||||
import git
|
||||
except ImportError:
|
||||
git = None
|
||||
|
||||
bandit_args = sys.argv[1:]
|
||||
baseline_tmp_file = "_bandit_baseline_run.json_"
|
||||
current_commit = None
|
||||
default_output_format = "terminal"
|
||||
LOG = logging.getLogger(__name__)
|
||||
repo = None
|
||||
report_basename = "bandit_baseline_result"
|
||||
valid_baseline_formats = ["txt", "html", "json"]
|
||||
|
||||
"""baseline.py"""
|
||||
|
||||
|
||||
def main():
|
||||
"""Execute Bandit."""
|
||||
# our cleanup function needs this and can't be passed arguments
|
||||
global current_commit
|
||||
global repo
|
||||
|
||||
parent_commit = None
|
||||
output_format = None
|
||||
repo = None
|
||||
report_fname = None
|
||||
|
||||
init_logger()
|
||||
|
||||
output_format, repo, report_fname = initialize()
|
||||
|
||||
if not repo:
|
||||
sys.exit(2)
|
||||
|
||||
# #################### Find current and parent commits ####################
|
||||
try:
|
||||
commit = repo.commit()
|
||||
current_commit = commit.hexsha
|
||||
LOG.info("Got current commit: [%s]", commit.name_rev)
|
||||
|
||||
commit = commit.parents[0]
|
||||
parent_commit = commit.hexsha
|
||||
LOG.info("Got parent commit: [%s]", commit.name_rev)
|
||||
|
||||
except git.GitCommandError:
|
||||
LOG.error("Unable to get current or parent commit")
|
||||
sys.exit(2)
|
||||
except IndexError:
|
||||
LOG.error("Parent commit not available")
|
||||
sys.exit(2)
|
||||
|
||||
# #################### Run Bandit against both commits ####################
|
||||
output_type = (
|
||||
["-f", "txt"]
|
||||
if output_format == default_output_format
|
||||
else ["-o", report_fname]
|
||||
)
|
||||
|
||||
with baseline_setup() as t:
|
||||
bandit_tmpfile = f"{t}/{baseline_tmp_file}"
|
||||
|
||||
steps = [
|
||||
{
|
||||
"message": "Getting Bandit baseline results",
|
||||
"commit": parent_commit,
|
||||
"args": bandit_args + ["-f", "json", "-o", bandit_tmpfile],
|
||||
},
|
||||
{
|
||||
"message": "Comparing Bandit results to baseline",
|
||||
"commit": current_commit,
|
||||
"args": bandit_args + ["-b", bandit_tmpfile] + output_type,
|
||||
},
|
||||
]
|
||||
|
||||
return_code = None
|
||||
|
||||
for step in steps:
|
||||
repo.head.reset(commit=step["commit"], working_tree=True)
|
||||
|
||||
LOG.info(step["message"])
|
||||
|
||||
bandit_command = ["bandit"] + step["args"]
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(bandit_command) # nosec: B603
|
||||
except subprocess.CalledProcessError as e:
|
||||
output = e.output
|
||||
return_code = e.returncode
|
||||
else:
|
||||
return_code = 0
|
||||
output = output.decode("utf-8") # subprocess returns bytes
|
||||
|
||||
if return_code not in [0, 1]:
|
||||
LOG.error(
|
||||
"Error running command: %s\nOutput: %s\n",
|
||||
bandit_args,
|
||||
output,
|
||||
)
|
||||
|
||||
# #################### Output and exit ####################################
|
||||
# print output or display message about written report
|
||||
if output_format == default_output_format:
|
||||
print(output)
|
||||
else:
|
||||
LOG.info("Successfully wrote %s", report_fname)
|
||||
|
||||
# exit with the code the last Bandit run returned
|
||||
sys.exit(return_code)
|
||||
|
||||
|
||||
# #################### Clean up before exit ###################################
|
||||
@contextlib.contextmanager
|
||||
def baseline_setup():
|
||||
"""Baseline setup by creating temp folder and resetting repo."""
|
||||
d = tempfile.mkdtemp()
|
||||
yield d
|
||||
shutil.rmtree(d, True)
|
||||
|
||||
if repo:
|
||||
repo.head.reset(commit=current_commit, working_tree=True)
|
||||
|
||||
|
||||
# #################### Setup logging ##########################################
|
||||
def init_logger():
|
||||
"""Init logger."""
|
||||
LOG.handlers = []
|
||||
log_level = logging.INFO
|
||||
log_format_string = "[%(levelname)7s ] %(message)s"
|
||||
logging.captureWarnings(True)
|
||||
LOG.setLevel(log_level)
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setFormatter(logging.Formatter(log_format_string))
|
||||
LOG.addHandler(handler)
|
||||
|
||||
|
||||
# #################### Perform initialization and validate assumptions ########
|
||||
def initialize():
|
||||
"""Initialize arguments and output formats."""
|
||||
valid = True
|
||||
|
||||
# #################### Parse Args #########################################
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Bandit Baseline - Generates Bandit results compared to "
|
||||
"a baseline",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="Additional Bandit arguments such as severity filtering (-ll) "
|
||||
"can be added and will be passed to Bandit.",
|
||||
)
|
||||
if sys.version_info >= (3, 14):
|
||||
parser.suggest_on_error = True
|
||||
parser.color = False
|
||||
|
||||
parser.add_argument(
|
||||
"targets",
|
||||
metavar="targets",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help="source file(s) or directory(s) to be tested",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
dest="output_format",
|
||||
action="store",
|
||||
default="terminal",
|
||||
help="specify output format",
|
||||
choices=valid_baseline_formats,
|
||||
)
|
||||
|
||||
args, _ = parser.parse_known_args()
|
||||
|
||||
# #################### Setup Output #######################################
|
||||
# set the output format, or use a default if not provided
|
||||
output_format = (
|
||||
args.output_format if args.output_format else default_output_format
|
||||
)
|
||||
|
||||
if output_format == default_output_format:
|
||||
LOG.info("No output format specified, using %s", default_output_format)
|
||||
|
||||
# set the report name based on the output format
|
||||
report_fname = f"{report_basename}.{output_format}"
|
||||
|
||||
# #################### Check Requirements #################################
|
||||
if git is None:
|
||||
LOG.error("Git not available, reinstall with baseline extra")
|
||||
valid = False
|
||||
return (None, None, None)
|
||||
|
||||
try:
|
||||
repo = git.Repo(os.getcwd())
|
||||
|
||||
except git.exc.InvalidGitRepositoryError:
|
||||
LOG.error("Bandit baseline must be called from a git project root")
|
||||
valid = False
|
||||
|
||||
except git.exc.GitCommandNotFound:
|
||||
LOG.error("Git command not found")
|
||||
valid = False
|
||||
|
||||
else:
|
||||
if repo.is_dirty():
|
||||
LOG.error(
|
||||
"Current working directory is dirty and must be " "resolved"
|
||||
)
|
||||
valid = False
|
||||
|
||||
# if output format is specified, we need to be able to write the report
|
||||
if output_format != default_output_format and os.path.exists(report_fname):
|
||||
LOG.error("File %s already exists, aborting", report_fname)
|
||||
valid = False
|
||||
|
||||
# Bandit needs to be able to create this temp file
|
||||
if os.path.exists(baseline_tmp_file):
|
||||
LOG.error(
|
||||
"Temporary file %s needs to be removed prior to running",
|
||||
baseline_tmp_file,
|
||||
)
|
||||
valid = False
|
||||
|
||||
# we must validate -o is not provided, as it will mess up Bandit baseline
|
||||
if "-o" in bandit_args:
|
||||
LOG.error("Bandit baseline must not be called with the -o option")
|
||||
valid = False
|
||||
|
||||
return (output_format, repo, report_fname) if valid else (None, None, None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,207 @@
|
||||
# Copyright 2015 Red Hat Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Bandit is a tool designed to find common security issues in Python code."""
|
||||
import argparse
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
from bandit.core import extension_loader
|
||||
|
||||
PROG_NAME = "bandit_conf_generator"
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
template = """
|
||||
### Bandit config file generated from:
|
||||
# '{cli}'
|
||||
|
||||
### This config may optionally select a subset of tests to run or skip by
|
||||
### filling out the 'tests' and 'skips' lists given below. If no tests are
|
||||
### specified for inclusion then it is assumed all tests are desired. The skips
|
||||
### set will remove specific tests from the include set. This can be controlled
|
||||
### using the -t/-s CLI options. Note that the same test ID should not appear
|
||||
### in both 'tests' and 'skips', this would be nonsensical and is detected by
|
||||
### Bandit at runtime.
|
||||
|
||||
# Available tests:
|
||||
{test_list}
|
||||
|
||||
# (optional) list included test IDs here, eg '[B101, B406]':
|
||||
{test}
|
||||
|
||||
# (optional) list skipped test IDs here, eg '[B101, B406]':
|
||||
{skip}
|
||||
|
||||
### (optional) plugin settings - some test plugins require configuration data
|
||||
### that may be given here, per-plugin. All bandit test plugins have a built in
|
||||
### set of sensible defaults and these will be used if no configuration is
|
||||
### provided. It is not necessary to provide settings for every (or any) plugin
|
||||
### if the defaults are acceptable.
|
||||
|
||||
{settings}
|
||||
"""
|
||||
|
||||
|
||||
def init_logger():
|
||||
"""Init logger."""
|
||||
LOG.handlers = []
|
||||
log_level = logging.INFO
|
||||
log_format_string = "[%(levelname)5s]: %(message)s"
|
||||
logging.captureWarnings(True)
|
||||
LOG.setLevel(log_level)
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setFormatter(logging.Formatter(log_format_string))
|
||||
LOG.addHandler(handler)
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse arguments."""
|
||||
help_description = """Bandit Config Generator
|
||||
|
||||
This tool is used to generate an optional profile. The profile may be used
|
||||
to include or skip tests and override values for plugins.
|
||||
|
||||
When used to store an output profile, this tool will output a template that
|
||||
includes all plugins and their default settings. Any settings which aren't
|
||||
being overridden can be safely removed from the profile and default values
|
||||
will be used. Bandit will prefer settings from the profile over the built
|
||||
in values."""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=help_description,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
if sys.version_info >= (3, 14):
|
||||
parser.suggest_on_error = True
|
||||
parser.color = False
|
||||
|
||||
parser.add_argument(
|
||||
"--show-defaults",
|
||||
dest="show_defaults",
|
||||
action="store_true",
|
||||
help="show the default settings values for each "
|
||||
"plugin but do not output a profile",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--out",
|
||||
dest="output_file",
|
||||
action="store",
|
||||
help="output file to save profile",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tests",
|
||||
dest="tests",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="list of test names to run",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--skip",
|
||||
dest="skips",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="list of test names to skip",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.output_file and not args.show_defaults:
|
||||
parser.print_help()
|
||||
parser.exit(1)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def get_config_settings():
|
||||
"""Get configuration settings."""
|
||||
config = {}
|
||||
for plugin in extension_loader.MANAGER.plugins:
|
||||
fn_name = plugin.name
|
||||
function = plugin.plugin
|
||||
|
||||
# if a function takes config...
|
||||
if hasattr(function, "_takes_config"):
|
||||
fn_module = importlib.import_module(function.__module__)
|
||||
|
||||
# call the config generator if it exists
|
||||
if hasattr(fn_module, "gen_config"):
|
||||
config[fn_name] = fn_module.gen_config(function._takes_config)
|
||||
|
||||
return yaml.safe_dump(config, default_flow_style=False)
|
||||
|
||||
|
||||
def main():
|
||||
"""Config generator to write configuration file."""
|
||||
init_logger()
|
||||
args = parse_args()
|
||||
|
||||
yaml_settings = get_config_settings()
|
||||
|
||||
if args.show_defaults:
|
||||
print(yaml_settings)
|
||||
|
||||
if args.output_file:
|
||||
if os.path.exists(os.path.abspath(args.output_file)):
|
||||
LOG.error("File %s already exists, exiting", args.output_file)
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
with open(args.output_file, "w") as f:
|
||||
skips = args.skips.split(",") if args.skips else []
|
||||
tests = args.tests.split(",") if args.tests else []
|
||||
|
||||
for skip in skips:
|
||||
if not extension_loader.MANAGER.check_id(skip):
|
||||
raise RuntimeError(f"unknown ID in skips: {skip}")
|
||||
|
||||
for test in tests:
|
||||
if not extension_loader.MANAGER.check_id(test):
|
||||
raise RuntimeError(f"unknown ID in tests: {test}")
|
||||
|
||||
tpl = "# {0} : {1}"
|
||||
test_list = [
|
||||
tpl.format(t.plugin._test_id, t.name)
|
||||
for t in extension_loader.MANAGER.plugins
|
||||
]
|
||||
|
||||
others = [
|
||||
tpl.format(k, v["name"])
|
||||
for k, v in (
|
||||
extension_loader.MANAGER.blacklist_by_id.items()
|
||||
)
|
||||
]
|
||||
test_list.extend(others)
|
||||
test_list.sort()
|
||||
|
||||
contents = template.format(
|
||||
cli=" ".join(sys.argv),
|
||||
settings=yaml_settings,
|
||||
test_list="\n".join(test_list),
|
||||
skip="skips: " + str(skips) if skips else "skips:",
|
||||
test="tests: " + str(tests) if tests else "tests:",
|
||||
)
|
||||
f.write(contents)
|
||||
|
||||
except OSError:
|
||||
LOG.error("Unable to open %s for writing", args.output_file)
|
||||
|
||||
except Exception as e:
|
||||
LOG.error("Error: %s", e)
|
||||
|
||||
else:
|
||||
LOG.info("Successfully wrote profile: %s", args.output_file)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
701
Backend/venv/lib/python3.12/site-packages/bandit/cli/main.py
Normal file
701
Backend/venv/lib/python3.12/site-packages/bandit/cli/main.py
Normal file
@@ -0,0 +1,701 @@
|
||||
#
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Bandit is a tool designed to find common security issues in Python code."""
|
||||
import argparse
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import bandit
|
||||
from bandit.core import config as b_config
|
||||
from bandit.core import constants
|
||||
from bandit.core import manager as b_manager
|
||||
from bandit.core import utils
|
||||
|
||||
BASE_CONFIG = "bandit.yaml"
|
||||
LOG = logging.getLogger()
|
||||
|
||||
|
||||
def _init_logger(log_level=logging.INFO, log_format=None):
|
||||
"""Initialize the logger.
|
||||
|
||||
:param debug: Whether to enable debug mode
|
||||
:return: An instantiated logging instance
|
||||
"""
|
||||
LOG.handlers = []
|
||||
|
||||
if not log_format:
|
||||
# default log format
|
||||
log_format_string = constants.log_format_string
|
||||
else:
|
||||
log_format_string = log_format
|
||||
|
||||
logging.captureWarnings(True)
|
||||
|
||||
LOG.setLevel(log_level)
|
||||
handler = logging.StreamHandler(sys.stderr)
|
||||
handler.setFormatter(logging.Formatter(log_format_string))
|
||||
LOG.addHandler(handler)
|
||||
LOG.debug("logging initialized")
|
||||
|
||||
|
||||
def _get_options_from_ini(ini_path, target):
|
||||
"""Return a dictionary of config options or None if we can't load any."""
|
||||
ini_file = None
|
||||
|
||||
if ini_path:
|
||||
ini_file = ini_path
|
||||
else:
|
||||
bandit_files = []
|
||||
|
||||
for t in target:
|
||||
for root, _, filenames in os.walk(t):
|
||||
for filename in fnmatch.filter(filenames, ".bandit"):
|
||||
bandit_files.append(os.path.join(root, filename))
|
||||
|
||||
if len(bandit_files) > 1:
|
||||
LOG.error(
|
||||
"Multiple .bandit files found - scan separately or "
|
||||
"choose one with --ini\n\t%s",
|
||||
", ".join(bandit_files),
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
elif len(bandit_files) == 1:
|
||||
ini_file = bandit_files[0]
|
||||
LOG.info("Found project level .bandit file: %s", bandit_files[0])
|
||||
|
||||
if ini_file:
|
||||
return utils.parse_ini_file(ini_file)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _init_extensions():
|
||||
from bandit.core import extension_loader as ext_loader
|
||||
|
||||
return ext_loader.MANAGER
|
||||
|
||||
|
||||
def _log_option_source(default_val, arg_val, ini_val, option_name):
|
||||
"""It's useful to show the source of each option."""
|
||||
# When default value is not defined, arg_val and ini_val is deterministic
|
||||
if default_val is None:
|
||||
if arg_val:
|
||||
LOG.info("Using command line arg for %s", option_name)
|
||||
return arg_val
|
||||
elif ini_val:
|
||||
LOG.info("Using ini file for %s", option_name)
|
||||
return ini_val
|
||||
else:
|
||||
return None
|
||||
# No value passed to commad line and default value is used
|
||||
elif default_val == arg_val:
|
||||
return ini_val if ini_val else arg_val
|
||||
# Certainly a value is passed to commad line
|
||||
else:
|
||||
return arg_val
|
||||
|
||||
|
||||
def _running_under_virtualenv():
|
||||
if hasattr(sys, "real_prefix"):
|
||||
return True
|
||||
elif sys.prefix != getattr(sys, "base_prefix", sys.prefix):
|
||||
return True
|
||||
|
||||
|
||||
def _get_profile(config, profile_name, config_path):
|
||||
profile = {}
|
||||
if profile_name:
|
||||
profiles = config.get_option("profiles") or {}
|
||||
profile = profiles.get(profile_name)
|
||||
if profile is None:
|
||||
raise utils.ProfileNotFound(config_path, profile_name)
|
||||
LOG.debug("read in legacy profile '%s': %s", profile_name, profile)
|
||||
else:
|
||||
profile["include"] = set(config.get_option("tests") or [])
|
||||
profile["exclude"] = set(config.get_option("skips") or [])
|
||||
return profile
|
||||
|
||||
|
||||
def _log_info(args, profile):
|
||||
inc = ",".join([t for t in profile["include"]]) or "None"
|
||||
exc = ",".join([t for t in profile["exclude"]]) or "None"
|
||||
LOG.info("profile include tests: %s", inc)
|
||||
LOG.info("profile exclude tests: %s", exc)
|
||||
LOG.info("cli include tests: %s", args.tests)
|
||||
LOG.info("cli exclude tests: %s", args.skips)
|
||||
|
||||
|
||||
def main():
|
||||
"""Bandit CLI."""
|
||||
# bring our logging stuff up as early as possible
|
||||
debug = (
|
||||
logging.DEBUG
|
||||
if "-d" in sys.argv or "--debug" in sys.argv
|
||||
else logging.INFO
|
||||
)
|
||||
_init_logger(debug)
|
||||
extension_mgr = _init_extensions()
|
||||
|
||||
baseline_formatters = [
|
||||
f.name
|
||||
for f in filter(
|
||||
lambda x: hasattr(x.plugin, "_accepts_baseline"),
|
||||
extension_mgr.formatters,
|
||||
)
|
||||
]
|
||||
|
||||
# now do normal startup
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Bandit - a Python source code security analyzer",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
if sys.version_info >= (3, 14):
|
||||
parser.suggest_on_error = True
|
||||
parser.color = False
|
||||
|
||||
parser.add_argument(
|
||||
"targets",
|
||||
metavar="targets",
|
||||
type=str,
|
||||
nargs="*",
|
||||
help="source file(s) or directory(s) to be tested",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--recursive",
|
||||
dest="recursive",
|
||||
action="store_true",
|
||||
help="find and process files in subdirectories",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--aggregate",
|
||||
dest="agg_type",
|
||||
action="store",
|
||||
default="file",
|
||||
type=str,
|
||||
choices=["file", "vuln"],
|
||||
help="aggregate output by vulnerability (default) or by filename",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
dest="context_lines",
|
||||
action="store",
|
||||
default=3,
|
||||
type=int,
|
||||
help="maximum number of code lines to output for each issue",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--configfile",
|
||||
dest="config_file",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="optional config file to use for selecting plugins and "
|
||||
"overriding defaults",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--profile",
|
||||
dest="profile",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="profile to use (defaults to executing all tests)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tests",
|
||||
dest="tests",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="comma-separated list of test IDs to run",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--skip",
|
||||
dest="skips",
|
||||
action="store",
|
||||
default=None,
|
||||
type=str,
|
||||
help="comma-separated list of test IDs to skip",
|
||||
)
|
||||
severity_group = parser.add_mutually_exclusive_group(required=False)
|
||||
severity_group.add_argument(
|
||||
"-l",
|
||||
"--level",
|
||||
dest="severity",
|
||||
action="count",
|
||||
default=1,
|
||||
help="report only issues of a given severity level or "
|
||||
"higher (-l for LOW, -ll for MEDIUM, -lll for HIGH)",
|
||||
)
|
||||
severity_group.add_argument(
|
||||
"--severity-level",
|
||||
dest="severity_string",
|
||||
action="store",
|
||||
help="report only issues of a given severity level or higher."
|
||||
' "all" and "low" are likely to produce the same results, but it'
|
||||
" is possible for rules to be undefined which will"
|
||||
' not be listed in "low".',
|
||||
choices=["all", "low", "medium", "high"],
|
||||
)
|
||||
confidence_group = parser.add_mutually_exclusive_group(required=False)
|
||||
confidence_group.add_argument(
|
||||
"-i",
|
||||
"--confidence",
|
||||
dest="confidence",
|
||||
action="count",
|
||||
default=1,
|
||||
help="report only issues of a given confidence level or "
|
||||
"higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)",
|
||||
)
|
||||
confidence_group.add_argument(
|
||||
"--confidence-level",
|
||||
dest="confidence_string",
|
||||
action="store",
|
||||
help="report only issues of a given confidence level or higher."
|
||||
' "all" and "low" are likely to produce the same results, but it'
|
||||
" is possible for rules to be undefined which will"
|
||||
' not be listed in "low".',
|
||||
choices=["all", "low", "medium", "high"],
|
||||
)
|
||||
output_format = (
|
||||
"screen"
|
||||
if (
|
||||
sys.stdout.isatty()
|
||||
and os.getenv("NO_COLOR") is None
|
||||
and os.getenv("TERM") != "dumb"
|
||||
)
|
||||
else "txt"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--format",
|
||||
dest="output_format",
|
||||
action="store",
|
||||
default=output_format,
|
||||
help="specify output format",
|
||||
choices=sorted(extension_mgr.formatter_names),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--msg-template",
|
||||
action="store",
|
||||
default=None,
|
||||
help="specify output message template"
|
||||
" (only usable with --format custom),"
|
||||
" see CUSTOM FORMAT section"
|
||||
" for list of available values",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
dest="output_file",
|
||||
action="store",
|
||||
nargs="?",
|
||||
type=argparse.FileType("w", encoding="utf-8"),
|
||||
default=sys.stdout,
|
||||
help="write report to filename",
|
||||
)
|
||||
group = parser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
dest="verbose",
|
||||
action="store_true",
|
||||
help="output extra information like excluded and included files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--debug",
|
||||
dest="debug",
|
||||
action="store_true",
|
||||
help="turn on debug mode",
|
||||
)
|
||||
group.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
"--silent",
|
||||
dest="quiet",
|
||||
action="store_true",
|
||||
help="only show output in the case of an error",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ignore-nosec",
|
||||
dest="ignore_nosec",
|
||||
action="store_true",
|
||||
help="do not skip lines with # nosec comments",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x",
|
||||
"--exclude",
|
||||
dest="excluded_paths",
|
||||
action="store",
|
||||
default=",".join(constants.EXCLUDE),
|
||||
help="comma-separated list of paths (glob patterns "
|
||||
"supported) to exclude from scan "
|
||||
"(note that these are in addition to the excluded "
|
||||
"paths provided in the config file) (default: "
|
||||
+ ",".join(constants.EXCLUDE)
|
||||
+ ")",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--baseline",
|
||||
dest="baseline",
|
||||
action="store",
|
||||
default=None,
|
||||
help="path of a baseline report to compare against "
|
||||
"(only JSON-formatted files are accepted)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ini",
|
||||
dest="ini_path",
|
||||
action="store",
|
||||
default=None,
|
||||
help="path to a .bandit file that supplies command line arguments",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--exit-zero",
|
||||
action="store_true",
|
||||
dest="exit_zero",
|
||||
default=False,
|
||||
help="exit with 0, " "even with results found",
|
||||
)
|
||||
python_ver = sys.version.replace("\n", "")
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"%(prog)s {bandit.__version__}\n"
|
||||
f" python version = {python_ver}",
|
||||
)
|
||||
|
||||
parser.set_defaults(debug=False)
|
||||
parser.set_defaults(verbose=False)
|
||||
parser.set_defaults(quiet=False)
|
||||
parser.set_defaults(ignore_nosec=False)
|
||||
|
||||
plugin_info = [
|
||||
f"{a[0]}\t{a[1].name}" for a in extension_mgr.plugins_by_id.items()
|
||||
]
|
||||
blacklist_info = []
|
||||
for a in extension_mgr.blacklist.items():
|
||||
for b in a[1]:
|
||||
blacklist_info.append(f"{b['id']}\t{b['name']}")
|
||||
|
||||
plugin_list = "\n\t".join(sorted(set(plugin_info + blacklist_info)))
|
||||
dedent_text = textwrap.dedent(
|
||||
"""
|
||||
CUSTOM FORMATTING
|
||||
-----------------
|
||||
|
||||
Available tags:
|
||||
|
||||
{abspath}, {relpath}, {line}, {col}, {test_id},
|
||||
{severity}, {msg}, {confidence}, {range}
|
||||
|
||||
Example usage:
|
||||
|
||||
Default template:
|
||||
bandit -r examples/ --format custom --msg-template \\
|
||||
"{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}"
|
||||
|
||||
Provides same output as:
|
||||
bandit -r examples/ --format custom
|
||||
|
||||
Tags can also be formatted in python string.format() style:
|
||||
bandit -r examples/ --format custom --msg-template \\
|
||||
"{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}"
|
||||
|
||||
See python documentation for more information about formatting style:
|
||||
https://docs.python.org/3/library/string.html
|
||||
|
||||
The following tests were discovered and loaded:
|
||||
-----------------------------------------------
|
||||
"""
|
||||
)
|
||||
parser.epilog = dedent_text + f"\t{plugin_list}"
|
||||
|
||||
# setup work - parse arguments, and initialize BanditManager
|
||||
args = parser.parse_args()
|
||||
# Check if `--msg-template` is not present without custom formatter
|
||||
if args.output_format != "custom" and args.msg_template is not None:
|
||||
parser.error("--msg-template can only be used with --format=custom")
|
||||
|
||||
# Check if confidence or severity level have been specified with strings
|
||||
if args.severity_string is not None:
|
||||
if args.severity_string == "all":
|
||||
args.severity = 1
|
||||
elif args.severity_string == "low":
|
||||
args.severity = 2
|
||||
elif args.severity_string == "medium":
|
||||
args.severity = 3
|
||||
elif args.severity_string == "high":
|
||||
args.severity = 4
|
||||
# Other strings will be blocked by argparse
|
||||
|
||||
if args.confidence_string is not None:
|
||||
if args.confidence_string == "all":
|
||||
args.confidence = 1
|
||||
elif args.confidence_string == "low":
|
||||
args.confidence = 2
|
||||
elif args.confidence_string == "medium":
|
||||
args.confidence = 3
|
||||
elif args.confidence_string == "high":
|
||||
args.confidence = 4
|
||||
# Other strings will be blocked by argparse
|
||||
|
||||
# Handle .bandit files in projects to pass cmdline args from file
|
||||
ini_options = _get_options_from_ini(args.ini_path, args.targets)
|
||||
if ini_options:
|
||||
# prefer command line, then ini file
|
||||
args.config_file = _log_option_source(
|
||||
parser.get_default("configfile"),
|
||||
args.config_file,
|
||||
ini_options.get("configfile"),
|
||||
"config file",
|
||||
)
|
||||
|
||||
args.excluded_paths = _log_option_source(
|
||||
parser.get_default("excluded_paths"),
|
||||
args.excluded_paths,
|
||||
ini_options.get("exclude"),
|
||||
"excluded paths",
|
||||
)
|
||||
|
||||
args.skips = _log_option_source(
|
||||
parser.get_default("skips"),
|
||||
args.skips,
|
||||
ini_options.get("skips"),
|
||||
"skipped tests",
|
||||
)
|
||||
|
||||
args.tests = _log_option_source(
|
||||
parser.get_default("tests"),
|
||||
args.tests,
|
||||
ini_options.get("tests"),
|
||||
"selected tests",
|
||||
)
|
||||
|
||||
ini_targets = ini_options.get("targets")
|
||||
if ini_targets:
|
||||
ini_targets = ini_targets.split(",")
|
||||
|
||||
args.targets = _log_option_source(
|
||||
parser.get_default("targets"),
|
||||
args.targets,
|
||||
ini_targets,
|
||||
"selected targets",
|
||||
)
|
||||
|
||||
# TODO(tmcpeak): any other useful options to pass from .bandit?
|
||||
|
||||
args.recursive = _log_option_source(
|
||||
parser.get_default("recursive"),
|
||||
args.recursive,
|
||||
ini_options.get("recursive"),
|
||||
"recursive scan",
|
||||
)
|
||||
|
||||
args.agg_type = _log_option_source(
|
||||
parser.get_default("agg_type"),
|
||||
args.agg_type,
|
||||
ini_options.get("aggregate"),
|
||||
"aggregate output type",
|
||||
)
|
||||
|
||||
args.context_lines = _log_option_source(
|
||||
parser.get_default("context_lines"),
|
||||
args.context_lines,
|
||||
int(ini_options.get("number") or 0) or None,
|
||||
"max code lines output for issue",
|
||||
)
|
||||
|
||||
args.profile = _log_option_source(
|
||||
parser.get_default("profile"),
|
||||
args.profile,
|
||||
ini_options.get("profile"),
|
||||
"profile",
|
||||
)
|
||||
|
||||
args.severity = _log_option_source(
|
||||
parser.get_default("severity"),
|
||||
args.severity,
|
||||
ini_options.get("level"),
|
||||
"severity level",
|
||||
)
|
||||
|
||||
args.confidence = _log_option_source(
|
||||
parser.get_default("confidence"),
|
||||
args.confidence,
|
||||
ini_options.get("confidence"),
|
||||
"confidence level",
|
||||
)
|
||||
|
||||
args.output_format = _log_option_source(
|
||||
parser.get_default("output_format"),
|
||||
args.output_format,
|
||||
ini_options.get("format"),
|
||||
"output format",
|
||||
)
|
||||
|
||||
args.msg_template = _log_option_source(
|
||||
parser.get_default("msg_template"),
|
||||
args.msg_template,
|
||||
ini_options.get("msg-template"),
|
||||
"output message template",
|
||||
)
|
||||
|
||||
args.output_file = _log_option_source(
|
||||
parser.get_default("output_file"),
|
||||
args.output_file,
|
||||
ini_options.get("output"),
|
||||
"output file",
|
||||
)
|
||||
|
||||
args.verbose = _log_option_source(
|
||||
parser.get_default("verbose"),
|
||||
args.verbose,
|
||||
ini_options.get("verbose"),
|
||||
"output extra information",
|
||||
)
|
||||
|
||||
args.debug = _log_option_source(
|
||||
parser.get_default("debug"),
|
||||
args.debug,
|
||||
ini_options.get("debug"),
|
||||
"debug mode",
|
||||
)
|
||||
|
||||
args.quiet = _log_option_source(
|
||||
parser.get_default("quiet"),
|
||||
args.quiet,
|
||||
ini_options.get("quiet"),
|
||||
"silent mode",
|
||||
)
|
||||
|
||||
args.ignore_nosec = _log_option_source(
|
||||
parser.get_default("ignore_nosec"),
|
||||
args.ignore_nosec,
|
||||
ini_options.get("ignore-nosec"),
|
||||
"do not skip lines with # nosec",
|
||||
)
|
||||
|
||||
args.baseline = _log_option_source(
|
||||
parser.get_default("baseline"),
|
||||
args.baseline,
|
||||
ini_options.get("baseline"),
|
||||
"path of a baseline report",
|
||||
)
|
||||
|
||||
try:
|
||||
b_conf = b_config.BanditConfig(config_file=args.config_file)
|
||||
except utils.ConfigError as e:
|
||||
LOG.error(e)
|
||||
sys.exit(2)
|
||||
|
||||
if not args.targets:
|
||||
parser.print_usage()
|
||||
sys.exit(2)
|
||||
|
||||
# if the log format string was set in the options, reinitialize
|
||||
if b_conf.get_option("log_format"):
|
||||
log_format = b_conf.get_option("log_format")
|
||||
_init_logger(log_level=logging.DEBUG, log_format=log_format)
|
||||
|
||||
if args.quiet:
|
||||
_init_logger(log_level=logging.WARN)
|
||||
|
||||
try:
|
||||
profile = _get_profile(b_conf, args.profile, args.config_file)
|
||||
_log_info(args, profile)
|
||||
|
||||
profile["include"].update(args.tests.split(",") if args.tests else [])
|
||||
profile["exclude"].update(args.skips.split(",") if args.skips else [])
|
||||
extension_mgr.validate_profile(profile)
|
||||
|
||||
except (utils.ProfileNotFound, ValueError) as e:
|
||||
LOG.error(e)
|
||||
sys.exit(2)
|
||||
|
||||
b_mgr = b_manager.BanditManager(
|
||||
b_conf,
|
||||
args.agg_type,
|
||||
args.debug,
|
||||
profile=profile,
|
||||
verbose=args.verbose,
|
||||
quiet=args.quiet,
|
||||
ignore_nosec=args.ignore_nosec,
|
||||
)
|
||||
|
||||
if args.baseline is not None:
|
||||
try:
|
||||
with open(args.baseline) as bl:
|
||||
data = bl.read()
|
||||
b_mgr.populate_baseline(data)
|
||||
except OSError:
|
||||
LOG.warning("Could not open baseline report: %s", args.baseline)
|
||||
sys.exit(2)
|
||||
|
||||
if args.output_format not in baseline_formatters:
|
||||
LOG.warning(
|
||||
"Baseline must be used with one of the following "
|
||||
"formats: " + str(baseline_formatters)
|
||||
)
|
||||
sys.exit(2)
|
||||
|
||||
if args.output_format != "json":
|
||||
if args.config_file:
|
||||
LOG.info("using config: %s", args.config_file)
|
||||
|
||||
LOG.info(
|
||||
"running on Python %d.%d.%d",
|
||||
sys.version_info.major,
|
||||
sys.version_info.minor,
|
||||
sys.version_info.micro,
|
||||
)
|
||||
|
||||
# initiate file discovery step within Bandit Manager
|
||||
b_mgr.discover_files(args.targets, args.recursive, args.excluded_paths)
|
||||
|
||||
if not b_mgr.b_ts.tests:
|
||||
LOG.error("No tests would be run, please check the profile.")
|
||||
sys.exit(2)
|
||||
|
||||
# initiate execution of tests within Bandit Manager
|
||||
b_mgr.run_tests()
|
||||
LOG.debug(b_mgr.b_ma)
|
||||
LOG.debug(b_mgr.metrics)
|
||||
|
||||
# trigger output of results by Bandit Manager
|
||||
sev_level = constants.RANKING[args.severity - 1]
|
||||
conf_level = constants.RANKING[args.confidence - 1]
|
||||
b_mgr.output_results(
|
||||
args.context_lines,
|
||||
sev_level,
|
||||
conf_level,
|
||||
args.output_file,
|
||||
args.output_format,
|
||||
args.msg_template,
|
||||
)
|
||||
|
||||
if (
|
||||
b_mgr.results_count(sev_filter=sev_level, conf_filter=conf_level) > 0
|
||||
and not args.exit_zero
|
||||
):
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user