All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.parosproxy.paros.CommandLine Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 *
 * 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: 2011/04/16 Support for running ZAP as a daemon
// ZAP: 2012/03/15 Removed unnecessary castings from methods parse, getArgument and getHelp.
//      Changed to use the class StringBuilder instead of the class StringBuffer in the method getHelp.
// ZAP: 2012/10/15 Issue 397: Support weekly builds
// ZAP: 2013/03/03 Issue 546: Remove all template Javadoc comments
// ZAP: 2013/03/20 Issue 568: Allow extensions to run from the command line
// ZAP: 2013/08/30 Issue 775: Allow host to be set via the command line
// ZAP: 2013/12/03 Issue 933: Automatically determine install dir
// ZAP: 2013/12/03 Issue 934: Handle files on the command line via extension
// ZAP: 2014/01/17 Issue 987: Allow arbitrary config file values to be set via the command line
// ZAP: 2014/05/20 Issue 1191: Cmdline session params have no effect
// ZAP: 2015/04/02 Issue 321: Support multiple databases and Issue 1582: Low memory option
// ZAP: 2015/10/06 Issue 1962: Install and update add-ons from the command line
// ZAP: 2016/08/19 Issue 2782: Support -configfile
// ZAP: 2016/09/22 JavaDoc tweaks
// ZAP: 2016/11/07 Allow to disable default standard output logging
// ZAP: 2017/03/26 Allow to obtain configs in the order specified
// ZAP: 2017/05/12 Issue 3460: Support -suppinfo 
// ZAP: 2017/05/31 Handle null args and include a message in all exceptions.
// ZAP: 2017/08/31 Use helper method I18N.getString(String, Object...).
// ZAP: 2017/11/21 Validate that -cmd and -daemon are not set at the same time (they are mutually exclusive).

package org.parosproxy.paros;

import java.io.File;
import java.io.FileInputStream;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.parosproxy.paros.extension.CommandLineArgument;
import org.parosproxy.paros.extension.CommandLineListener;
import org.parosproxy.paros.network.HttpSender;
import org.zaproxy.zap.ZAP;

public class CommandLine {

	private static final Logger logger = Logger.getLogger(CommandLine.class);

    // ZAP: Made public
    public static final String SESSION = "-session";
    public static final String NEW_SESSION = "-newsession";
    public static final String DAEMON = "-daemon";
    public static final String HELP = "-help";
    public static final String HELP2 = "-h";
    public static final String DIR = "-dir";
    public static final String VERSION = "-version";
    public static final String PORT = "-port";
    public static final String HOST = "-host";
    public static final String CMD = "-cmd";
    public static final String INSTALL_DIR = "-installdir";
    public static final String CONFIG = "-config";
    public static final String CONFIG_FILE = "-configfile";
    public static final String LOWMEM = "-lowmem";
    public static final String EXPERIMENTALDB = "-experimentaldb";
    public static final String SUPPORT_INFO = "-suppinfo";

    /**
     * Command line option to disable the default logging through standard output.
     * 
     * @see #isNoStdOutLog()
     * @since 2.6.0
     */
    public static final String NOSTDOUT = "-nostdout";

    static final String NO_USER_AGENT = "-nouseragent";
    static final String SP = "-sp";

    private boolean GUI = true;
    private boolean daemon = false;
    private boolean reportVersion = false;
    private boolean displaySupportInfo = false;
    private boolean lowMem = false;
    private boolean experimentalDb = false;
    private int port = -1;
    private String host = null;
    private String[] args;
    private final Map configs = new LinkedHashMap<>();
    private final Hashtable keywords = new Hashtable<>();
    private List commandList = null;

    /**
     * Flag that indicates whether or not the default logging through standard output should be disabled.
     */
    private boolean noStdOutLog;

    public CommandLine(String[] args) throws Exception {
        this.args = args == null ? new String[0] : args;
        parseFirst(this.args);

        if (isEnabled(CommandLine.CMD) && isEnabled(CommandLine.DAEMON)) {
            throw new IllegalArgumentException(
                    "Command line arguments " + CommandLine.CMD + " and " + CommandLine.DAEMON
                            + " cannot be used at the same time.");
        }
    }

