]> source.dussan.org Git - gitblit.git/commitdiff
XSS sanitize standard page url parameters
authorJames Moger <james.moger@gitblit.com>
Sat, 6 Sep 2014 15:27:04 +0000 (11:27 -0400)
committerJames Moger <james.moger@gitblit.com>
Sun, 7 Sep 2014 15:43:33 +0000 (11:43 -0400)
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
src/main/java/com/gitblit/wicket/GitblitParamUrlCodingStrategy.java

index 6cf5f582cdb8bc283fda76f5a437e1913f1836de..38dbf57d73870eb62dcfaed81e2ba5cd66f22f6a 100644 (file)
@@ -255,7 +255,7 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp {
                if (!settings.getBoolean(Keys.web.mountParameters, true)) {
                        parameters = new String[] {};
                }
-               mount(new GitblitParamUrlCodingStrategy(settings, location, clazz, parameters));
+               mount(new GitblitParamUrlCodingStrategy(settings, xssFilter, location, clazz, parameters));
 
                // map the mount point to the cache control definition
                if (clazz.isAnnotationPresent(CacheControl.class)) {
index c6583946a51ad04cdac09964b87ef1194c5320b3..536f88f4b090a2b1bd4b05a3cbc28f42a3fda768 100644 (file)
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit.wicket;\r
-\r
-import java.text.MessageFormat;\r
-import java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.apache.wicket.IRequestTarget;\r
-import org.apache.wicket.Page;\r
-import org.apache.wicket.request.RequestParameters;\r
-import org.apache.wicket.request.target.coding.MixedParamUrlCodingStrategy;\r
-import org.apache.wicket.util.string.AppendingStringBuffer;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.IStoredSettings;\r
-import com.gitblit.Keys;\r
-\r
-/**\r
- * Simple subclass of mixed parameter url coding strategy that works around the\r
- * encoded forward-slash issue that is present in some servlet containers.\r
- *\r
- * https://issues.apache.org/jira/browse/WICKET-1303\r
- * http://tomcat.apache.org/security-6.html\r
- *\r
- * @author James Moger\r
- *\r
- */\r
-public class GitblitParamUrlCodingStrategy extends MixedParamUrlCodingStrategy {\r
-\r
-       private final String[] parameterNames;\r
-\r
-       private Logger logger = LoggerFactory.getLogger(GitblitParamUrlCodingStrategy.class);\r
-\r
-       private IStoredSettings settings;\r
-\r
-       /**\r
-        * Construct.\r
-        *\r
-        * @param <C>\r
-        * @param mountPath\r
-        *            mount path (not empty)\r
-        * @param bookmarkablePageClass\r
-        *            class of mounted page (not null)\r
-        * @param parameterNames\r
-        *            the parameter names (not null)\r
-        */\r
-       public <C extends Page> GitblitParamUrlCodingStrategy(\r
-                       IStoredSettings settings,\r
-                       String mountPath,\r
-                       Class<C> bookmarkablePageClass, String[] parameterNames) {\r
-\r
-               super(mountPath, bookmarkablePageClass, parameterNames);\r
-               this.parameterNames = parameterNames;\r
-               this.settings = settings;\r
-       }\r
-\r
-       /**\r
-        * Url encodes a string that is mean for a URL path (e.g., between slashes)\r
-        *\r
-        * @param string\r
-        *            string to be encoded\r
-        * @return encoded string\r
-        */\r
-       @Override\r
-       protected String urlEncodePathComponent(String string) {\r
-               char altChar = settings.getChar(Keys.web.forwardSlashCharacter, '/');\r
-               if (altChar != '/') {\r
-                       string = string.replace('/', altChar);\r
-               }\r
-               return super.urlEncodePathComponent(string);\r
-       }\r
-\r
-       /**\r
-        * Returns a decoded value of the given value (taken from a URL path\r
-        * section)\r
-        *\r
-        * @param value\r
-        * @return Decodes the value\r
-        */\r
-       @Override\r
-       protected String urlDecodePathComponent(String value) {\r
-               char altChar = settings.getChar(Keys.web.forwardSlashCharacter, '/');\r
-               if (altChar != '/') {\r
-                       value = value.replace(altChar, '/');\r
-               }\r
-               return super.urlDecodePathComponent(value);\r
-       }\r
-\r
-       /**\r
-        * Gets the decoded request target.\r
-        *\r
-        * @param requestParameters\r
-        *            the request parameters\r
-        * @return the decoded request target\r
-        */\r
-       @Override\r
-       public IRequestTarget decode(RequestParameters requestParameters) {\r
-               final String parametersFragment = requestParameters.getPath().substring(\r
-                               getMountPath().length());\r
-               logger.debug(MessageFormat\r
-                               .format("REQ: {0} PARAMS {1}", getMountPath(), parametersFragment));\r
-\r
-               return super.decode(requestParameters);\r
-       }\r
-\r
-       /**\r
-        * @see org.apache.wicket.request.target.coding.AbstractRequestTargetUrlCodingStrategy#appendParameters(org.apache.wicket.util.string.AppendingStringBuffer,\r
-        *      java.util.Map)\r
-        */\r
-       @Override\r
-       protected void appendParameters(AppendingStringBuffer url, Map<String, ?> parameters)\r
-       {\r
-               if (!url.endsWith("/"))\r
-               {\r
-                       url.append("/");\r
-               }\r
-\r
-               Set<String> parameterNamesToAdd = new HashSet<String>(parameters.keySet());\r
-\r
-               // Find index of last specified parameter\r
-               boolean foundParameter = false;\r
-               int lastSpecifiedParameter = parameterNames.length;\r
-               while (lastSpecifiedParameter != 0 && !foundParameter)\r
-               {\r
-                       foundParameter = parameters.containsKey(parameterNames[--lastSpecifiedParameter]);\r
-               }\r
-\r
-               if (foundParameter)\r
-               {\r
-                       for (int i = 0; i <= lastSpecifiedParameter; i++)\r
-                       {\r
-                               String parameterName = parameterNames[i];\r
-                               final Object param = parameters.get(parameterName);\r
-                               String value = param instanceof String[] ? ((String[])param)[0] : ((param == null)\r
-                                       ? null : param.toString());\r
-                               if (value == null)\r
-                               {\r
-                                       value = "";\r
-                               }\r
-                               if (!url.endsWith("/"))\r
-                               {\r
-                                       url.append("/");\r
-                               }\r
-                               url.append(urlEncodePathComponent(value));\r
-                               parameterNamesToAdd.remove(parameterName);\r
-                       }\r
-               }\r
-\r
-               if (!parameterNamesToAdd.isEmpty())\r
-               {\r
-                       boolean first = true;\r
-                       for (String parameterName : parameterNamesToAdd)\r
-                       {\r
-                               final Object param = parameters.get(parameterName);\r
-                               if (param instanceof String[]) {\r
-                                       String [] values = (String[]) param;\r
-                                       for (String value : values) {\r
-                                               url.append(first ? '?' : '&');\r
-                                               url.append(urlEncodeQueryComponent(parameterName)).append("=").append(\r
-                                                               urlEncodeQueryComponent(value));\r
-                                               first = false;\r
-                                       }\r
-                               } else {\r
-                                       url.append(first ? '?' : '&');\r
-                                       String value = String.valueOf(param);\r
-                                       url.append(urlEncodeQueryComponent(parameterName)).append("=").append(\r
-                                               urlEncodeQueryComponent(value));\r
-                               }\r
-                               first = false;\r
-                       }\r
-               }\r
-       }\r
+/*
+ * Copyright 2011 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.IRequestTarget;
+import org.apache.wicket.Page;
+import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
+import org.apache.wicket.request.RequestParameters;
+import org.apache.wicket.request.target.coding.MixedParamUrlCodingStrategy;
+import org.apache.wicket.util.string.AppendingStringBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.utils.XssFilter;
+
+/**
+ * Simple subclass of mixed parameter url coding strategy that works around the
+ * encoded forward-slash issue that is present in some servlet containers.
+ *
+ * https://issues.apache.org/jira/browse/WICKET-1303
+ * http://tomcat.apache.org/security-6.html
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitParamUrlCodingStrategy extends MixedParamUrlCodingStrategy {
+
+       private final String[] parameterNames;
+
+       private Logger logger = LoggerFactory.getLogger(GitblitParamUrlCodingStrategy.class);
+
+       private IStoredSettings settings;
+
+       private XssFilter xssFilter;
+
+       /**
+        * Construct.
+        *
+        * @param <C>
+        * @param mountPath
+        *            mount path (not empty)
+        * @param bookmarkablePageClass
+        *            class of mounted page (not null)
+        * @param parameterNames
+        *            the parameter names (not null)
+        */
+       public <C extends Page> GitblitParamUrlCodingStrategy(
+                       IStoredSettings settings,
+                       XssFilter xssFilter,
+                       String mountPath,
+                       Class<C> bookmarkablePageClass, String[] parameterNames) {
+
+               super(mountPath, bookmarkablePageClass, parameterNames);
+               this.parameterNames = parameterNames;
+               this.settings = settings;
+               this.xssFilter = xssFilter;
+       }
+
+       /**
+        * Url encodes a string that is mean for a URL path (e.g., between slashes)
+        *
+        * @param string
+        *            string to be encoded
+        * @return encoded string
+        */
+       @Override
+       protected String urlEncodePathComponent(String string) {
+               char altChar = settings.getChar(Keys.web.forwardSlashCharacter, '/');
+               if (altChar != '/') {
+                       string = string.replace('/', altChar);
+               }
+               return super.urlEncodePathComponent(string);
+       }
+
+       /**
+        * Returns a decoded value of the given value (taken from a URL path
+        * section)
+        *
+        * @param value
+        * @return Decodes the value
+        */
+       @Override
+       protected String urlDecodePathComponent(String value) {
+               char altChar = settings.getChar(Keys.web.forwardSlashCharacter, '/');
+               if (altChar != '/') {
+                       value = value.replace(altChar, '/');
+               }
+               return super.urlDecodePathComponent(value);
+       }
+
+       /**
+        * Gets the decoded request target.
+        *
+        * @param requestParameters
+        *            the request parameters
+        * @return the decoded request target
+        */
+       @Override
+       public IRequestTarget decode(RequestParameters requestParameters) {
+               Map<String, Object> parameterMap = (Map<String, Object>) requestParameters.getParameters();
+               for (Map.Entry<String, Object> entry : parameterMap.entrySet()) {
+                       String parameter = entry.getKey();
+                       if (parameter.startsWith(WebRequestCodingStrategy.NAME_SPACE)) {
+                               // ignore Wicket parameters
+                               continue;
+                       }
+
+                       // sanitize Gitblit request parameters
+                       Object o = entry.getValue();
+                       if (o instanceof String) {
+                               String value = o.toString();
+                               String safeValue = xssFilter.none(value);
+                               if (!value.equals(safeValue)) {
+                                       logger.warn("XSS filter triggered on {} URL parameter: {}={}",
+                                                       getMountPath(), parameter, value);
+                                       parameterMap.put(parameter, safeValue);
+                               }
+                       } else if (o instanceof String[]) {
+                               String[] values = (String[]) o;
+                               for (int i = 0; i < values.length; i++) {
+                                       String value = values[i].toString();
+                                       String safeValue = xssFilter.none(value);
+                                       if (!value.equals(safeValue)) {
+                                               logger.warn("XSS filter triggered on {} URL parameter: {}={}",
+                                                               getMountPath(), parameter, value);
+                                               values[i] = safeValue;
+                                       }
+                               }
+                       }
+               }
+
+               return super.decode(requestParameters);
+       }
+
+       /**
+        * @see org.apache.wicket.request.target.coding.AbstractRequestTargetUrlCodingStrategy#appendParameters(org.apache.wicket.util.string.AppendingStringBuffer,
+        *      java.util.Map)
+        */
+       @Override
+       protected void appendParameters(AppendingStringBuffer url, Map<String, ?> parameters)
+       {
+               if (!url.endsWith("/"))
+               {
+                       url.append("/");
+               }
+
+               Set<String> parameterNamesToAdd = new HashSet<String>(parameters.keySet());
+
+               // Find index of last specified parameter
+               boolean foundParameter = false;
+               int lastSpecifiedParameter = parameterNames.length;
+               while (lastSpecifiedParameter != 0 && !foundParameter)
+               {
+                       foundParameter = parameters.containsKey(parameterNames[--lastSpecifiedParameter]);
+               }
+
+               if (foundParameter)
+               {
+                       for (int i = 0; i <= lastSpecifiedParameter; i++)
+                       {
+                               String parameterName = parameterNames[i];
+                               final Object param = parameters.get(parameterName);
+                               String value = param instanceof String[] ? ((String[])param)[0] : ((param == null)
+                                       ? null : param.toString());
+                               if (value == null)
+                               {
+                                       value = "";
+                               }
+                               if (!url.endsWith("/"))
+                               {
+                                       url.append("/");
+                               }
+                               url.append(urlEncodePathComponent(value));
+                               parameterNamesToAdd.remove(parameterName);
+                       }
+               }
+
+               if (!parameterNamesToAdd.isEmpty())
+               {
+                       boolean first = true;
+                       for (String parameterName : parameterNamesToAdd)
+                       {
+                               final Object param = parameters.get(parameterName);
+                               if (param instanceof String[]) {
+                                       String [] values = (String[]) param;
+                                       for (String value : values) {
+                                               url.append(first ? '?' : '&');
+                                               url.append(urlEncodeQueryComponent(parameterName)).append("=").append(
+                                                               urlEncodeQueryComponent(value));
+                                               first = false;
+                                       }
+                               } else {
+                                       url.append(first ? '?' : '&');
+                                       String value = String.valueOf(param);
+                                       url.append(urlEncodeQueryComponent(parameterName)).append("=").append(
+                                               urlEncodeQueryComponent(value));
+                               }
+                               first = false;
+                       }
+               }
+       }
 }
\ No newline at end of file