1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2020 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing a check for SQL injection. 8""" 9 10# 11# This is a modified version of the one found in the bandit package. 12# 13# Original Copyright 2014 Hewlett-Packard Development Company, L.P. 14# 15# SPDX-License-Identifier: Apache-2.0 16# 17 18import ast 19import re 20 21from Security import SecurityUtils 22 23 24def getChecks(): 25 """ 26 Public method to get a dictionary with checks handled by this module. 27 28 @return dictionary containing checker lists containing checker function and 29 list of codes 30 @rtype dict 31 """ 32 return { 33 "Str": [ 34 (checkHardcodedSqlExpressions, ("S608",)), 35 ], 36 } 37 38 39SIMPLE_SQL_RE = re.compile( 40 r'(select\s.*from\s|' 41 r'delete\s+from\s|' 42 r'insert\s+into\s.*values\s|' 43 r'update\s.*set\s)', 44 re.IGNORECASE | re.DOTALL, 45) 46 47 48def _checkString(data): 49 """ 50 Function to check a given string against the list of search patterns. 51 52 @param data string data to be checked 53 @type str 54 @return flag indicating a match 55 @rtype bool 56 """ 57 return SIMPLE_SQL_RE.search(data) is not None 58 59 60def _evaluateAst(node): 61 """ 62 Function to analyze the given ast node. 63 64 @param node ast node to be analyzed 65 @type ast.Str 66 @return tuple containing a flag indicating an execute call and 67 the resulting statement 68 @rtype tuple of (bool, str) 69 """ 70 wrapper = None 71 statement = '' 72 73 if isinstance(node._securityParent, ast.BinOp): 74 out = SecurityUtils.concatString(node, node._securityParent) 75 wrapper = out[0]._securityParent 76 statement = out[1] 77 elif ( 78 isinstance(node._securityParent, ast.Attribute) and 79 node._securityParent.attr == 'format' 80 ): 81 statement = node.s 82 # Hierarchy for "".format() is Wrapper -> Call -> Attribute -> Str 83 wrapper = node._securityParent._securityParent._securityParent 84 elif ( 85 hasattr(ast, 'JoinedStr') and 86 isinstance(node._securityParent, ast.JoinedStr) 87 ): 88 statement = node.s 89 wrapper = node._securityParent._securityParent 90 91 if isinstance(wrapper, ast.Call): # wrapped in "execute" call? 92 names = ['execute', 'executemany'] 93 name = SecurityUtils.getCalledName(wrapper) 94 return (name in names, statement) 95 else: 96 return (False, statement) 97 98 99def checkHardcodedSqlExpressions(reportError, context, config): 100 """ 101 Function to check for SQL injection. 102 103 @param reportError function to be used to report errors 104 @type func 105 @param context security context object 106 @type SecurityContext 107 @param config dictionary with configuration data 108 @type dict 109 """ 110 val = _evaluateAst(context.node) 111 if _checkString(val[1]): 112 reportError( 113 context.node.lineno - 1, 114 context.node.col_offset, 115 "S608", 116 "M", 117 "M" if val[0] else "L", 118 ) 119