    private boolean checkPair(String[] args, String paramName, int i) throws Exception {
        String key = args[i];
        String value = null;
        if (key == null) {
            return false;
        }
        
        if (key.equalsIgnoreCase(paramName)) {
            value = args[i + 1];
            if (value == null) {
                throw new Exception("Missing parameter for keyword '" + paramName + "'.");
            }
            
            keywords.put(paramName, value);
            args[i] = null;
            args[i + 1] = null;
            return true;
        }
        
        return false;
    }

    private boolean checkSwitch(String[] args, String paramName, int i) throws Exception {
        String key = args[i];
        if (key == null) {
            return false;
        }
        if (key.equalsIgnoreCase(paramName)) {
            keywords.put(paramName, "");
            args[i] = null;
            return true;
        }
        return false;
    }

    private void parseFirst(String[] args) throws Exception {

        for (int i = 0; i < args.length; i++) {

            if (parseSwitchs(args, i)) {
                continue;
            }
            if (parseKeywords(args, i)) {
                continue;
            }

        }

    }

    public void parse(List commandList, Map extMap) throws Exception {
        this.commandList = commandList;
        CommandLineArgument lastArg = null;
        boolean found = false;
        int remainingValueCount = 0;

        for (int i = 0; i < args.length; i++) {
            if (args[i] == null) {
                continue;
            }
            
            found = false;

            for (int j = 0; j < commandList.size() && !found; j++) {
                CommandLineArgument[] extArg = commandList.get(j);
                for (int k = 0; k < extArg.length && !found; k++) {
                    if (args[i].compareToIgnoreCase(extArg[k].getName()) == 0) {

                        // check if previous keyword satisfied its required no. of parameters
                        if (remainingValueCount > 0) {
                            throw new Exception("Missing parameters for keyword '" + lastArg.getName() + "'.");
                        }

                        // process this keyword
                        lastArg = extArg[k];
                        lastArg.setEnabled(true);
                        found = true;
                        args[i] = null;
                        remainingValueCount = lastArg.getNumOfArguments();
                    }
                }
            }

            // check if current string is a keyword preceded by '-'
            if (args[i] != null && args[i].startsWith("-")) {
                continue;
            }

            // check if there is no more expected param value
            if (lastArg != null && remainingValueCount == 0) {
                continue;
            }

            // check if consume remaining for last matched keywords
            if (!found && lastArg != null) {
                if (lastArg.getPattern() == null || lastArg.getPattern().matcher(args[i]).find()) {
                    lastArg.getArguments().add(args[i]);
                    if (remainingValueCount > 0) {
                        remainingValueCount--;
                    }
                    args[i] = null;
                } else {
                    throw new Exception(lastArg.getErrorMessage());
                }
            }

        }

        // check if the last keyword satified its no. of parameters.
        if (lastArg != null && remainingValueCount > 0) {
            throw new Exception("Missing parameters for keyword '" + lastArg.getName() + "'.");
        }

        // check for supported extensions
        for (int i = 0; i < args.length; i++) {
            if (args[i] == null) {
                continue;
            }
            int dotIndex = args[i].lastIndexOf(".");
            if (dotIndex < 0) {
                // Only support files with extensions
                continue;
            }
            File file = new File(args[i]);
            if (!file.exists() || !file.canRead()) {
                // Not there or cant read .. move on
                continue;
            }

            String ext = args[i].substring(dotIndex + 1);
            CommandLineListener cll = extMap.get(ext);
            if (cll != null) {
                if (cll.handleFile(file)) {
                    found = true;
                    args[i] = null;
                }
            }
        }

        // check if there is some unknown keywords or parameters
        for (String arg : args) {
            if (arg != null) {
                if (arg.startsWith("-")) {
                    throw new Exception(Constant.messages.getString("start.cmdline.badparam", arg));
                    
                } else {
                    // Assume they were trying to specify a file
                    File f = new File(arg);
                    if (!f.exists()) {
                        throw new Exception(Constant.messages.getString("start.cmdline.nofile", arg));
                    
                    } else if (!f.canRead()) {
                        throw new Exception(Constant.messages.getString("start.cmdline.noread", arg));
                    
                    } else {
                        // We probably dont handle this sort of file
                        throw new Exception(Constant.messages.getString("start.cmdline.badfile", arg));
                    }
                }
            }
        }
    }

