|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2020 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing checks for blacklisted methods and functions. |
|
8 """ |
|
9 |
|
10 # |
|
11 # This is a modified version of the one found in the bandit package. |
|
12 # |
|
13 # Original Copyright 2016 Hewlett-Packard Development Company, L.P. |
|
14 # |
|
15 # SPDX-License-Identifier: Apache-2.0 |
|
16 # |
|
17 |
|
18 import ast |
|
19 import fnmatch |
|
20 |
|
21 import AstUtilities |
|
22 |
|
23 _blacklists = { |
|
24 'S301': ([ |
|
25 'pickle.loads', |
|
26 'pickle.load', |
|
27 'pickle.Unpickler', |
|
28 'cPickle.loads', |
|
29 'cPickle.load', |
|
30 'cPickle.Unpickler', |
|
31 'dill.loads', |
|
32 'dill.load', |
|
33 'dill.Unpickler', |
|
34 'shelve.open', |
|
35 'shelve.DbfilenameShelf'], |
|
36 "M"), |
|
37 'S302': ([ |
|
38 'marshal.load', |
|
39 'marshal.loads'], |
|
40 "M"), |
|
41 'S303': ([ |
|
42 'hashlib.md5', |
|
43 'hashlib.sha1', |
|
44 'Crypto.Hash.MD2.new', |
|
45 'Crypto.Hash.MD4.new', |
|
46 'Crypto.Hash.MD5.new', |
|
47 'Crypto.Hash.SHA.new', |
|
48 'Cryptodome.Hash.MD2.new', |
|
49 'Cryptodome.Hash.MD4.new', |
|
50 'Cryptodome.Hash.MD5.new', |
|
51 'Cryptodome.Hash.SHA.new', |
|
52 'cryptography.hazmat.primitives.hashes.MD5', |
|
53 'cryptography.hazmat.primitives.hashes.SHA1'], |
|
54 "M"), |
|
55 'S304': ([ |
|
56 'Crypto.Cipher.ARC2.new', |
|
57 'Crypto.Cipher.ARC4.new', |
|
58 'Crypto.Cipher.Blowfish.new', |
|
59 'Crypto.Cipher.DES.new', |
|
60 'Crypto.Cipher.XOR.new', |
|
61 'Cryptodome.Cipher.ARC2.new', |
|
62 'Cryptodome.Cipher.ARC4.new', |
|
63 'Cryptodome.Cipher.Blowfish.new', |
|
64 'Cryptodome.Cipher.DES.new', |
|
65 'Cryptodome.Cipher.XOR.new', |
|
66 'cryptography.hazmat.primitives.ciphers.algorithms.ARC4', |
|
67 'cryptography.hazmat.primitives.ciphers.algorithms.Blowfish', |
|
68 'cryptography.hazmat.primitives.ciphers.algorithms.IDEA'], |
|
69 "H"), |
|
70 'S305': ([ |
|
71 'cryptography.hazmat.primitives.ciphers.modes.ECB'], |
|
72 "M"), |
|
73 'S306': ([ |
|
74 'tempfile.mktemp'], |
|
75 "M"), |
|
76 'S307': ([ |
|
77 'eval'], |
|
78 "M"), |
|
79 'S308': ([ |
|
80 'django.utils.safestring.mark_safe'], |
|
81 "M"), |
|
82 'S309': ([ |
|
83 'httplib.HTTPSConnection', |
|
84 'http.client.HTTPSConnection', |
|
85 'six.moves.http_client.HTTPSConnection'], |
|
86 "M"), |
|
87 'S310': ([ |
|
88 'urllib.urlopen', |
|
89 'urllib.request.urlopen', |
|
90 'urllib.urlretrieve', |
|
91 'urllib.request.urlretrieve', |
|
92 'urllib.URLopener', |
|
93 'urllib.request.URLopener', |
|
94 'urllib.FancyURLopener', |
|
95 'urllib.request.FancyURLopener', |
|
96 'urllib2.urlopen', |
|
97 'urllib2.Request', |
|
98 'six.moves.urllib.request.urlopen', |
|
99 'six.moves.urllib.request.urlretrieve', |
|
100 'six.moves.urllib.request.URLopener', |
|
101 'six.moves.urllib.request.FancyURLopener'], |
|
102 ""), |
|
103 'S311': ([ |
|
104 'random.random', |
|
105 'random.randrange', |
|
106 'random.randint', |
|
107 'random.choice', |
|
108 'random.uniform', |
|
109 'random.triangular'], |
|
110 "L"), |
|
111 'S312': ([ |
|
112 'telnetlib.*'], |
|
113 "H"), |
|
114 'S313': ([ |
|
115 'xml.etree.cElementTree.parse', |
|
116 'xml.etree.cElementTree.iterparse', |
|
117 'xml.etree.cElementTree.fromstring', |
|
118 'xml.etree.cElementTree.XMLParser'], |
|
119 "M"), |
|
120 'S314': ([ |
|
121 'xml.etree.ElementTree.parse', |
|
122 'xml.etree.ElementTree.iterparse', |
|
123 'xml.etree.ElementTree.fromstring', |
|
124 'xml.etree.ElementTree.XMLParser'], |
|
125 "M"), |
|
126 'S315': ([ |
|
127 'xml.sax.expatreader.create_parser'], |
|
128 "M"), |
|
129 'S316': ([ |
|
130 'xml.dom.expatbuilder.parse', |
|
131 'xml.dom.expatbuilder.parseString'], |
|
132 "M"), |
|
133 'S317': ([ |
|
134 'xml.sax.parse', |
|
135 'xml.sax.parseString', |
|
136 'xml.sax.make_parser'], |
|
137 "M"), |
|
138 'S318': ([ |
|
139 'xml.dom.minidom.parse', |
|
140 'xml.dom.minidom.parseString'], |
|
141 "M"), |
|
142 'S319': ([ |
|
143 'xml.dom.pulldom.parse', |
|
144 'xml.dom.pulldom.parseString'], |
|
145 "M"), |
|
146 'S320': ([ |
|
147 'lxml.etree.parse', |
|
148 'lxml.etree.fromstring', |
|
149 'lxml.etree.RestrictedElement', |
|
150 'lxml.etree.GlobalParserTLS', |
|
151 'lxml.etree.getDefaultParser', |
|
152 'lxml.etree.check_docinfo'], |
|
153 "M"), |
|
154 'S321': ([ |
|
155 'ftplib.*'], |
|
156 "H"), |
|
157 'S322': ([ |
|
158 'input'], |
|
159 "H"), |
|
160 'S323': ([ |
|
161 'ssl._create_unverified_context'], |
|
162 "M"), |
|
163 'S324': ([ |
|
164 'os.tempnam', |
|
165 'os.tmpnam'], |
|
166 "M"), |
|
167 } |
|
168 |
|
169 |
|
170 def getChecks(): |
|
171 """ |
|
172 Public method to get a dictionary with checks handled by this module. |
|
173 |
|
174 @return dictionary containing checker lists containing checker function and |
|
175 list of codes |
|
176 @rtype dict |
|
177 """ |
|
178 return { |
|
179 "Call": [ |
|
180 (checkBlacklist, tuple(_blacklists.keys())), |
|
181 ], |
|
182 } |
|
183 |
|
184 |
|
185 def checkBlacklist(reportError, context, config): |
|
186 """ |
|
187 Function to check for blacklisted method calls. |
|
188 |
|
189 @param reportError function to be used to report errors |
|
190 @type func |
|
191 @param context security context object |
|
192 @type SecurityContext |
|
193 @param config dictionary with configuration data |
|
194 @type dict |
|
195 """ |
|
196 nodeType = context.node.__class__.__name__ |
|
197 |
|
198 if nodeType == 'Call': |
|
199 func = context.node.func |
|
200 if isinstance(func, ast.Name) and func.id == '__import__': |
|
201 if len(context.node.args): |
|
202 if AstUtilities.isString(context.node.args[0]): |
|
203 name = context.node.args[0].s |
|
204 else: |
|
205 name = "UNKNOWN" |
|
206 else: |
|
207 name = "" # handle '__import__()' |
|
208 else: |
|
209 name = context.callFunctionNameQual |
|
210 # In the case the Call is an importlib.import, treat the first |
|
211 # argument name as an actual import module name. |
|
212 # Will produce None if argument is not a literal or identifier. |
|
213 if name in ["importlib.import_module", "importlib.__import__"]: |
|
214 name = context.callArgs[0] |
|
215 |
|
216 for code in _blacklists: |
|
217 qualnames, severity = _blacklists[code] |
|
218 for qualname in qualnames: |
|
219 if name and fnmatch.fnmatch(name, qualname): |
|
220 reportError( |
|
221 context.node.lineno - 1, |
|
222 context.node.col_offset, |
|
223 code, |
|
224 severity, |
|
225 "H", |
|
226 name |
|
227 ) |