/* * * Paros and its related class files. * * Paros is an HTTP/HTTPS proxy for assessing web application security. * Copyright (C) 2003-2004 Chinotec Technologies Company * * This program is free software; you can redistribute it and/or * modify it under the terms of the Clarified Artistic License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Clarified Artistic License for more details. * * You should have received a copy of the Clarified Artistic License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // ZAP: 2012/03/15 Changed the methods (the private) setParameter and paramAppend to // use the class StringBuilder instead of StringBuffer. Removed unnecessary // castings in the method setParameter. Made a change in the method parse. // ZAP: 2012/04/25 Added @Override annotation to all appropriate methods. // ZAP: 2013/05/02 Re-arranged all modifiers into Java coding standard order // ZAP: 2013/07/02 Changed Vector to ArrayList because obsolete and faster // ZAP: 2013/08/21 Added a new encoding/decoding model for a correct parameter value interpretation // ZAP: 2014/01/06 Issue 965: Support 'single page' apps and 'non standard' parameter separators // ZAP: 2014/02/08 Used the same constants used in ScanParam Target settings // ZAP: 2016/05/04 Changes to address issues related to ParameterParser // ZAP: 2016/05/26 Use non-null String for names and values of parameters, scanners might not handle null names/values well // ZAP: 2016/09/13 Issue 2863: Attack query string even if not originally specified package org.parosproxy.paros.core.scanner; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.network.HttpMessage; import org.zaproxy.zap.model.ParameterParser; public abstract class VariantAbstractQuery implements Variant { private List<NameValuePair> listParam = new ArrayList<>(); public VariantAbstractQuery() { } /** * Build the message content setting the query string * according to the Variant specific implementation * * @param msg the message object we need to modify * @param query the query string we need to set inside the message */ protected abstract void buildMessage(HttpMessage msg, String query); /** * Return escaped mutate of the value. To be overridden by subclass. * * @param msg * @param value * @return the escaped value */ protected abstract String getEscapedValue(HttpMessage msg, String value); /** * Gets parameter's name in encoded/escaped form. * <p> * Default implementation is to URL encode the name. * * @param msg the message that contains the parameter * @param name the name to escape * @return the escaped name * @since 2.5.0 * @see URLEncoder#encode(String, String) */ protected String getEscapedName(HttpMessage msg, String name) { return name != null ? AbstractPlugin.getURLEncode(name) : ""; } /** * Return unescaped mutate of the value. To be overridden by subclass. * * @param value * @return the unescaped value */ protected abstract String getUnescapedValue(String value); /** * @deprecated (2.5.0) use {@link #setParameters(int, List)} instead. */ @Deprecated @SuppressWarnings("javadoc") protected void setParams(int type, Map<String, String> params) { int i = 0; for (Entry<String, String> param : params.entrySet()) { listParam.add(new NameValuePair(type, param.getKey(), getUnescapedValue(param.getValue()), i)); i++; } } /** * Sets the given {@code parameters} of the given {@code type} as the list of parameters handled by this variant. * <p> * The names and values of the parameters are expected to be in decoded form. * * @param type the type of parameters * @param parameters the actual parameters to add * @since 2.5.0 * @see #getParamList() * @see NameValuePair#TYPE_QUERY_STRING * @see NameValuePair#TYPE_POST_DATA */ protected void setParameters(int type, List<org.zaproxy.zap.model.NameValuePair> parameters) { listParam.clear(); int i = 0; for (org.zaproxy.zap.model.NameValuePair parameter : parameters) { listParam.add(new NameValuePair(type, nonNullString(parameter.getName()), nonNullString(parameter.getValue()), i)); i++; } if (i == 0) { // No query params, lets add one just to make sure listParam.add(new NameValuePair(type, "query", "query", i)); } } private static String nonNullString(String string) { if (string == null) { return ""; } return string; } @Override public List<NameValuePair> getParamList() { return listParam; } /** * If name and value = null, not to append entire parameter. */ @Override public String setParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) { return this.setParameter(msg, originalPair, name, value, false); } @Override public String setEscapedParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) { return this.setParameter(msg, originalPair, name, value, true); } private String setParameter(HttpMessage msg, NameValuePair originalPair, String name, String value, boolean escaped) { // We need the correct parameter parser to use the right separators ParameterParser parser; if (originalPair.getType() == NameValuePair.TYPE_POST_DATA) { parser = Model.getSingleton().getSession().getFormParamParser(msg.getRequestHeader().getURI().toString()); } else { parser = Model.getSingleton().getSession().getUrlParamParser(msg.getRequestHeader().getURI().toString()); } StringBuilder sb = new StringBuilder(); String encodedValue = (escaped) ? value : getEscapedValue(msg, value); NameValuePair pair; boolean isAppended; for (int i = 0; i < getParamList().size(); i++) { pair = getParamList().get(i); if (i == originalPair.getPosition()) { isAppended = paramAppend(sb, getEscapedName(msg, name), encodedValue, parser); } else { isAppended = paramAppend(sb, getEscapedName(msg, pair.getName()), getEscapedValue(msg, pair.getValue()), parser); } if (isAppended && i < getParamList().size() - 1) { sb.append(parser.getDefaultKeyValuePairSeparator()); } } if (sb.length() == 0) { // No original query string sb.append(encodedValue); } String query = sb.toString(); buildMessage(msg, query); return query; } /** * Set the name value pair into the StringBuilder. If both name and value is * null, not to append whole parameter. * * @param sb * @param name Null = not to append parameter. * @param value null = not to append parameter value. * @return true = parameter changed. */ private boolean paramAppend(StringBuilder sb, String name, String value, ParameterParser parser) { boolean isEdited = false; if (name != null) { sb.append(name); isEdited = true; } if (value != null) { sb.append(parser.getDefaultKeyValueSeparator()); sb.append(value); isEdited = true; } return isEdited; } }