    private boolean parseSwitchs(String[] args, int i) throws Exception {

        boolean result = false;

        if (checkSwitch(args, NO_USER_AGENT, i)) {
            HttpSender.setUserAgent("");
            Constant.setEyeCatcher("");
            result = true;

        } else if (checkSwitch(args, SP, i)) {
            Constant.setSP(true);
            result = true;
            
        } else if (checkSwitch(args, CMD, i)) {
            setDaemon(false);
            setGUI(false);
            
        } else if (checkSwitch(args, DAEMON, i)) {
            setDaemon(true);
            setGUI(false);
            
        } else if (checkSwitch(args, LOWMEM, i)) {
            setLowMem(true);
            
        } else if (checkSwitch(args, EXPERIMENTALDB, i)) {
            setExperimentalDb(true);
            
        } else if (checkSwitch(args, HELP, i)) {
            result = true;
            setGUI(false);
            
        } else if (checkSwitch(args, HELP2, i)) {
            result = true;
            setGUI(false);
            
        } else if (checkSwitch(args, VERSION, i)) {
            reportVersion = true;
            setDaemon(false);
            setGUI(false);
        } else if (checkSwitch(args, NOSTDOUT, i)) {
            noStdOutLog = true;
        } else if (checkSwitch(args, SUPPORT_INFO, i)) {
            displaySupportInfo = true;
            setDaemon(false);
            setGUI(false);
        }

        return result;
    }

    private boolean parseKeywords(String[] args, int i) throws Exception {
        boolean result = false;
        if (checkPair(args, NEW_SESSION, i)) {
            result = true;
            
        } else if (checkPair(args, SESSION, i)) {
            result = true;
            
        } else if (checkPair(args, DIR, i)) {
            Constant.setZapHome(keywords.get(DIR));
            result = true;
            
        } else if (checkPair(args, INSTALL_DIR, i)) {
            Constant.setZapInstall(keywords.get(INSTALL_DIR));
            result = true;
            
        } else if (checkPair(args, HOST, i)) {
            this.host = keywords.get(HOST);
            result = true;
            
        } else if (checkPair(args, PORT, i)) {
            this.port = Integer.parseInt(keywords.get(PORT));
            result = true;
            
        } else if (checkPair(args, CONFIG, i)) {
            String pair = keywords.get(CONFIG);
            if (pair != null && pair.indexOf("=") > 0) {
                int eqIndex = pair.indexOf("=");
                this.configs.put(pair.substring(0, eqIndex), pair.substring(eqIndex + 1));
                result = true;
            }
        } else if (checkPair(args, CONFIG_FILE, i)) {
            String conf = keywords.get(CONFIG_FILE);
            File confFile = new File(conf);
            if (! confFile.isFile()) {
                // We cant use i18n here as the messages wont have been loaded
                throw new Exception("No such file: " + confFile.getAbsolutePath());
            } else if (! confFile.canRead()) {
                // We cant use i18n here as the messages wont have been loaded
                throw new Exception("File not readable: " + confFile.getAbsolutePath());
            }
            Properties prop = new Properties();
            try (FileInputStream inStream = new FileInputStream(confFile)) {
                prop.load(inStream);
            }
            
            for (Entry keyValue : prop.entrySet()) {
                this.configs.put((String)keyValue.getKey(), (String)keyValue.getValue());
            }
        }
        
        return result;
    }

    /**
     * Tells whether or not ZAP was started with GUI.
     * 
     * @return {@code true} if ZAP was started with GUI, {@code false} otherwise
     */
    public boolean isGUI() {
        return GUI;
    }

