1# -*- coding:utf-8 -*-
2#
3# Copyright 2014 Hewlett-Packard Development Company, L.P.
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import copy
8import logging
9import warnings
10
11from bandit.core import constants
12from bandit.core import context as b_context
13from bandit.core import utils
14
15warnings.formatwarning = utils.warnings_formatter
16LOG = logging.getLogger(__name__)
17
18
19class BanditTester(object):
20    def __init__(self, testset, debug, nosec_lines):
21        self.results = []
22        self.testset = testset
23        self.last_result = None
24        self.debug = debug
25        self.nosec_lines = nosec_lines
26
27    def run_tests(self, raw_context, checktype):
28        '''Runs all tests for a certain type of check, for example
29
30        Runs all tests for a certain type of check, for example 'functions'
31        store results in results.
32
33        :param raw_context: Raw context dictionary
34        :param checktype: The type of checks to run
35        :param nosec_lines: Lines which should be skipped because of nosec
36        :return: a score based on the number and type of test results
37        '''
38
39        scores = {
40            'SEVERITY': [0] * len(constants.RANKING),
41            'CONFIDENCE': [0] * len(constants.RANKING)
42        }
43
44        tests = self.testset.get_tests(checktype)
45        for test in tests:
46            name = test.__name__
47            # execute test with the an instance of the context class
48            temp_context = copy.copy(raw_context)
49            context = b_context.Context(temp_context)
50            try:
51                if hasattr(test, '_config'):
52                    result = test(context, test._config)
53                else:
54                    result = test(context)
55
56                # if we have a result, record it and update scores
57                if (result is not None and
58                        result.lineno not in self.nosec_lines and
59                        temp_context['lineno'] not in self.nosec_lines):
60
61                    if isinstance(temp_context['filename'], bytes):
62                        result.fname = temp_context['filename'].decode('utf-8')
63                    else:
64                        result.fname = temp_context['filename']
65
66                    if result.lineno is None:
67                        result.lineno = temp_context['lineno']
68                    result.linerange = temp_context['linerange']
69                    result.col_offset = temp_context['col_offset']
70                    result.test = name
71                    if result.test_id == "":
72                        result.test_id = test._test_id
73
74                    self.results.append(result)
75
76                    LOG.debug("Issue identified by %s: %s", name, result)
77                    sev = constants.RANKING.index(result.severity)
78                    val = constants.RANKING_VALUES[result.severity]
79                    scores['SEVERITY'][sev] += val
80                    con = constants.RANKING.index(result.confidence)
81                    val = constants.RANKING_VALUES[result.confidence]
82                    scores['CONFIDENCE'][con] += val
83
84            except Exception as e:
85                self.report_error(name, context, e)
86                if self.debug:
87                    raise
88        LOG.debug("Returning scores: %s", scores)
89        return scores
90
91    @staticmethod
92    def report_error(test, context, error):
93        what = "Bandit internal error running: "
94        what += "%s " % test
95        what += "on file %s at line %i: " % (
96            context._context['filename'],
97            context._context['lineno']
98        )
99        what += str(error)
100        import traceback
101        what += traceback.format_exc()
102        LOG.error(what)
103