|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing checks for blacklisted methods and functions. |
|
8 """ |
|
9 |
|
10 import ast |
|
11 import fnmatch |
|
12 |
|
13 |
|
14 _blacklists = { |
|
15 'S301': ([ |
|
16 'pickle.loads', |
|
17 'pickle.load', |
|
18 'pickle.Unpickler', |
|
19 'cPickle.loads', |
|
20 'cPickle.load', |
|
21 'cPickle.Unpickler', |
|
22 'dill.loads', |
|
23 'dill.load', |
|
24 'dill.Unpickler', |
|
25 'shelve.open', |
|
26 'shelve.DbfilenameShelf'], |
|
27 "M"), |
|
28 'S302': ([ |
|
29 'marshal.load', |
|
30 'marshal.loads'], |
|
31 "M"), |
|
32 'S303': ([ |
|
33 'hashlib.md5', |
|
34 'hashlib.sha1', |
|
35 'Crypto.Hash.MD2.new', |
|
36 'Crypto.Hash.MD4.new', |
|
37 'Crypto.Hash.MD5.new', |
|
38 'Crypto.Hash.SHA.new', |
|
39 'Cryptodome.Hash.MD2.new', |
|
40 'Cryptodome.Hash.MD4.new', |
|
41 'Cryptodome.Hash.MD5.new', |
|
42 'Cryptodome.Hash.SHA.new', |
|
43 'cryptography.hazmat.primitives.hashes.MD5', |
|
44 'cryptography.hazmat.primitives.hashes.SHA1'], |
|
45 "M"), |
|
46 } |
|
47 |
|
48 |
|
49 def getChecks(): |
|
50 """ |
|
51 Public method to get a dictionary with checks handled by this module. |
|
52 |
|
53 @return dictionary containing checker lists containing checker function and |
|
54 list of codes |
|
55 @rtype dict |
|
56 """ |
|
57 # TODO: should be list of tuples |
|
58 return { |
|
59 "Call": (checkBlacklist, tuple(_blacklists.keys())), |
|
60 } |
|
61 |
|
62 |
|
63 def checkBlacklist(reportError, context, config): |
|
64 nodeType = context.node.__class__.__name__ |
|
65 |
|
66 if nodeType == 'Call': |
|
67 func = context.node.func |
|
68 if isinstance(func, ast.Name) and func.id == '__import__': |
|
69 if len(context.node.args): |
|
70 if isinstance(context.node.args[0], ast.Str): |
|
71 name = context.node.args[0].s |
|
72 else: |
|
73 name = "UNKNOWN" |
|
74 else: |
|
75 name = "" # handle '__import__()' |
|
76 else: |
|
77 name = context.callFunctionNameQual |
|
78 # In the case the Call is an importlib.import, treat the first |
|
79 # argument name as an actual import module name. |
|
80 # Will produce None if argument is not a literal or identifier. |
|
81 if name in ["importlib.import_module", "importlib.__import__"]: |
|
82 name = context.call_args[0] |
|
83 |
|
84 for code in _blacklists: |
|
85 qualnames, severity = _blacklists[code] |
|
86 for qualname in qualnames: |
|
87 if name and fnmatch.fnmatch(name, qualname): |
|
88 return reportError( |
|
89 context.node.lineno, |
|
90 context.node.col_offset, |
|
91 code, |
|
92 "M", |
|
93 "H" |
|
94 ) |
|
95 |
|
96 return None |