/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2013 The ZAP Development team * * 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 org.parosproxy.paros.core.scanner; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import org.parosproxy.paros.network.HttpHeader; import org.parosproxy.paros.network.HttpMessage; /** * Abstract class for HTTP RPC request handling * * @author andy */ public abstract class VariantAbstractRPCQuery implements Variant { private final Logger logger = Logger.getLogger(this.getClass()); private final List<RPCParameter> listParam = new ArrayList<>(); private final List<NameValuePair> params = new ArrayList<>(); private String requestContent; @Override public void setMessage(HttpMessage msg) { // First check if it's a gwt rpc form data request // Otherwise give back an empty param list String contentType = msg.getRequestHeader().getHeader(HttpHeader.CONTENT_TYPE); if (contentType != null && isValidContentType(contentType)) { try { setRequestContent(msg.getRequestBody().toString()); } catch (Exception e) { logger.warn("Failed to parse the request body: " + e.getMessage(), e); } } } /** * * @param name the name of the parameter * @param beginOffset the begin offset of the parameter value inside the RPC content body * @param endOffset the ending offset of the parameter value inside the RPC content body * @param toQuote the parameter need to be quoted when used * @param escaped the parameter value should be escaped so it has to be unescaped before */ public void addParameter(String name, int beginOffset, int endOffset, boolean toQuote, boolean escaped) { RPCParameter param = new RPCParameter(); String value = requestContent.substring(beginOffset, endOffset); param.setName(name); param.setValue((escaped) ? getUnescapedValue(value) : value); param.setBeginOffset(beginOffset); param.setEndOffset(endOffset); param.setToQuote(toQuote); listParam.add(param); } /** * * @param name the name of the parameter * @param beginOffset the begin offset of the parameter value inside the RPC content body * @param endOffset the ending offset of the parameter value inside the RPC content body * @param toQuote the parameter need to be quoted when used * @param value the value that need to be set */ public void addParameter(String name, int beginOffset, int endOffset, boolean toQuote, String value) { RPCParameter param = new RPCParameter(); param.setName(name); param.setValue(value); param.setBeginOffset(beginOffset); param.setEndOffset(endOffset); param.setToQuote(toQuote); listParam.add(param); } /** * * @param beginOffset * @param endOffset * @return */ public String getToken(int beginOffset, int endOffset) { return requestContent.substring(beginOffset, endOffset); } /** * * @return */ @Override public List<NameValuePair> getParamList() { return params; } /** * * @param msg * @param originalPair * @param name * @param value * @return */ @Override public String setParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) { return this.setParameter(msg, originalPair, name, value, false); } /** * * @param msg * @param originalPair * @param name * @param value * @return */ @Override public String setEscapedParameter(HttpMessage msg, NameValuePair originalPair, String name, String value) { return this.setParameter(msg, originalPair, name, value, true); } /** * * @param msg * @param originalPair * @param name * @param value * @param escaped * @return */ private String setParameter(HttpMessage msg, NameValuePair originalPair, String name, String value, boolean escaped) { RPCParameter param = listParam.get(originalPair.getPosition()); StringBuilder sb = new StringBuilder(); sb.append(requestContent.substring(0, param.getBeginOffset())); sb.append(escaped ? value : getEscapedValue(value, param.isToQuote())); sb.append(requestContent.substring(param.getEndOffset())); String query = sb.toString(); msg.getRequestBody().setBody(query); return query; } /** * * @param requestContent */ protected void setRequestContent(String requestContent) { this.requestContent = requestContent; parseContent(requestContent); // Put each parameter in order by beginOffset // so that we can inject payloads // following the request order // -------------------------------------- Collections.sort(listParam); for (int i = 0; i < listParam.size(); i++) { RPCParameter param = listParam.get(i); params.add(new NameValuePair(NameValuePair.TYPE_POST_DATA, param.getName(), param.getValue(), i)); } } /** * * @return */ public String getReadableParametrizedQuery() { StringBuilder result = new StringBuilder(); int begin = 0; int end; for (RPCParameter param : listParam) { end = param.getBeginOffset(); result.append(requestContent.substring(begin, end)); result.append("__INJECTABLE_PARAM__"); begin = param.getEndOffset(); } result.append(requestContent.substring(begin)); return result.toString(); } /** * * @param contentType * @return */ public abstract boolean isValidContentType(String contentType); /** * * @param content */ public abstract void parseContent(String content); /** * * @param value * @param toQuote * @return */ public abstract String getEscapedValue(String value, boolean toQuote); /** * * @param value * @return */ public abstract String getUnescapedValue(String value); /** * Inner support class */ protected class RPCParameter implements Comparable<RPCParameter> { private String name; private String value; private int beginOffset; private int endOffset; private boolean toQuote; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public int getBeginOffset() { return beginOffset; } public void setBeginOffset(int beginOffset) { this.beginOffset = beginOffset; } public int getEndOffset() { return endOffset; } public void setEndOffset(int endOffset) { this.endOffset = endOffset; } public boolean isToQuote() { return toQuote; } public void setToQuote(boolean toQuote) { this.toQuote = toQuote; } @Override public int compareTo(RPCParameter t) { return this.beginOffset - t.beginOffset; } } }