/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2014 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.List;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.script.ExtensionScript;
import org.zaproxy.zap.extension.script.ScriptWrapper;
/**
* Custom Variant based on an implemented Script
*
* @author yhawke (2014)
*/
public class VariantCustom implements Variant {
ExtensionScript extension = null;
private ScriptWrapper wrapper = null;
private VariantScript script = null;
private final List<NameValuePair> params = new ArrayList<>();
// base64 strings are similar, except they can contain + and /, and end
// with 0 - 2 '=' signs. They are also a multiple of 4 bytes.
private final static Pattern BASE64_PATTERN = Pattern.compile("^[a-zA-Z0-9/+]+={0,2}$");
/**
* Create a new Custom Variant using the specific script
* @param wrapper the script wrapper that need to be set for this Variant
* @param extension the general extension Script object
*/
public VariantCustom(ScriptWrapper wrapper, ExtensionScript extension) {
this.wrapper = wrapper;
this.extension = extension;
if (wrapper != null && extension != null && wrapper.isEnabled()) {
try {
this.script = extension.getInterface(wrapper, VariantScript.class);
if (script == null) {
extension.handleFailedScriptInterface(
wrapper,
Constant.messages.getString("variant.scripts.interface.variant.error", wrapper.getName()));
}
} catch (Exception ex) {
// Catch Exception instead of ScriptException and IOException because script engine implementations
// might throw other exceptions on script errors (e.g. jdk.nashorn.internal.runtime.ECMAException)
this.extension.handleScriptException(wrapper, ex);
}
}
}
/**
* Set the current message that this Variant has to scan
* @param msg the message object (remember Response is not set)
*/
@Override
public void setMessage(HttpMessage msg) {
try {
if (script != null) {
script.parseParameters(this, msg);
}
} catch (Exception e) {
// Catch Exception instead of ScriptException because script engine implementations might
// throw other exceptions on script errors (e.g. jdk.nashorn.internal.runtime.ECMAException)
extension.handleScriptException(wrapper, e);
}
}
/**
* Give back the list of retrieved parameters
* @return the list of parsed parameters
*/
@Override
public List<NameValuePair> getParamList() {
return params;
}
/**
* Support method to get back the name of the n-th parameter
* @param index the index of the requested parameter
* @return the parameter name if exists
*/
public String getParamName(int index) {
return (index < params.size()) ? params.get(index).getName() : null;
}
/**
* Support method to get back the value of the n-th parameter
* @param index the index of the requested parameter
* @return the parameter value if exists
*/
public String getParamValue(int index) {
return (index < params.size()) ? params.get(index).getValue() : null;
}
/**
* Get the number of parameters currently available for this variant
* @return
*/
public int getParamNumber() {
return params.size();
}
/**
* Support method to add a new param to this custom variant
* @param name the param name
* @param value the value of this parameter
* @param type the type of this parameter
*/
public void addParam(String name, String value, int type) {
// Current size usually is equal to the position
params.add(new NameValuePair(type, name, value, params.size()));
}
/**
* Support method to add a new QueryString param to this custom variant
* @param name the param name
* @param value the value of this parameter
*/
public void addParamQuery(String name, String value) {
addParam(name, value, NameValuePair.TYPE_QUERY_STRING);
}
/**
* Support method to add a new PostData param to this custom variant
* @param name the param name
* @param value the value of this parameter
*/
public void addParamPost(String name, String value) {
addParam(name, value, NameValuePair.TYPE_POST_DATA);
}
/**
* Support method to add a new Header param to this custom variant
* @param name the param name
* @param value the value of this parameter
*/
public void addParamHeader(String name, String value) {
addParam(name, value, NameValuePair.TYPE_HEADER);
}
/**
* Support method to encode a string to Base64
* @param value the value that need to be encoded
* @return the encoded string
*/
public String encodeBase64(String value) {
return Base64.encodeBase64String(value.getBytes());
}
/**
* Support method to decode a Base64 string
* @param value the value that need to be decoded
* @return the decoded string
*/
public String decodeBase64(String value) {
return new String(Base64.decodeBase64(value));
}
/**
* Support method to verify if the content is a Base64 string
* @param value the value that need to be checked
* @return true if the value is a Base64 string
*/
public boolean isBase64(String value) {
return (BASE64_PATTERN.matcher(value).matches() && ((value.length() % 4) == 0));
}
@Override
public String setParameter(HttpMessage msg, NameValuePair originalPair, String param, String value) {
return setParameter(msg, param, value, false);
}
@Override
public String setEscapedParameter(HttpMessage msg, NameValuePair originalPair, String param, String value) {
return setParameter(msg, param, value, true);
}
/**
* Inner method for correct scripting
* @param msg the message that need to be modified
* @param paramName the name of the parameter
* @param value the value thta should be set for this parameter
* @param escaped true if the parameter has been already escaped
* @return the value set as parameter
*/
private String setParameter(HttpMessage msg, String paramName, String value, boolean escaped) {
try {
if (script != null) {
script.setParameter(this, msg, paramName, value, escaped);
}
} catch (Exception e) {
// Catch Exception instead of ScriptException because script engine implementations might
// throw other exceptions on script errors (e.g. jdk.nashorn.internal.runtime.ECMAException)
extension.handleScriptException(wrapper, e);
}
return value;
}
}