1 19 20 package edu.umd.cs.findbugs.detect; 21 22 import java.util.Iterator ; 23 24 import org.apache.bcel.classfile.JavaClass; 25 import org.apache.bcel.classfile.Method; 26 import org.apache.bcel.generic.ConstantPoolGen; 27 import org.apache.bcel.generic.GETSTATIC; 28 import org.apache.bcel.generic.INVOKEINTERFACE; 29 import org.apache.bcel.generic.INVOKEVIRTUAL; 30 import org.apache.bcel.generic.Instruction; 31 import org.apache.bcel.generic.InstructionHandle; 32 import org.apache.bcel.generic.LDC; 33 import org.apache.bcel.generic.MethodGen; 34 35 import edu.umd.cs.findbugs.BugInstance; 36 import edu.umd.cs.findbugs.BugReporter; 37 import edu.umd.cs.findbugs.Detector; 38 import edu.umd.cs.findbugs.ba.CFG; 39 import edu.umd.cs.findbugs.ba.CFGBuilderException; 40 import edu.umd.cs.findbugs.ba.ClassContext; 41 import edu.umd.cs.findbugs.ba.DataflowAnalysisException; 42 import edu.umd.cs.findbugs.ba.Location; 43 import edu.umd.cs.findbugs.ba.constant.Constant; 44 import edu.umd.cs.findbugs.ba.constant.ConstantDataflow; 45 import edu.umd.cs.findbugs.ba.constant.ConstantFrame; 46 47 53 public class FindSqlInjection implements Detector { 54 55 BugReporter bugReporter; 56 57 public FindSqlInjection(BugReporter bugReporter) { 58 this.bugReporter = bugReporter; 59 } 60 61 private boolean prescreen(ClassContext classContext, Method method) { 62 return true; 63 } 64 65 public void visitClassContext(ClassContext classContext) { 66 JavaClass javaClass = classContext.getJavaClass(); 67 Method[] methodList = javaClass.getMethods(); 68 69 for (Method method : methodList) { 70 MethodGen methodGen = classContext.getMethodGen(method); 71 if (methodGen == null) 72 continue; 73 74 if (!prescreen(classContext, method)) 75 continue; 76 77 try { 78 analyzeMethod(classContext, method); 79 } catch (DataflowAnalysisException e) { 80 bugReporter.logError("FindSqlInjection caught exception while analyzing " + methodGen, e); 81 } catch (CFGBuilderException e) { 82 bugReporter.logError("FindSqlInjection caught exception while analyzing " + methodGen, e); 83 } 84 } 85 } 86 87 private void analyzeMethod(ClassContext classContext, Method method) 88 throws DataflowAnalysisException, CFGBuilderException { 89 90 JavaClass javaClass = classContext.getJavaClass(); 91 MethodGen methodGen = classContext.getMethodGen(method); 92 if (methodGen == null) return; 93 ConstantPoolGen cpg = methodGen.getConstantPool(); 94 try { 95 CFG cfg = classContext.getCFG(method); 96 ConstantDataflow dataflow 97 = classContext.getConstantDataflow(method); 98 boolean sawOpenQuote = false; 99 boolean sawCloseQuote = false; 100 boolean sawComma = false; 101 boolean sawAppend = false; 102 boolean sawUnsafeAppend = false; 103 for (Iterator <Location> i = cfg.locationIterator(); i.hasNext(); ) { 104 Location location = i.next(); 105 Instruction ins = location.getHandle().getInstruction(); 106 if (ins instanceof LDC) { 107 LDC load = (LDC) ins; 108 Object value = load.getValue(cpg); 109 if (value instanceof String ) { 110 String stringValue = ((String )value).trim(); 111 if (stringValue.startsWith(",") || stringValue.endsWith(",")) 112 sawComma = true; 113 if (stringValue.endsWith("'")) 114 sawOpenQuote = true; 115 if (stringValue.startsWith("'")) 116 sawCloseQuote = true; 117 } 118 } else if (ins instanceof INVOKEVIRTUAL) { 119 INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) ins; 120 121 if (invoke.getMethodName(cpg).equals("append") 122 && invoke.getClassName(cpg).startsWith("java.lang.StringB")) { 123 sawAppend = true; 124 InstructionHandle prev = location.getHandle().getPrev(); 125 if (prev != null) { 126 Instruction prevIns = prev.getInstruction(); 127 if (!(prevIns instanceof LDC || prevIns instanceof GETSTATIC)) 128 sawUnsafeAppend = true; 129 } 130 else sawUnsafeAppend = true; 131 } 132 } 133 } 134 135 for (Iterator <Location> i = cfg.locationIterator(); i.hasNext(); ) { 136 Location location = i.next(); 137 138 Instruction ins = location.getHandle().getInstruction(); 139 if (!(ins instanceof INVOKEINTERFACE)) continue; 140 INVOKEINTERFACE invoke = (INVOKEINTERFACE) ins; 141 142 String methodName = invoke.getMethodName(cpg); 143 String interfaceName = invoke.getClassName(cpg); 144 if (methodName.equals("prepareStatement") && interfaceName.equals("java.sql.Connection") 145 || methodName.startsWith("execute") && interfaceName.equals("java.sql.Statement")) { 146 ConstantFrame frame = dataflow.getFactAtLocation(location); 147 Constant value = frame.getStackValue(0); 148 149 if (!value.isConstantString()) { 150 int priority = LOW_PRIORITY; 151 if (sawAppend && sawOpenQuote && sawCloseQuote) priority = HIGH_PRIORITY; 152 else if (sawAppend && sawComma) priority = NORMAL_PRIORITY; 153 if (!sawUnsafeAppend) priority+=2; 154 bugReporter.reportBug( 155 new BugInstance(this, 156 methodName.equals("prepareStatement") 157 ? "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING" 158 : "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE", 159 priority) 160 .addClassAndMethod(methodGen, javaClass.getSourceFileName()) 161 .addSourceLine(classContext, methodGen, javaClass.getSourceFileName(), location.getHandle())); 162 } 163 } 164 165 166 } 167 } catch (RuntimeException e) { 168 System.out.println("Exception while checking for SQL injection in " 169 + methodGen + " in " + javaClass.getSourceFileName()); 170 e.printStackTrace(System.out); 171 } 172 173 } 174 175 176 public void report() { 177 } 178 } 179 180 | Popular Tags |