File: config_generator.py

package info (click to toggle)
bandit 1.6.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 1,844 kB
  • sloc: python: 10,049; makefile: 22; sh: 14
file content (186 lines) | stat: -rw-r--r-- 6,323 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# Copyright 2015 Red Hat Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function

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():
    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():
    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)

    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():
    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():
    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('unknown ID in skips: %s' % skip)

                for test in tests:
                    if not extension_loader.MANAGER.check_id(test):
                        raise RuntimeError('unknown ID in tests: %s' % 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 IOError:
            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())