org.zaproxy.zap.model.StandardParameterParser.java Source code

Java tutorial

Introduction

Here is the source code for org.zaproxy.zap.model.StandardParameterParser.java

Source

/*
 * Zed Attack Proxy (ZAP) and its related class files.
 * 
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 * 
 * 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.zaproxy.zap.model;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.log4j.Logger;
import org.parosproxy.paros.network.HtmlParameter;
import org.parosproxy.paros.network.HttpMessage;

public class StandardParameterParser implements ParameterParser {

    private static final String CONFIG_KV_PAIR_SEPARATORS = "kvps";
    private static final String CONFIG_KV_SEPARATORS = "kvs";
    private static final String CONFIG_STRUCTURAL_PARAMS = "struct";

    private Context context;
    private Pattern keyValuePairSeparatorPattern;
    private Pattern keyValueSeparatorPattern;
    private String keyValuePairSeparators;
    private String keyValueSeparators;
    private List<String> structuralParameters = new ArrayList<String>();

    private static Logger log = Logger.getLogger(StandardParameterParser.class);

    public StandardParameterParser(String keyValuePairSeparators, String keyValueSeparators)
            throws PatternSyntaxException {
        super();
        this.setKeyValuePairSeparators(keyValuePairSeparators);
        this.setKeyValueSeparators(keyValueSeparators);
    }

    public StandardParameterParser() {
        this("&", "=");
    }

    private Pattern getKeyValuePairSeparatorPattern() {
        return this.keyValuePairSeparatorPattern;
    }

    private Pattern getKeyValueSeparatorPattern() {
        return this.keyValueSeparatorPattern;
    }

    @Override
    public void init(String config) {
        try {
            JSONObject json = JSONObject.fromObject(config);
            this.setKeyValuePairSeparators(json.getString(CONFIG_KV_PAIR_SEPARATORS));
            this.setKeyValueSeparators(json.getString(CONFIG_KV_SEPARATORS));
            JSONArray ja = json.getJSONArray(CONFIG_STRUCTURAL_PARAMS);
            for (Object obj : ja.toArray()) {
                this.structuralParameters.add(obj.toString());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public String getConfig() {
        JSONObject json = new JSONObject();
        json.put(CONFIG_KV_PAIR_SEPARATORS, this.getKeyValuePairSeparators());
        json.put(CONFIG_KV_SEPARATORS, this.getKeyValueSeparators());

        JSONArray ja = new JSONArray();
        ja.addAll(this.structuralParameters);
        json.put(CONFIG_STRUCTURAL_PARAMS, ja);

        return json.toString();
    }

    @Override
    public Map<String, String> getParams(HttpMessage msg, HtmlParameter.Type type) {
        Map<String, String> map = new HashMap<String, String>();
        if (msg == null) {
            return map;
        }
        try {
            switch (type) {
            case form:
                return this.parse(msg.getRequestBody().toString());
            case url:
                return this.parse(msg.getRequestHeader().getURI().getQuery());
            default:
                throw new InvalidParameterException("Type not supported: " + type);
            }
        } catch (URIException e) {
            log.error(e.getMessage(), e);
        }
        return map;
    }

    private void setKeyValueSeparatorPattern(Pattern keyValueSeparatorPattern) {
        this.keyValueSeparatorPattern = keyValueSeparatorPattern;
    }

    private void setKeyValuePairSeparatorPattern(Pattern keyValuePairSeparatorPattern) {
        this.keyValuePairSeparatorPattern = keyValuePairSeparatorPattern;
    }

    public String getKeyValuePairSeparators() {
        return keyValuePairSeparators;
    }

    public void setKeyValuePairSeparators(String keyValuePairSeparators) throws PatternSyntaxException {
        this.setKeyValuePairSeparatorPattern(Pattern.compile("[" + keyValuePairSeparators + "]"));
        this.keyValuePairSeparators = keyValuePairSeparators;
    }

    public String getKeyValueSeparators() {
        return keyValueSeparators;
    }

    public void setKeyValueSeparators(String keyValueSeparators) throws PatternSyntaxException {
        this.setKeyValueSeparatorPattern(Pattern.compile("[" + keyValueSeparators + "]"));
        this.keyValueSeparators = keyValueSeparators;
    }

    @Override
    public String getDefaultKeyValuePairSeparator() {
        if (this.keyValuePairSeparators != null && this.keyValuePairSeparators.length() > 0) {
            return this.keyValuePairSeparators.substring(0, 1);
        }
        // The default
        return "&";
    }

    @Override
    public String getDefaultKeyValueSeparator() {
        if (this.keyValueSeparators != null && this.keyValueSeparators.length() > 0) {
            return this.keyValueSeparators.substring(0, 1);
        }
        // The default
        return "=";
    }

    public List<String> getStructuralParameters() {
        return Collections.unmodifiableList(structuralParameters);
    }

    public void setStructuralParameters(List<String> structuralParameters) {
        this.structuralParameters.clear();
        this.structuralParameters.addAll(structuralParameters);
    }

    @Override
    public Map<String, String> parse(String paramStr) {
        Map<String, String> map = new HashMap<String, String>();

        if (paramStr != null) {
            String[] keyValue = this.getKeyValuePairSeparatorPattern().split(paramStr);
            for (int i = 0; i < keyValue.length; i++) {
                try {
                    String[] keyEqValue = this.getKeyValueSeparatorPattern().split(keyValue[i]);
                    if (keyEqValue.length == 1) {
                        map.put(keyEqValue[0], "");
                    } else if (keyEqValue.length > 1) {
                        map.put(keyEqValue[0], keyEqValue[1]);
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return map;
    }

    @Override
    public StandardParameterParser clone() {
        StandardParameterParser spp = new StandardParameterParser(this.getKeyValuePairSeparators(),
                this.getKeyValueSeparators());
        spp.setStructuralParameters(this.getStructuralParameters());
        return spp;
    }

    @Override
    public List<String> getTreePath(URI uri) throws URIException {
        return this.getTreePath(uri, true);
    }

    private List<String> getTreePath(URI uri, boolean incStructParams) throws URIException {
        String path = uri.getPath();
        List<String> list = new ArrayList<String>();
        if (path != null) {
            Context context = this.getContext();
            if (context != null) {
                String uriStr = uri.toString();
                boolean changed = false;
                for (StructuralNodeModifier ddn : context.getDataDrivenNodes()) {
                    Matcher m = ddn.getPattern().matcher(uriStr);
                    if (m.find()) {
                        if (m.groupCount() == 3) {
                            path = m.group(1) + SessionStructure.DATA_DRIVEN_NODE_PREFIX + ddn.getName()
                                    + SessionStructure.DATA_DRIVEN_NODE_POSTFIX + m.group(3);
                            if (!path.startsWith("/")) {
                                // Should always start with a slash;)
                                path = "/" + path;
                            }
                            changed = true;
                        } else if (m.groupCount() == 2) {
                            path = m.group(1) + SessionStructure.DATA_DRIVEN_NODE_PREFIX + ddn.getName()
                                    + SessionStructure.DATA_DRIVEN_NODE_POSTFIX;
                            if (!path.startsWith("/")) {
                                // Should always start with a slash;)
                                path = "/" + path;
                            }
                            changed = true;
                        }
                    }
                }
                if (changed) {
                    log.debug("Changed path from " + uri.getPath() + " to " + path);
                }
            }

            // Note: Start from the 2nd path element as the first on is always the empty string due
            // to the split
            String[] pathList = path.split("/");
            for (int i = 1; i < pathList.length; i++) {
                list.add(pathList[i]);
            }
        }
        if (incStructParams) {
            // Add any structural params (url param) in key order
            Map<String, String> urlParams = this.parse(uri.getQuery());
            List<String> keys = new ArrayList<String>(urlParams.keySet());
            Collections.sort(keys);
            for (String key : keys) {
                if (this.structuralParameters.contains(key)) {
                    list.add(urlParams.get(key));
                }
            }
        }

        return list;
    }

    @Override
    public List<String> getTreePath(HttpMessage msg) throws URIException {
        URI uri = msg.getRequestHeader().getURI();

        List<String> list = getTreePath(uri);

        // Add any structural params (form params) in key order
        Map<String, String> formParams = this.parse(msg.getRequestBody().toString());
        List<String> keys = new ArrayList<String>(formParams.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            if (this.structuralParameters.contains(key)) {
                list.add(formParams.get(key));
            }
        }

        return list;
    }

    @Override
    public String getAncestorPath(URI uri, int depth) throws URIException {
        // If the depth is 0, return an empty path
        String path = uri.getPath();
        if (depth == 0 || path == null) {
            return "";
        }
        List<String> pathList = getTreePath(uri, false);

        // Add the 'normal' (plus data driven) path elements 
        // until we finish them or we reach the desired depth
        StringBuilder parentPath = new StringBuilder(path.length());
        for (int i = 0; i < pathList.size() && depth > 0; i++, depth--) {
            String element = pathList.get(i);
            parentPath.append('/');
            if (element.startsWith(SessionStructure.DATA_DRIVEN_NODE_PREFIX)) {
                // Its a data driven node - use the regex pattern instead
                parentPath.append(SessionStructure.DATA_DRIVEN_NODE_REGEX);
            } else {
                parentPath.append(element);
            }
        }
        // If we're done or we have no structural parameters, just return
        if (depth == 0 || structuralParameters.isEmpty()) {
            return parentPath.toString();
        }

        // Add the 'structural params' path elements
        boolean firstElement = true;
        Map<String, String> urlParams = this.parse(uri.getQuery());
        for (Entry<String, String> param : urlParams.entrySet()) {
            if (this.structuralParameters.contains(param.getKey())) {
                if (firstElement) {
                    firstElement = false;
                    parentPath.append('?');
                } else {
                    parentPath.append(keyValuePairSeparators);
                }
                parentPath.append(param.getKey()).append(keyValueSeparators).append(param.getValue());
                if ((--depth) == 0) {
                    break;
                }
            }
        }
        return parentPath.toString();
    }

    @Override
    public void setContext(Context context) {
        this.context = context;
    }

    @Override
    public Context getContext() {
        return context;
    }

}