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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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 protected SafeParametersInterceptor(XWorkVersionSupport versionSupport)
63 {
64 this.versionSupport = versionSupport;
65 }
66
67 protected void after(ActionInvocation dispatcher, String result) throws Exception
68 {
69 }
70
71 public void setDisableAnnotationChecks(boolean disableAnnotationChecks)
72 {
73 this.disableAnnotationChecks = disableAnnotationChecks;
74 }
75
76
77
78
79
80
81
82
83
84
85
86 protected boolean shouldNotIntercept(ActionInvocation actionInvocation)
87 {
88 return versionSupport.extractAction(actionInvocation) instanceof NoParameters;
89 }
90
91 protected void before(ActionInvocation invocation) throws Exception
92 {
93
94 if (shouldNotIntercept(invocation))
95 {
96 return;
97 }
98
99 Action action = versionSupport.extractAction(invocation);
100
101
102 final Map<String, Object> parameters = filterSafeParameters(ActionContext.getContext().getParameters(), action);
103
104
105
106 if (log.isDebugEnabled())
107 {
108 log.debug("Setting params " + parameters);
109 }
110
111 ActionContext invocationContext = invocation.getInvocationContext();
112
113
114 try
115 {
116 invocationContext.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.TRUE);
117 invocationContext.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.TRUE);
118 invocationContext.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
119
120 if (parameters != null)
121 {
122 final OgnlValueStack stack = ActionContext.getContext().getValueStack();
123
124 for (Map.Entry<String, Object> entry : parameters.entrySet())
125 {
126 String name = entry.getKey();
127
128 stack.setValue(name, entry.getValue());
129 }
130 }
131 }
132 finally
133 {
134 invocationContext.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.FALSE);
135 invocationContext.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.FALSE);
136 invocationContext.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.FALSE);
137 }
138 }
139
140 private Map<String, Object> filterSafeParameters(Map<String, String> parameters, Action action)
141 {
142 Map<String, Object> safeParameters = new HashMap<String, Object>();
143
144 for (Map.Entry<String, String> entry : parameters.entrySet())
145 {
146 if (isSafeParameterName(entry.getKey(), action, disableAnnotationChecks))
147 {
148 safeParameters.put(entry.getKey(), entry.getValue());
149 }
150 }
151
152 return safeParameters;
153 }
154
155 static boolean isSafeParameterName(String key, Action action)
156 {
157 return isSafeParameterName(key, action, true);
158 }
159
160 static boolean isSafeParameterName(String key, Action action, boolean disableAnnotationChecks)
161 {
162 if (!SAFE_PARAMETER_NAME_PATTERN.matcher(key).matches())
163 {
164 return false;
165 }
166
167 if (!disableAnnotationChecks && (key.contains(".") || MAP_PARAMETER_PATTERN.matcher(key).matches())) {
168 return isSafeComplexParameterName(key, action);
169 }
170
171 return true;
172 }
173
174 private static boolean isSafeComplexParameterName(String key, Action action)
175 {
176 try
177 {
178 String initialParameterName = extractInitialParameterName(key);
179 BeanInfo info = Introspector.getBeanInfo(action.getClass());
180 PropertyDescriptor[] descs = info.getPropertyDescriptors();
181
182 for (PropertyDescriptor desc : descs)
183 {
184 if (desc.getName().equals(initialParameterName))
185 {
186 if (isSafeMethod(desc.getReadMethod()))
187 {
188 return true;
189 }
190 else
191 {
192 log.info("Attempt to call unsafe property setter " + key + " on " + action);
193 return false;
194 }
195 }
196 }
197 }
198 catch (IntrospectionException e)
199 {
200 log.warn("Error introspecting action parameter " + key + " for action " + action + ": " + e.getMessage(), e);
201 }
202
203 return false;
204 }
205
206 private static String extractInitialParameterName(String key)
207 {
208 if (!key.contains("[") || (key.indexOf(".") > 0 && key.indexOf("[") > key.indexOf(".")))
209 {
210 return key.substring(0, key.indexOf("."));
211 }
212 else
213 {
214 return key.substring(0, key.indexOf("["));
215 }
216 }
217
218 private static boolean isSafeMethod(Method writeMethod)
219 {
220 return writeMethod.getAnnotation(ParameterSafe.class) != null ||
221 writeMethod.getReturnType().getAnnotation(ParameterSafe.class) != null;
222 }
223 }