View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.sunsecure;
5   
6   import java.util.ArrayList;
7   import java.util.List;
8   
9   import net.sourceforge.pmd.lang.ast.Node;
10  import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
11  import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement;
12  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
14  import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
15  import net.sourceforge.pmd.lang.java.ast.ASTExpression;
16  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
17  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
18  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
19  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
20  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
21  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
22  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
23  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
24  
25  /**
26   * If a method or constructor receives an array as an argument, the array should
27   * be cloned instead of directly stored. This prevents future changes from the user
28   * from affecting the original array.
29   *
30   * @since Created on Jan 17, 2005
31   * @author mgriffa
32   */
33  public class ArrayIsStoredDirectlyRule extends AbstractSunSecureRule {
34  
35      @Override
36      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
37          if (node.isInterface()) {
38              return data;
39          }
40          return super.visit(node, data);
41      }
42  
43      @Override
44      public Object visit(ASTConstructorDeclaration node, Object data) {
45          ASTFormalParameter[] arrs = getArrays(node.getParameters());
46          if (arrs != null) {
47              //TODO check if one of these arrays is stored in a non local variable
48              List<ASTBlockStatement> bs = node.findDescendantsOfType(ASTBlockStatement.class);
49              checkAll(data, arrs, bs);
50          }
51          return data;
52      }
53  
54      @Override
55      public Object visit(ASTMethodDeclaration node, Object data) {
56          final ASTFormalParameters params = node.getFirstDescendantOfType(ASTFormalParameters.class);
57          ASTFormalParameter[] arrs = getArrays(params);
58          if (arrs != null) {
59              checkAll(data, arrs, node.findDescendantsOfType(ASTBlockStatement.class));
60          }
61          return data;
62      }
63  
64      private void checkAll(Object context, ASTFormalParameter[] arrs, List<ASTBlockStatement> bs) {
65          for (ASTFormalParameter element : arrs) {
66              checkForDirectAssignment(context, element, bs);
67          }
68      }
69  
70      private String getExpressionVarName(Node e) {
71          String assignedVar = getFirstNameImage(e);
72          if (assignedVar == null) {
73              ASTPrimarySuffix suffix = e.getFirstDescendantOfType(ASTPrimarySuffix.class);
74              if (suffix != null) {
75                  assignedVar = suffix.getImage();
76                  ASTPrimaryPrefix prefix = e.getFirstDescendantOfType(ASTPrimaryPrefix.class);
77                  if (prefix != null) {
78                      if (prefix.usesThisModifier()) {
79                          assignedVar = "this." + assignedVar;
80                      } else if (prefix.usesSuperModifier()) {
81                          assignedVar = "super." + assignedVar;
82                      }
83                  }
84              }
85          }
86          return assignedVar;
87      }
88  
89      /**
90       * Checks if the variable designed in parameter is written to a field (not local variable) in the statements.
91       */
92      private boolean checkForDirectAssignment(Object ctx, final ASTFormalParameter parameter, final List<ASTBlockStatement> bs) {
93          final ASTVariableDeclaratorId vid = parameter.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
94          final String varName = vid.getImage();
95          for (ASTBlockStatement b: bs) {
96              if (b.hasDescendantOfType(ASTAssignmentOperator.class)) {
97                  final ASTStatementExpression se = b.getFirstDescendantOfType(ASTStatementExpression.class);
98                  if (se == null || !(se.jjtGetChild(0) instanceof ASTPrimaryExpression)) {
99                      continue;
100                 }
101                 ASTPrimaryExpression pe = (ASTPrimaryExpression) se.jjtGetChild(0);
102                 String assignedVar = getExpressionVarName(pe);
103                 if (assignedVar == null) {
104                     continue;
105                 }
106 
107                 Node n = pe.getFirstParentOfType(ASTMethodDeclaration.class);
108                 if (n == null) {
109 					n = pe.getFirstParentOfType(ASTConstructorDeclaration.class);
110 					if (n == null) {
111 						continue;
112 					}
113 				}
114                 if (!isLocalVariable(assignedVar, n)) {
115                     // TODO could this be more clumsy?  We really
116                     // need to build out the PMD internal framework more
117                     // to support simply queries like "isAssignedTo()" or something
118                     if (se.jjtGetNumChildren() < 3) {
119                         continue;
120                     }
121                     ASTExpression e = (ASTExpression) se.jjtGetChild(2);
122                     if (e.hasDescendantOfType(ASTEqualityExpression.class)) {
123                         continue;
124                     }
125                     String val = getExpressionVarName(e);
126                     if (val == null) {
127                         continue;
128                     }
129                     ASTPrimarySuffix foo = se.getFirstDescendantOfType(ASTPrimarySuffix.class);
130                     if (foo != null && foo.isArrayDereference()) {
131                         continue;
132                     }
133 
134                     if (val.equals(varName)) {
135                 	Node md = parameter.getFirstParentOfType(ASTMethodDeclaration.class);
136                         if (md == null) {
137                         	md = pe.getFirstParentOfType(ASTConstructorDeclaration.class);
138         				}
139                         if (!isLocalVariable(varName, md)) {
140                             addViolation(ctx, parameter, varName);
141                         }
142                     }
143                 }
144             }
145         }
146         return false;
147     }
148 
149     private final ASTFormalParameter[] getArrays(ASTFormalParameters params) {
150         final List<ASTFormalParameter> l = params.findChildrenOfType(ASTFormalParameter.class);
151         if (l != null && !l.isEmpty()) {
152             List<ASTFormalParameter> l2 = new ArrayList<ASTFormalParameter>();
153             for (ASTFormalParameter fp: l) {
154                 if (fp.isArray()) {
155 		    l2.add(fp);
156 		}
157             }
158             return l2.toArray(new ASTFormalParameter[l2.size()]);
159         }
160         return null;
161     }
162 
163 }