    /**
     * Sets whether or not ZAP was started with GUI.
     * 
     * @param GUI {@code true} if ZAP was started with GUI, {@code false} otherwise
     */
    public void setGUI(boolean GUI) {
        this.GUI = GUI;
    }

    public boolean isDaemon() {
        return daemon;
    }

    public void setDaemon(boolean daemon) {
        this.daemon = daemon;
    }

    public boolean isLowMem() {
		return lowMem;
	}

	public void setLowMem(boolean lowMem) {
		this.lowMem = lowMem;
	}

	public boolean isExperimentalDb() {
		return experimentalDb;
	}

	public void setExperimentalDb(boolean experimentalDb) {
		this.experimentalDb = experimentalDb;
	}

	public boolean isReportVersion() {
        return this.reportVersion;
    }

    public boolean isDisplaySupportInfo() {
        return this.displaySupportInfo;
    }

    public int getPort() {
        return this.port;
    }

    public String getHost() {
        return host;
    }

    /**
     * Gets the {@code config} command line arguments, in no specific order.
     *
     * @return the {@code config} command line arguments.
     * @deprecated (2.6.0) Use {@link #getOrderedConfigs()} instead, which are in the order they were specified.
     */
    @Deprecated
    public Hashtable getConfigs() {
        return new Hashtable<>(configs);
    }

    /**
     * Gets the {@code config} command line arguments, in the order they were specified.
     *
     * @return the {@code config} command line arguments.
     * @since 2.6.0
     */
    public Map getOrderedConfigs() {
        return configs;
    }

    public String getArgument(String keyword) {
        return keywords.get(keyword);
    }

    public String getHelp() {
    	return CommandLine.getHelp(commandList);
    }

    /**
     * Tells whether or not the default logging through standard output should be disabled.
     *
     * @return {@code true} if the default logging through standard output should be disabled, {@code false} otherwise.
     * @since 2.6.0
     */
    public boolean isNoStdOutLog() {
        return noStdOutLog;
    }

    public static String getHelp(List cmdList) {
        String zap;
        if (Constant.isWindows()) {
            zap = "zap.bat";
            
        } else {
            zap = "zap.sh";
        }
        StringBuilder sb = new StringBuilder();
        sb.append(Constant.messages.getString("cmdline.help", zap));

        if (cmdList != null) {
	        for (CommandLineArgument[] extArgs : cmdList) {
	            for (CommandLineArgument extArg : extArgs) {
	                sb.append("\t");
	                sb.append(extArg.getHelpMessage()).append("\n");
	            }
	        }
        }
        return sb.toString();
    }

    public boolean isEnabled(String keyword) {
        Object obj = keywords.get(keyword);
        return (obj != null) && (obj instanceof String);
    }
    
    /**
     * A method for reporting informational messages in {@link CommandLineListener#execute(CommandLineArgument[])}
     * implementations. It ensures that messages are written to the log file and/or written to stdout as appropriate.
     * @param str the informational message
     */
    public static void info(String str) {
    	switch (ZAP.getProcessType()) {
    	case cmdline:	System.out.println(str); break;
    	default:		// Ignore
    	}
    	// Always write to the log
    	logger.info(str);
    }
    
    /**
     * A method for reporting error messages in {@link CommandLineListener#execute(CommandLineArgument[])} implementations.
     * It ensures that messages are written to the log file and/or written to stderr as appropriate.
     * @param str the error message
     */
    public static void error(String str) {
    	switch (ZAP.getProcessType()) {
    	case cmdline:	System.err.println(str); break;
    	default:		// Ignore
    	}
    	// Always write to the log
		logger.error(str);
    }
    
    /**
     * A method for reporting error messages in {@link CommandLineListener#execute(CommandLineArgument[])} implementations.
     * It ensures that messages are written to the log file and/or written to stderr as appropriate.
     * @param str the error message
     * @param e the cause of the error
     */
    public static void error(String str, Throwable e) {
    	switch (ZAP.getProcessType()) {
    	case cmdline:	System.err.println(str); break;
    	default:		// Ignore
    	}
    	// Always write to the log
		logger.error(str, e);
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy