Clover Coverage Report - Atlassian XWork(Aggregated)
Coverage timestamp: Wed Jul 27 2011 23:39:31 CDT
49   223   28   4.45
18   151   0.57   11
11     2.55  
1    
 
 
  SafeParametersInterceptor       Line # 53 49 28 42.3% 0.42307693
 
  (9)
 
1    package com.atlassian.xwork.interceptors;
2   
3    import com.atlassian.xwork.ParameterSafe;
4    import com.atlassian.xwork.XWorkVersionSupport;
5    import com.opensymphony.xwork.Action;
6    import com.opensymphony.xwork.ActionContext;
7    import com.opensymphony.xwork.ActionInvocation;
8    import com.opensymphony.xwork.interceptor.AroundInterceptor;
9    import com.opensymphony.xwork.interceptor.NoParameters;
10    import com.opensymphony.xwork.util.InstantiatingNullHandler;
11    import com.opensymphony.xwork.util.OgnlValueStack;
12    import com.opensymphony.xwork.util.XWorkConverter;
13    import com.opensymphony.xwork.util.XWorkMethodAccessor;
14    import org.apache.log4j.Logger;
15   
16    import java.beans.BeanInfo;
17    import java.beans.IntrospectionException;
18    import java.beans.Introspector;
19    import java.beans.PropertyDescriptor;
20    import java.lang.reflect.Method;
21    import java.util.HashMap;
22    import java.util.Map;
23    import java.util.regex.Pattern;
24   
25    /**
26    * Injects submitted form parameters into action properties. This implementation performs white-list based
27    * sanity checks on incoming parameters before allowing OGNL to perform any potentially dangerous operations on
28    * an action, closing off an entire category of parameter injection attacks.
29    * <p/>
30    * Parameters that set a value on an action directly will be allowed as will index-based setters for collections
31    * of values. However:
32    * <ol>
33    * <li> To defend against possible OGNL vulnerabilities (especially Unicode attacks), parameter names will be
34    * filtered so only ascii alphanumeric characters (plus the underscore, square brackets and apostrophes) are permitted
35    * <li> If the dot-notation is used to access some property on an action (i.e. a parameter called "search.query")
36    * the type returned from the getter (getSearch()) MUST have the @ParameterSafe annotation for the parameter
37    * to be accepted, <i>or</i> the getter method must have the @ParameterSafe annotation
38    * <li> If the map-notation is used to access some property on an action (i.e. a parameter called "map['key']")
39    * the getter method must have the @ParameterSafe annotation
40    * </ol>
41    * <p/>
42    * These last two checks (@ParameterSafe checks for dot- and map-notation) can be skipped by setting
43    * disableAnnotationChecks. When disabled this interceptor still prevents Unicode-attacks (amoungst other things)
44    * but allows dot/map traversal of any POJO retrievable from an action. To disable, use a param e.g.
45    * <pre>
46    * &lt;interceptor name="params" class="com.atlassian.xwork12.interceptors.SafeParametersInterceptor">
47    * &lt;param name="disableAnnotationChecks">true&lt;/param>
48    * &lt;/interceptor>
49    * </pre>
50    * <p/>
51    * Portions of this class are copied from XWork under the Apache license, Copyright (c) 2002-2003 by OpenSymphony
52    */
 
53    public abstract class SafeParametersInterceptor extends AroundInterceptor
54    {
55    public static final Logger log = Logger.getLogger(SafeParametersInterceptor.class);
56   
57    private static final Pattern SAFE_PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9\\.\\]\\[_']+");
58    private static final Pattern MAP_PARAMETER_PATTERN = Pattern.compile(".*\\['[a-zA-Z0-9_]+'\\]");
59    private final XWorkVersionSupport versionSupport;
60    private boolean disableAnnotationChecks = false;
61   
 
62  0 toggle protected SafeParametersInterceptor(XWorkVersionSupport versionSupport)
63    {
64  0 this.versionSupport = versionSupport;
65    }
66   
 
67  0 toggle protected void after(ActionInvocation dispatcher, String result) throws Exception
68    {
69    }
70   
 
71  0 toggle public void setDisableAnnotationChecks(boolean disableAnnotationChecks)
72    {
73  0 this.disableAnnotationChecks = disableAnnotationChecks;
74    }
75   
76    /**
77    * The implementation of this method should evalutate if the passed in actionInvocation.getAction()
78    * is of a type {@link com.opensymphony.xwork.interceptor.NoParameters} if it is, we should not bother
79    * intercepting.
80    * <p/>
81    * The reason for this abstract class is so we are compatible with both 1.0.3 and 1.2.3 of XWork.
82    *
83    * @param actionInvocation the action invocation being intercepted
84    * @return true if we are not of type {@link com.opensymphony.xwork.interceptor.NoParameters}
85    */
 
86  0 toggle protected boolean shouldNotIntercept(ActionInvocation actionInvocation)
87    {
88  0 return versionSupport.extractAction(actionInvocation) instanceof NoParameters;
89    }
90   
 
91  0 toggle protected void before(ActionInvocation invocation) throws Exception
92    {
93   
94  0 if (shouldNotIntercept(invocation))
95    {
96  0 return;
97    }
98   
99  0 Action action = versionSupport.extractAction(invocation);
100   
101    //noinspection unchecked
102  0 final Map<String, Object> parameters = filterSafeParameters(ActionContext.getContext().getParameters(), action);
103   
104    // Copied from the XWork parameters interceptor:
105   
106  0 if (log.isDebugEnabled())
107    {
108  0 log.debug("Setting params " + parameters);
109    }
110   
111  0 ActionContext invocationContext = invocation.getInvocationContext();
112   
113   
114  0 try
115    {
116  0 invocationContext.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.TRUE);
117  0 invocationContext.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.TRUE);
118  0 invocationContext.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
119   
120  0 if (parameters != null)
121    {
122  0 final OgnlValueStack stack = ActionContext.getContext().getValueStack();
123   
124  0 for (Map.Entry<String, Object> entry : parameters.entrySet())
125    {
126  0 String name = entry.getKey();
127   
128  0 stack.setValue(name, entry.getValue());
129    }
130    }
131    }
132    finally
133    {
134  0 invocationContext.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.FALSE);
135  0 invocationContext.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.FALSE);
136  0 invocationContext.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.FALSE);
137    }
138    }
139   
 
140  0 toggle private Map<String, Object> filterSafeParameters(Map<String, String> parameters, Action action)
141    {
142  0 Map<String, Object> safeParameters = new HashMap<String, Object>();
143   
144  0 for (Map.Entry<String, String> entry : parameters.entrySet())
145    {
146  0 if (isSafeParameterName(entry.getKey(), action, disableAnnotationChecks))
147    {
148  0 safeParameters.put(entry.getKey(), entry.getValue());
149    }
150    }
151   
152  0 return safeParameters;
153    }
154   
 
155  0 toggle static boolean isSafeParameterName(String key, Action action)
156    {
157  0 return isSafeParameterName(key, action, true);
158    }
159   
 
160  45 toggle static boolean isSafeParameterName(String key, Action action, boolean disableAnnotationChecks)
161    {
162  45 if (!SAFE_PARAMETER_NAME_PATTERN.matcher(key).matches())
163    {
164  12 return false;
165    }
166   
167  33 if (!disableAnnotationChecks && (key.contains(".") || MAP_PARAMETER_PATTERN.matcher(key).matches())) {
168  18 return isSafeComplexParameterName(key, action);
169    }
170   
171  15 return true;
172    }
173   
 
174  18 toggle private static boolean isSafeComplexParameterName(String key, Action action)
175    {
176  18 try
177    {
178  18 String initialParameterName = extractInitialParameterName(key);
179  18 BeanInfo info = Introspector.getBeanInfo(action.getClass());
180  18 PropertyDescriptor[] descs = info.getPropertyDescriptors();
181   
182  18 for (PropertyDescriptor desc : descs)
183    {
184  188 if (desc.getName().equals(initialParameterName))
185    {
186  18 if (isSafeMethod(desc.getReadMethod()))
187    {
188  9 return true;
189    }
190    else
191    {
192  9 log.info("Attempt to call unsafe property setter " + key + " on " + action);
193  9 return false;
194    }
195    }
196    }
197    }
198    catch (IntrospectionException e)
199    {
200  0 log.warn("Error introspecting action parameter " + key + " for action " + action + ": " + e.getMessage(), e);
201    }
202   
203  0 return false;
204    }
205   
 
206  18 toggle private static String extractInitialParameterName(String key)
207    {
208  18 if (!key.contains("[") || (key.indexOf(".") > 0 && key.indexOf("[") > key.indexOf(".")))
209    {
210  10 return key.substring(0, key.indexOf("."));
211    }
212    else
213    {
214  8 return key.substring(0, key.indexOf("["));
215    }
216    }
217   
 
218  18 toggle private static boolean isSafeMethod(Method writeMethod)
219    {
220  18 return writeMethod.getAnnotation(ParameterSafe.class) != null ||
221    writeMethod.getReturnType().getAnnotation(ParameterSafe.class) != null;
222    }
223    }