src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Security/SecurityChecker.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the security checker.
8 """
9
10 import copy
11 import collections
12
13 from . import Checks
14 from .SecurityNodeVisitor import SecurityNodeVisitor
15
16
17 class SecurityChecker:
18 """
19 Class implementing a checker for security issues.
20 """
21 Codes = [
22 # assert used
23 "S101",
24
25 # exec used
26 "S102",
27
28 # bad file permissions
29 "S103",
30
31 # bind to all interfaces
32 "S104",
33
34 # hardcoded passwords
35 "S105", "S106", "S107"
36
37 # hardcoded tmp directory
38 "S108",
39
40 # try-except
41 "S110", "S112",
42
43 # flask app
44 "S201",
45
46 # insecure function calls (blacklisted)
47 "S301", "S302", "S303", "S304", "S305", "S306", "S307", "S308", "S309",
48 "S310", "S311", "S312", "S313", "S314", "S315", "S316", "S317", "S318",
49 "S319", "S320", "S321", "S322", "S323", "S324",
50
51 # hashlib.new
52 "S331",
53
54 # insecure imports (blacklisted)
55 "S401", "S402", "S403", "S404", "S405", "S406", "S407", "S408", "S409",
56 "S410", "S411", "S412", "S413",
57
58 # insecure certificate usage
59 "S501",
60
61 # insecure SSL/TLS protocol version
62 "S502", "S503", "S504",
63
64 # weak cryptographic keys
65 "S505",
66
67 # YAML load
68 "S506",
69
70 # SSH host key verification
71 "S507",
72
73 # Shell injection
74 "S601", "S602", "S603", "S604", "S605", "S606", "S607",
75
76 # SQL injection
77 "S608",
78
79 # Wildcard injection
80 "S609",
81
82 # Django SQL injection
83 "S610", "S611",
84
85 # Jinja2 templates
86 "S701",
87
88 # Mako templates
89 "S702",
90
91 # Django XSS vulnerability
92 "S703",
93
94 # hardcoded AWS passwords
95 "S801", "S802",
96 ]
97
98 def __init__(self, source, filename, tree, select, ignore, expected,
99 repeat, args):
100 """
101 Constructor
102
103 @param source source code to be checked
104 @type list of str
105 @param filename name of the source file
106 @type str
107 @param tree AST tree of the source code
108 @type ast.Module
109 @param select list of selected codes
110 @type list of str
111 @param ignore list of codes to be ignored
112 @type list of str
113 @param expected list of expected codes
114 @type list of str
115 @param repeat flag indicating to report each occurrence of a code
116 @type bool
117 @param args dictionary of arguments for the security checks
118 @type dict
119 """
120 self.__select = tuple(select)
121 self.__ignore = ('',) if select else tuple(ignore)
122 self.__expected = expected[:]
123 self.__repeat = repeat
124 self.__filename = filename
125 self.__source = source[:]
126 self.__tree = copy.deepcopy(tree)
127 self.__args = args
128
129 # statistics counters
130 self.counters = {}
131
132 # collection of detected errors
133 self.errors = []
134
135 checkersWithCodes = Checks.generateCheckersDict()
136
137 self.__checkers = collections.defaultdict(list)
138 for checkType, checkersList in checkersWithCodes.items():
139 for checker, codes in checkersList:
140 if any(not (code and self.__ignoreCode(code))
141 for code in codes):
142 self.__checkers[checkType].append(checker)
143
144 def __ignoreCode(self, code):
145 """
146 Private method to check if the message code should be ignored.
147
148 @param code message code to check for
149 @type str
150 @return flag indicating to ignore the given code
151 @rtype bool
152 """
153 return (code.startswith(self.__ignore) and
154 not code.startswith(self.__select))
155
156 def reportError(self, lineNumber, offset, code, severity, confidence,
157 *args):
158 """
159 Public method to record an issue.
160
161 @param lineNumber line number of the issue
162 @type int
163 @param offset position within line of the issue
164 @type int
165 @param code message code
166 @type str
167 @param severity severity code (H = high, M = medium, L = low,
168 U = undefined)
169 @type str
170 @param confidence confidence code (H = high, M = medium, L = low,
171 U = undefined)
172 @type str
173 @param args arguments for the message
174 @type list
175 """
176 if self.__ignoreCode(code):
177 return
178
179 if code in self.counters:
180 self.counters[code] += 1
181 else:
182 self.counters[code] = 1
183
184 # Don't care about expected codes
185 if code in self.__expected:
186 return
187
188 if code and (self.counters[code] == 1 or self.__repeat):
189 # record the issue with one based line number
190 self.errors.append({
191 "file": self.__filename,
192 "line": lineNumber + 1,
193 "offset": offset,
194 "code": code,
195 "args": args,
196 "severity": severity,
197 "confidence": confidence,
198 })
199
200 def getConfig(self):
201 """
202 Public method to get the configuration dictionary.
203
204 @return dictionary containing the configuration
205 @rtype dict
206 """
207 return self.__args
208
209 def run(self):
210 """
211 Public method to check the given source against security related
212 conditions.
213 """
214 if not self.__filename:
215 # don't do anything, if essential data is missing
216 return
217
218 if not self.__checkers:
219 # don't do anything, if no codes were selected
220 return
221
222 securityNodeVisitor = SecurityNodeVisitor(
223 self, self.__checkers, self.__filename)
224 securityNodeVisitor.generic_visit(self.__tree)

eric ide

mercurial