001: /*
002: * Use Case SQL String Extractor
003: * Take all SQL string components and concate them in order of occurance.
004: * Postprocessor may examine the SQL expression for the following
005: * 1 Table - DAO Crossreference: Which Java Class access which table in a specfic mode?
006: * 2 Analyse SQL expression for quality
007: * 3 Detect transaction logic in PL/SQL statements and correlate them with Java TX
008: *
009: * Solution Space
010: * SQL strings shall be defined as static final prepared statements inside the DAO.
011: * In this scenario, the SQL string and Java class are easy to associate.
012: * But schema & table names are typically constants defined in constant "interfaces" or property files.
013: * Here we need a resolving mechanism and have to use a string constant repository.
014: *
015: * Example:
016: * String str = "SELECT * FROM " + LtcConstant.EMPLOYEE_TABLE;
017: *
018: * Where Clause Injection
019: * Many SELECT statements are dynamically concated and parts (especially WHERE clauses) are injected.
020: * The parameters (e.g. Foreign Key value) are passed in the method parameter lists and StringBuffer >> append(String)
021: * or the slow "+" operator concat the SQL String. Our use case could survive by catching the parameter names.
022: *
023: * Example:
024: * String str = "Select DOC_I from EC_TRANS where DOC_I = ?";
025: *
026: * But the table name has to be resolved and any complex SQL injection
027: *
028: * Variable Stack
029: * The visitor has to build a variable stack for class variables, instance & local variables, and method parameters.
030: * Not only variables keeping string constants but all Strings, and StringBuffer has to be stored.
031: * The dynamics of operator "+" and at least the StringBuffer API append(String) has to be simulated.
032: * We have here a mass problem and a memory storage has to be replaced by a persistent store.
033: *
034: * Algorithm:
035: * First Scan
036: * Identify all StringConstants, String, and StringBuffer and save them
037: * with there access level, class, modifier, line, column, and value
038: *
039: * Variable Resolving
040: * Check for variables in the string variables value.
041: * If the variable contains other vars, use a recursive approach.
042: * Each resolved variable value will be stored and bind.
043: * In case of any errors (e.g. variable values are result of UI or 3rd party returns),
044: * the constant "<varNotResolvable> will be inserted
045: *
046: *
047: *
048: * Example:
049: * String str = "Select DOC_I from (" + strWithComplexSQLString +")";
050: *
051: * A full caller trace is needed in order to reference the source of parameters.
052: *
053: *
054: * Grammar for Cross Reference
055: * We could use a ANTLR SQL grammar, but for simple cross referencing we have adavantage of a SAX-minded state graph.
056: *
057: * SELECT -> FROM -> <tableName>
058: * INSERT -> INTO -> <tableName>
059: * UPDATE -> <tableName>
060: * DELETE -> FROM -> <tableName>
061: *
062: * These SQLs may be nested, but this is irrelevant for cross referencing.
063: *
064: * Challenge
065: * Hibernate Config
066: *
067: */
068: package org.hammurapi.inspectors.metrics;
069:
070: import java.util.Collection;
071: import java.util.Enumeration;
072: import java.util.Hashtable;
073: import java.util.Iterator;
074: import java.util.Properties;
075:
076: import org.hammurapi.HammurapiException;
077: import org.hammurapi.InspectorBase;
078: import org.hammurapi.results.AnnotationContext;
079: import org.hammurapi.results.LinkedAnnotation;
080:
081: import com.pavelvlasov.jsel.Constructor;
082: import com.pavelvlasov.jsel.Interface;
083: import com.pavelvlasov.jsel.JselException;
084: import com.pavelvlasov.jsel.LanguageElement;
085: import com.pavelvlasov.jsel.Method;
086: import com.pavelvlasov.jsel.Parameter;
087: import com.pavelvlasov.jsel.Repository;
088: import com.pavelvlasov.jsel.TypeDefinition;
089: import com.pavelvlasov.jsel.VariableDefinition;
090: import com.pavelvlasov.jsel.expressions.Dot;
091: import com.pavelvlasov.jsel.expressions.Ident;
092: import com.pavelvlasov.jsel.expressions.MethodCall;
093: import com.pavelvlasov.jsel.expressions.Plus;
094: import com.pavelvlasov.jsel.expressions.PlusAssignment;
095: import com.pavelvlasov.jsel.expressions.StringConstant;
096: import com.pavelvlasov.jsel.statements.ForInitializer;
097:
098: /**
099: * @author MUCBJ0
100: *
101: * TODO To change the template for this generated type comment go to
102: * Window - Preferences - Java - Code Style - Code Templates
103: */
104: public class SqlStringExtractor extends InspectorBase {
105:
106: //-- everything with prefix current* represents the current source
107: private StringVariable currentVariable = null;
108:
109: private String currentClassName = "";
110: private String currentClassFcn = "";
111:
112: public SqlExtractorPersistencyService persistencyService = null;
113:
114: //!! memory greed prob .. pass to DB; reinitalized in visit(TypeDef)
115: private Hashtable variableTable = new Hashtable();
116:
117: public void init() throws HammurapiException {
118: super .init();
119: // persistencyService = new SqlExtractorHyperSonicInMemoryDb(context);
120: persistencyService = new SqlExtractorHyperSonicStanaloneServer(
121: context);
122: }
123:
124: public void visit(TypeDefinition p) {
125: System.out.println(variableTable.size() + " -- " + p.getName());
126: //-- store current state
127: storeCurrentState();
128:
129: //-- reinitalize
130: variableTable = new Hashtable();
131: currentClassName = p.getName();
132: currentClassFcn = p.getFcn();
133: }
134:
135: public void visit(Interface p) {
136: System.out.println(variableTable.size() + " -- " + p.getName());
137: //-- store current state
138: storeCurrentState();
139:
140: //-- reinitalize
141: variableTable = new Hashtable();
142: currentClassName = p.getName();
143: currentClassFcn = p.getFcn();
144: }
145:
146: public void visit(MethodCall methodCall) {
147: // System.out.println("*> " + methodCall.getMethodName() );
148:
149: if ("append".equals(methodCall.getMethodName())) {
150:
151: System.out.println(currentVariable.name);
152:
153: //StringBuffer currentVarValue = (StringBuffer) variableTable .get(id.toString());
154:
155: }
156:
157: /* Code code = methodCall.getEnclosingCode();
158: try {
159: // System.out.println("*> " + methodCall.getProvider().toString());
160: if( code != null){
161: Operation op = (Operation) code;
162: // OperationInfo provider = new OperationInfo(code);
163: TypeBody tb = code.getEnclosingType();
164:
165: String key = tb.getFcn()+ ">>" +op.getOperationSignature();
166: // System.out.println( key );
167: } else {
168: // System.out.println( "code null" );
169: }
170: } catch (JselException e) {
171: // TODO Auto-generated catch block
172: e.printStackTrace();
173: }
174: */
175: }
176:
177: public void visit(final Plus aPlus) {
178: //System.out.println(" Plus " + aPlus );
179:
180: try {
181:
182: if ("java.lang.String".equals(aPlus.getTypeSpecification()
183: .toString())) {
184: //System.out.println(" Plus " + aPlus.getOperands() );
185: Collection lst = aPlus.getOperands();
186: // System.out.println(" instanceof " + lst.get(lst.size()-1).getClass().toString() );
187: /*
188: String lastString = (String)lst.get(lst.size()-1);
189: StringBuffer tempString = currentString;
190: tempString.append(lastString);
191: currentString= tempString;
192:
193: System.out.println(" currentString " + currentString );
194: } else {
195: // handle variable of method call
196: System.out.println( "+++ " +lst.get(lst.size()-1) );
197: }
198: */
199: }
200: } catch (Exception e) {
201: // TODO Auto-generated catch block
202: e.printStackTrace();
203: }
204:
205: }
206:
207: public void visit(final PlusAssignment aPlus) {
208: // System.out.println(" PlusAssignment " );
209: }
210:
211: /*
212: * Problem: Parameter Strings are visited AFTER method internals
213: */
214: /*
215: * Problem: Parameter Strings are visited AFTER method internals
216: */
217: public void visit(final Parameter aParam) {
218: // System.out.println(" Parameter " + aParam);
219: try {
220: if (aParam.getTypeSpecification().isKindOf(
221: "java.lang.String")
222: || aParam.getTypeSpecification().isKindOf(
223: "java.lang.StringBuffer")) {
224:
225: currentVariable = new StringVariable(aParam,
226: new StringBuffer("<" + aParam.getName() + ">"),
227: aParam.getName(), "Parameter",
228: currentClassName, currentClassFcn);
229: /* currentVariable.langElement= aParam;
230: currentVariable.type = "Parameter";
231: currentVariable.varValue = new StringBuffer("<"+ aParam.getName() +">" );
232: */// saveLanguageElement( currentVariable.langElement, currentVariable.varValue.toString() );
233: variableTable.put(aParam.getName(), currentVariable);
234: //this.currentStringValue = new StringBuffer();
235: }
236: } catch (JselException e) {
237: // TODO Auto-generated catch block
238: e.printStackTrace();
239: }
240: }
241:
242: public void visit(final VariableDefinition varDef) {
243: // System.out.println("## varDef "+ varDef);
244: try {
245: if (varDef.getTypeSpecification().isKindOf(
246: "java.lang.String")
247: || varDef.getTypeSpecification().isKindOf(
248: "java.lang.StringBuffer")) {
249: if (currentVariable != null
250: && currentVariable.langElement != null) {
251:
252: // reinitalize
253: if (currentVariable.langElement instanceof VariableDefinition) {
254: variableTable
255: .put(
256: ((VariableDefinition) currentVariable.langElement)
257: .getName(),
258: currentVariable);
259: } else if (currentVariable.langElement instanceof Parameter) {
260: variableTable
261: .put(
262: ((Parameter) currentVariable.langElement)
263: .getName(),
264: currentVariable);
265: }
266: }
267: currentVariable = new StringVariable(varDef,
268: new StringBuffer(), varDef.getName(),
269: "local variable", currentClassName,
270: currentClassFcn);
271:
272: } else {
273: // not a String Var: Reset current stack.
274: currentVariable = new StringVariable(varDef,
275: new StringBuffer(), varDef.getName(),
276: "local variable: not a String type",
277: currentClassName, currentClassFcn);
278: }
279: } catch (JselException e) {
280: // TODO Auto-generated catch block
281: e.printStackTrace();
282: }
283: }
284:
285: public void visit(Dot dot) {
286:
287: }
288:
289: public void visit(Constructor le) {
290: //System.out.println("Constructor " + le.toString());
291: }
292:
293: public void visit(Method le) {
294: // System.out.println("Method " + le.toString());
295: }
296:
297: public void visit(ForInitializer le) {
298: //System.out.println("ForInitializer " + le.toString());
299: }
300:
301: //!! Problem: static class vars: search term is var name without class name only.
302: /*
303: * Ident AnotherDaoObject
304: Ident LTC_REPORT_DB
305: Ident LTC_REPORT_DB<--"LTC_REPORT"
306:
307: Johannes,
308:
309: In AnotherDaoObject.LTC_REPORT_DB you need to invoke getProvider() of the
310: last ident and you'll get a reference to LTC_REPORT_DB field in
311: AnotherDaoObject, which would be of type VariableDefinition, Parameter,
312: TypeDefiniton for source files and java.lang.reflect.Field for external
313: classes, ... See JavaDoc
314: http://www.pavelvlasov.com/products/Jsel/doc/api/com/pavelvlasov/jsel/expressions/Ident.html#getProvider().
315: ---
316: Best regards, Pavel.
317:
318: ----- Original Message -----
319: From: <Johannes.Bellert@ge.com>
320: To: <vlasov@pavelvlasov.com>; <Pavel.Vlasov@ge.com>
321: Sent: Monday, September 27, 2004 4:18 PM
322: Subject: Ident -> Dot -> Ident
323:
324:
325: > Pavel,
326: > I got a baby step further with my SQL Extractor.
327: > I catch all StringLiterals and assigne them to variables .. if a String is
328: > concated (Plus, PlusAssignment, append(String)), I simulate the behavior.
329: > I also look up for already defined Vars which works quite OK for local and
330: > instance Vars.
331: > Public Class Vars will be stored with Class name qualifier.
332: >
333: > Problem I have now is, how to identify something like
334: > String strCompanyDaoUpdate = "INSERT INTO " +
335: > AnotherDaoObject.LTC_REPORT_DB;
336: >
337: > I tried to implement a state engine like:
338: > Ident -> Dot -> Ident
339: > and append the var name appropriately. Unfortunately, I have to implement
340: > all possible nodes in the visitor for all Dots & Idents ..
341: > I also could ask the Dot for children but this is very hard wired and I
342: > learned that I have to add those checks in the standard Ident as well.
343: >
344: > Any ideas?
345: >
346: > <<summary.html_String literals39090.ZIP>>
347: > Thanks,
348: > Johannes
349: */
350: public void visit(final Ident id) {
351: // System.out.println("Ident " + id.toString());
352: try {
353: final Object provider = id.getProvider();
354: if (provider != null
355: && ((provider instanceof VariableDefinition) && (((VariableDefinition) provider)
356: .getTypeSpecification().isKindOf(
357: "java.lang.String") || ((VariableDefinition) provider)
358: .getTypeSpecification().isKindOf(
359: "java.lang.StringBuffer")))
360: || (provider instanceof Parameter && (((Parameter) provider)
361: .getTypeSpecification().isKindOf(
362: "java.lang.String") || ((Parameter) provider)
363: .getTypeSpecification().isKindOf(
364: "java.lang.StringBuffer")))) {
365:
366: StringVariable svTmp = (StringVariable) variableTable
367: .get(id.toString());
368: StringBuffer currentVarValue;
369:
370: if (svTmp != null) {
371: currentVarValue = svTmp.varValue;
372: } else {
373: currentVarValue = new StringBuffer();
374: }
375:
376: // handle public static Class variables
377: LanguageElement le = ((LanguageElement) id).getParent();
378: if (le instanceof Dot
379: && le.getAst().getNumberOfChildren() == 2) {
380: String className = le.getAst().getFirstChild()
381: .getText();
382: StringVariable currentVariableX = (StringVariable) variableTable
383: .get(className);
384:
385: // not a class but a StringBuffer Variable or something
386: if (currentVariableX != null) {
387: // System.out.println("currentVarValueX " + currentVariableX.name );
388: currentVariable = currentVariableX;
389: /*
390: currentVariable.varValue.append(currentVarValue);
391: // put here the right Hand to left hand side assignment
392: System.out.println("Ident "
393: + currentVariable.name.toString() + "<--"
394: + currentVariable.varValue);
395: saveLanguageElement(currentVariable.langElement,
396: currentVariable.varValue.toString());
397: */
398: // System.out.println("Ident " + currentVariable.name.toString() + "<--" + currentVariable.varValue);
399: } else {
400: String currentLangElementName = className + "."
401: + id.toString();
402: //!!
403: // variableTable.put(currentLangElementName, );
404: // System.out.println("Ident added: " + currentLangElementName + " " + currentVariable.varValue);
405:
406: currentVariable.varValue.append(" <");
407: currentVariable.varValue
408: .append(currentLangElementName);
409: currentVariable.varValue.append("> ");
410: }
411: } else if (currentVarValue != null
412: && currentVarValue.length() > 0) {
413:
414: currentVariable.varValue.append(currentVarValue);
415: // put here the right Hand to left hand side assignment
416: // System.out.println("Ident " + currentVariable.name.toString() + "<--" + currentVariable.varValue);
417: // saveLanguageElement(currentVariable.langElement,currentVariable.varValue.toString());
418:
419: } else {
420: boolean paramIsFound = false;
421: // unresolved variables -- assumption: Operation Parameters
422: LanguageElement lex = currentVariable.langElement
423: .getParent().getParent();
424: if (lex instanceof Method) {
425: Method met = (Method) lex;
426: // System.out.println("Ident currentVariable .. " + met.getName());
427: Iterator it = met.getParameters().iterator();
428: while (it.hasNext()) {
429: Parameter p = (Parameter) it.next();
430: // System.out.println("p.getName() " + p.getName());
431: if (id.toString().equals(p.getName())) {
432: paramIsFound = true;
433: }
434: }
435:
436: if (paramIsFound) {
437: currentVariable.varValue.append("@");
438: currentVariable.varValue
439: .append(currentClassName);
440: currentVariable.varValue.append(">>");
441: currentVariable.varValue.append(met
442: .getName());
443: currentVariable.varValue.append("::");
444: currentVariable.varValue.append(id);
445: currentVariable.varValue.append("-- ");
446: // saveLanguageElement(currentVariable.langElement, currentVariable.varValue.toString());
447: variableTable.put(currentVariable.name,
448: currentVariable);
449: // System.out.println("Ident currentVariable "+ currentVariable.name +" varValue " + currentVariable.varValue);
450: }
451: }
452: }
453: }
454: } catch (Exception e) {
455: // TODO Auto-generated catch block
456: e.printStackTrace();
457: }
458:
459: // try to resolve string
460: /*
461: * try { if (currentLangElement != null) { System.out.println("
462: * currentString " + currentLangElement.toString() + " <- " +
463: * currentStringValue); }
464: *
465: * final Object provider = id.getProvider(); if (provider != null &&
466: * provider instanceof VariableDefinition && (((VariableDefinition)
467: * provider).getTypeSpecification() .isKindOf("java.lang.String") ||
468: * ((VariableDefinition) provider) .getTypeSpecification().isKindOf(
469: * "java.lang.StringBuffer"))) { System.out.println("OO " +
470: * provider.toString()); SQLProcessor processor =
471: * getProcessor((SourceMarker) id); if (processor != null) { // check
472: * for String Only ! processor.processUpdate("INSERT INTO " +
473: * varTableName + " (VAR_NAME, VAR_VALUE, SOURCE, LINE, COL) " + "VALUES
474: * (?,?,?,?,?)", new Parameterizer() { public void
475: * parameterize(PreparedStatement ps) throws SQLException {
476: * ps.setString(1, id.toString()); ps.setString(2, provider.toString());
477: * SourceMarker sourceMarker = (SourceMarker) id; ps.setString(3,
478: * sourceMarker.getSourceURL()); ps.setInt(4, sourceMarker.getLine());
479: * ps.setInt(5, sourceMarker.getColumn()); } }); } } } catch (Exception
480: * e) { // TODO Auto-generated catch block e.printStackTrace(); }
481: */
482: }
483:
484: public String cutLeadingTrailingQuote(String str) {
485: String tmpString = str;
486: if (str != null) {
487: if (str.charAt(0) == '"') {
488: tmpString = str.substring(1);
489: }
490: str = tmpString;
491: if (str.charAt(str.length() - 1) == '"'
492: && (str.length() - 1) > -1) {
493: tmpString = str.substring(0, str.length() - 1);
494: }
495: }
496: return tmpString;
497: }
498:
499: public void visit(final StringConstant constant) {
500: // System.out.println("++ "+ constant.toString() );
501:
502: final String constantCopy = cutLeadingTrailingQuote(constant
503: .toString());
504: // System.out.println("++ "+ constantCopy );
505: currentVariable.varValue.append(constantCopy);
506: logCurrentLangElement();
507: /*
508: SQLProcessor processor = getProcessor((SourceMarker) constant);
509: if (processor != null) {
510: try {
511: processor.processUpdate(
512: "INSERT INTO " + sqlTableName
513: + " (LITERAL, SOURCE, LINE, COL) "
514: + "VALUES (?,?,?,?)", new Parameterizer() {
515: public void parameterize(PreparedStatement ps)
516: throws SQLException {
517: ps.setString(1, constantCopy );
518: SourceMarker sourceMarker = (SourceMarker) constant;
519: ps.setString(2, sourceMarker.getSourceURL());
520: ps.setInt(3, sourceMarker.getLine());
521: ps.setInt(4, sourceMarker.getColumn());
522: }
523: });
524: } catch (SQLException e) {
525: context.warn((SourceMarker) constant, e);
526: }
527: }
528: */
529: }
530:
531: private void storeCurrentState() {
532: if (currentClassName != null && !"".equals(currentClassName)) {
533: Enumeration enum = this .variableTable.elements();
534: while (enum.hasMoreElements()) {
535: StringVariable sVar = (StringVariable) enum.nextElement();
536: this .persistencyService.saveLanguageElement(sVar);
537: }
538: }
539: }
540:
541: private void logCurrentLangElement() {
542: if (currentVariable.langElement != null) {
543: // System.out.println(" currentString " + currentVariable.langElement.toString() +" <- " + currentVariable.varValue );
544: } else {
545: // System.out.println(" currentString is null" );
546: }
547: }
548:
549: public void leave(Repository repo) {
550:
551: this .storeCurrentState();
552:
553: context.annotate(new LinkedAnnotation() {
554: String path;
555:
556: public String getPath() {
557: return path;
558: }
559:
560: public String getName() {
561: return "String literals";
562: }
563:
564: public void render(AnnotationContext context)
565: throws HammurapiException {
566: persistencyService.render(context, path);
567: }
568:
569: public Properties getProperties() {
570: return null;
571: }
572: });
573:
574: }
575:
576: public void destroy() {
577: persistencyService.destroy();
578:
579: super.destroy();
580: }
581:
582: }
|