-/*\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