org.zaproxy.zap.extension.ascan.CustomScanDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.zaproxy.zap.extension.ascan.CustomScanDialog.java

Source

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

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.ListCellRenderer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.Highlighter.Highlight;
import javax.swing.text.Highlighter.HighlightPainter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConfigurationUtils;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.core.scanner.Category;
import org.parosproxy.paros.core.scanner.ScannerParam;
import org.parosproxy.paros.core.scanner.VariantUserDefined;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.OptionsParam;
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.model.SiteNode;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.view.AbstractParamContainerPanel;
import org.zaproxy.zap.extension.users.ExtensionUserManagement;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.model.StructuralNode;
import org.zaproxy.zap.model.StructuralSiteNode;
import org.zaproxy.zap.model.Target;
import org.zaproxy.zap.model.Tech;
import org.zaproxy.zap.model.TechSet;
import org.zaproxy.zap.users.User;
import org.zaproxy.zap.utils.ZapTextArea;
import org.zaproxy.zap.view.JCheckBoxTree;
import org.zaproxy.zap.view.LayoutHelper;
import org.zaproxy.zap.view.StandardFieldsDialog;

public class CustomScanDialog extends StandardFieldsDialog {

    protected static final String[] STD_TAB_LABELS = { "ascan.custom.tab.scope", "ascan.custom.tab.input",
            "ascan.custom.tab.custom", "ascan.custom.tab.tech", "ascan.custom.tab.policy" };

    private static final String FIELD_START = "ascan.custom.label.start";
    private static final String FIELD_POLICY = "ascan.custom.label.policy";
    private static final String FIELD_CONTEXT = "ascan.custom.label.context";
    private static final String FIELD_USER = "ascan.custom.label.user";
    private static final String FIELD_RECURSE = "ascan.custom.label.recurse";
    private static final String FIELD_ADVANCED = "ascan.custom.label.adv";

    private static final String FIELD_DISABLE_VARIANTS_MSG = "variant.options.disable";

    private static final Logger logger = Logger.getLogger(CustomScanDialog.class);
    private static final long serialVersionUID = 1L;

    private JButton[] extraButtons = null;
    private ExtensionActiveScan extension = null;

    private final ExtensionUserManagement extUserMgmt = (ExtensionUserManagement) Control.getSingleton()
            .getExtensionLoader().getExtension(ExtensionUserManagement.NAME);

    private int headerLength = -1;
    // The index of the start of the URL path eg after https://www.example.com:1234/ - no point attacking this
    private int urlPathStart = -1;
    private Target target = null;

    private ScannerParam scannerParam = null;
    private OptionsParam optionsParam = null;

    private JPanel customPanel = null;
    private JPanel techPanel = null;
    private ZapTextArea requestField = null;
    private JButton addCustomButton = null;
    private JButton removeCustomButton = null;
    private JList<Highlight> injectionPointList = null;
    private final DefaultListModel<Highlight> injectionPointModel = new DefaultListModel<>();
    private final JLabel customPanelStatus = new JLabel();
    private JCheckBox disableNonCustomVectors = null;
    private JCheckBoxTree techTree = null;
    private final HashMap<Tech, DefaultMutableTreeNode> techToNodeMap = new HashMap<>();
    private TreeModel techModel = null;
    private String scanPolicyName;
    private ScanPolicy scanPolicy = null;
    private PolicyAllCategoryPanel policyAllCategoryPanel = null;
    private OptionsVariantPanel variantPanel = null;
    private List<PolicyCategoryPanel> categoryPanels = Collections.emptyList();
    private List<CustomScanPanel> customPanels = null;
    private boolean showingAdvTabs = true;

    public CustomScanDialog(ExtensionActiveScan ext, String[] tabLabels, List<CustomScanPanel> customPanels,
            Frame owner, Dimension dim) {
        super(owner, "ascan.custom.title", dim, tabLabels);

        this.extension = ext;
        this.customPanels = customPanels;

        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosed(WindowEvent e) {
                scanPolicy = null;
            }
        });

        // The first time init to the default options set, after that keep own copies
        reset(false);
    }

    public void init(Target target) {
        if (target != null) {
            // If one isnt specified then leave the previously selected one
            this.target = target;
        }

        logger.debug("init " + this.target);

        this.removeAllFields();
        this.injectionPointModel.clear();
        this.headerLength = -1;
        this.urlPathStart = -1;

        if (scanPolicyName != null && PolicyManager.policyExists(scanPolicyName)) {
            try {
                scanPolicy = extension.getPolicyManager().getPolicy(scanPolicyName);
            } catch (ConfigurationException e) {
                logger.warn("Failed to load scan policy (" + scanPolicyName + "):", e);
            }
        }

        if (scanPolicy == null) {
            scanPolicy = extension.getPolicyManager().getDefaultScanPolicy();
            scanPolicyName = scanPolicy.getName();
        }

        this.addTargetSelectField(0, FIELD_START, this.target, false, false);
        this.addComboField(0, FIELD_POLICY, extension.getPolicyManager().getAllPolicyNames(), scanPolicy.getName());
        this.addComboField(0, FIELD_CONTEXT, new String[] {}, "");
        this.addComboField(0, FIELD_USER, new String[] {}, "");
        this.addCheckBoxField(0, FIELD_RECURSE, true);
        // This option is always read from the 'global' options
        this.addCheckBoxField(0, FIELD_ADVANCED, extension.getScannerParam().isShowAdvancedDialog());

        this.addFieldListener(FIELD_POLICY, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                policySelected();
            }
        });

        this.addPadding(0);

        // Default to Recurse, so always set the warning
        customPanelStatus.setText(Constant.messages.getString("ascan.custom.status.recurse"));

        this.addFieldListener(FIELD_CONTEXT, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setUsers();
                setTech();
            }
        });

        this.addFieldListener(FIELD_RECURSE, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setFieldStates();
            }
        });

        this.addFieldListener(FIELD_ADVANCED, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Save the adv option permanently for next time

                setAdvancedOptions(getBoolValue(FIELD_ADVANCED));
            }
        });

        this.getVariantPanel().initParam(scannerParam);
        this.setCustomTabPanel(1, getVariantPanel());

        // Custom vectors panel
        this.setCustomTabPanel(2, getCustomPanel());

        // Technology panel
        this.setCustomTabPanel(3, getTechPanel());

        // Policy panel
        AbstractParamContainerPanel policyPanel = new AbstractParamContainerPanel(
                Constant.messages.getString("ascan.custom.tab.policy"));

        String[] ROOT = {};

        policyPanel.addParamPanel(null, getPolicyAllCategoryPanel(true), false);

        categoryPanels = new ArrayList<>(Category.getAllNames().length);
        for (int i = 0; i < Category.getAllNames().length; i++) {
            PolicyCategoryPanel panel = new PolicyCategoryPanel(i, this.scanPolicy.getPluginFactory(),
                    scanPolicy.getDefaultThreshold());
            policyPanel.addParamPanel(ROOT, Category.getName(i), panel, true);
            this.categoryPanels.add(panel);
        }

        policyPanel.showDialog(true);

        this.setCustomTabPanel(4, policyPanel);

        // add custom panels
        int cIndex = 5;
        if (this.customPanels != null) {
            for (CustomScanPanel customPanel : this.customPanels) {
                this.setCustomTabPanel(cIndex, customPanel.getPanel(true));
                cIndex++;
            }
        }

        if (target != null) {
            // Set up the fields if a node has been specified, otherwise leave as previously set
            this.populateRequestField(this.target.getStartNode());
            this.targetSelected(FIELD_START, this.target);
            this.setUsers();
            this.setTech();
        }

        this.setAdvancedOptions(extension.getScannerParam().isShowAdvancedDialog());

        this.pack();
    }

    @Override
    public String getHelpIndex() {
        return "ui.dialogs.advascan";
    }

    private PolicyAllCategoryPanel getPolicyAllCategoryPanel(boolean reinit) {
        if (policyAllCategoryPanel == null) {
            policyAllCategoryPanel = new PolicyAllCategoryPanel(this, extension, scanPolicy, true);
            policyAllCategoryPanel.setName(Constant.messages.getString("ascan.custom.tab.policy"));
        } else if (reinit) {
            policyAllCategoryPanel.reloadPolicies();
        }
        return policyAllCategoryPanel;
    }

    private void policySelected() {
        String policyName = getStringValue(FIELD_POLICY);
        try {
            scanPolicy = extension.getPolicyManager().getPolicy(policyName);
            getPolicyAllCategoryPanel(false).setScanPolicy(scanPolicy);
            for (PolicyCategoryPanel panel : this.categoryPanels) {
                panel.setPluginFactory(scanPolicy.getPluginFactory(), scanPolicy.getDefaultThreshold());
            }

            scanPolicyName = policyName;
        } catch (ConfigurationException e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void setAdvancedOptions(boolean adv) {
        this.getField(FIELD_POLICY).setEnabled(!adv);
        if (adv) {
            ((JComboBox<?>) this.getField(FIELD_POLICY))
                    .setToolTipText(Constant.messages.getString("ascan.custom.tooltip.policy"));
        } else {
            ((JComboBox<?>) this.getField(FIELD_POLICY)).setToolTipText("");
        }

        if (showingAdvTabs == adv) {
            // Nothing else to do
            return;
        }
        // Show/hide all except from the first tab
        this.setTabsVisible(new String[] { "ascan.custom.tab.input", "ascan.custom.tab.custom",
                "ascan.custom.tab.tech", "ascan.custom.tab.policy" }, adv);

        if (this.customPanels != null) {
            for (CustomScanPanel customPanel : this.customPanels) {
                this.setTabsVisible(new String[] { customPanel.getLabel() }, adv);
            }
        }

        showingAdvTabs = adv;
        // Always save in the 'global' options
        extension.getScannerParam().setShowAdvancedDialog(adv);
    }

    private void populateRequestField(SiteNode node) {
        try {
            if (node == null || node.getHistoryReference() == null
                    || node.getHistoryReference().getHttpMessage() == null) {
                this.getRequestField().setText("");

            } else {
                // Populate the custom vectors http pane
                HttpMessage msg = node.getHistoryReference().getHttpMessage();
                String header = msg.getRequestHeader().toString();
                StringBuilder sb = new StringBuilder();
                sb.append(header);
                this.headerLength = header.length();
                this.urlPathStart = header.indexOf("/", header.indexOf("://") + 2) + 1; // Ignore <METHOD> http(s)://host:port/
                sb.append(msg.getRequestBody().toString());
                this.getRequestField().setText(sb.toString());

                // Only set the recurse option if the node has children, and disable it otherwise
                JCheckBox recurseChk = (JCheckBox) this.getField(FIELD_RECURSE);
                recurseChk.setEnabled(node.getChildCount() > 0);
                recurseChk.setSelected(node.getChildCount() > 0);
            }

            this.setFieldStates();

        } catch (HttpMalformedHeaderException | DatabaseException e) {
            // 
            this.getRequestField().setText("");
        }

    }

    @Override
    public void targetSelected(String field, Target node) {
        List<String> ctxNames = new ArrayList<>();
        if (node != null) {
            // The user has selected a new node
            this.target = node;
            if (node.getStartNode() != null) {
                populateRequestField(node.getStartNode());

                Session session = Model.getSingleton().getSession();
                List<Context> contexts = session.getContextsForNode(node.getStartNode());
                for (Context context : contexts) {
                    ctxNames.add(context.getName());
                }

            } else if (node.getContext() != null) {
                ctxNames.add(node.getContext().getName());
            }

            this.setTech();
        }

        this.setComboFields(FIELD_CONTEXT, ctxNames, "");
        this.getField(FIELD_CONTEXT).setEnabled(ctxNames.size() > 0);
    }

    private Context getSelectedContext() {
        String ctxName = this.getStringValue(FIELD_CONTEXT);
        if (this.extUserMgmt != null && !this.isEmptyField(FIELD_CONTEXT)) {
            Session session = Model.getSingleton().getSession();
            return session.getContext(ctxName);
        }
        return null;
    }

    private User getSelectedUser() {
        Context context = this.getSelectedContext();
        if (context != null) {
            String userName = this.getStringValue(FIELD_USER);
            List<User> users = this.extUserMgmt.getContextUserAuthManager(context.getIndex()).getUsers();
            for (User user : users) {
                if (userName.equals(user.getName())) {
                    return user;
                }
            }
        }
        return null;
    }

    private void setUsers() {
        Context context = this.getSelectedContext();
        List<String> userNames = new ArrayList<>();
        if (context != null) {
            List<User> users = this.extUserMgmt.getContextUserAuthManager(context.getIndex()).getUsers();
            userNames.add(""); // The default should always be 'not specified'
            for (User user : users) {
                userNames.add(user.getName());
            }
        }
        this.setComboFields(FIELD_USER, userNames, "");
        this.getField(FIELD_USER).setEnabled(userNames.size() > 1); // Theres always 1..
    }

    private void setTech() {
        Context context = this.getSelectedContext();

        TechSet ts = new TechSet(Tech.builtInTech);
        Iterator<Tech> iter = ts.getIncludeTech().iterator();

        DefaultMutableTreeNode root = new DefaultMutableTreeNode(
                Constant.messages.getString("ascan.custom.tab.tech.node"));
        Tech tech;
        DefaultMutableTreeNode parent;
        DefaultMutableTreeNode node;
        while (iter.hasNext()) {
            tech = iter.next();
            if (tech.getParent() != null) {
                parent = techToNodeMap.get(tech.getParent());
            } else {
                parent = null;
            }
            if (parent == null) {
                parent = root;
            }
            node = new DefaultMutableTreeNode(tech.getUiName());
            parent.add(node);
            techToNodeMap.put(tech, node);
        }

        techModel = new DefaultTreeModel(root);
        techTree.setModel(techModel);
        techTree.expandAll();
        // Default to everything set
        TreePath rootTp = new TreePath(root);
        techTree.checkSubTree(rootTp, true);
        techTree.setCheckBoxEnabled(rootTp, false);

        if (context != null) {
            TechSet techSet = context.getTechSet();
            Iterator<Entry<Tech, DefaultMutableTreeNode>> iter2 = techToNodeMap.entrySet().iterator();
            while (iter.hasNext()) {
                Entry<Tech, DefaultMutableTreeNode> nodeEntry = iter2.next();
                TreePath tp = this.getTechPath(nodeEntry.getValue());
                if (techSet.includes(nodeEntry.getKey())) {
                    this.getTechTree().check(tp, true);
                }
            }
        }
    }

    private TreePath getTechPath(TreeNode node) {
        List<TreeNode> list = new ArrayList<>();

        // Add all nodes to list
        while (node != null) {
            list.add(node);
            node = node.getParent();
        }
        Collections.reverse(list);

        // Convert array of nodes to TreePath
        return new TreePath(list.toArray());
    }

    private ZapTextArea getRequestField() {
        if (requestField == null) {
            requestField = new ZapTextArea();
            requestField.setEditable(false);
            requestField.setLineWrap(true);
            requestField.getCaret().setVisible(true);
        }
        return requestField;
    }

    private OptionsVariantPanel getVariantPanel() {
        if (variantPanel == null) {
            variantPanel = new OptionsVariantPanel();
        }

        return variantPanel;
    }

    private JPanel getCustomPanel() {
        if (customPanel == null) {
            customPanel = new JPanel(new GridBagLayout());

            JScrollPane scrollPane = new JScrollPane();
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
            scrollPane.setViewportView(getRequestField());

            JPanel buttonPanel = new JPanel(new GridBagLayout());

            getRequestField().addCaretListener(new CaretListener() {
                @Override
                public void caretUpdate(CaretEvent event) {
                    setFieldStates();
                }
            });

            buttonPanel.add(new JLabel(""), LayoutHelper.getGBC(0, 0, 1, 0.5)); // Spacer
            buttonPanel.add(getAddCustomButton(), LayoutHelper.getGBC(1, 0, 1, 1, 0.0D, 0.0D,
                    GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, new Insets(5, 5, 5, 5)));

            buttonPanel.add(new JLabel(""), LayoutHelper.getGBC(2, 0, 1, 0.5)); // Spacer

            buttonPanel.add(new JLabel(""), LayoutHelper.getGBC(0, 1, 1, 0.5)); // Spacer
            buttonPanel.add(getRemoveCustomButton(), LayoutHelper.getGBC(1, 1, 1, 1, 0.0D, 0.0D,
                    GridBagConstraints.BOTH, GridBagConstraints.NORTHWEST, new Insets(5, 5, 5, 5)));

            buttonPanel.add(new JLabel(""), LayoutHelper.getGBC(2, 1, 1, 0.5)); // Spacer

            JScrollPane scrollPane2 = new JScrollPane(getInjectionPointList());
            scrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

            buttonPanel.add(new JLabel(Constant.messages.getString("ascan.custom.label.vectors")),
                    LayoutHelper.getGBC(0, 2, 3, 0.0D, 0.0D));

            buttonPanel.add(scrollPane2, LayoutHelper.getGBC(0, 3, 3, 1.0D, 1.0D));

            JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane, buttonPanel);
            splitPane.setDividerLocation(550);
            customPanel.add(splitPane, LayoutHelper.getGBC(0, 0, 1, 1, 1.0D, 1.0D));
            customPanel.add(customPanelStatus, LayoutHelper.getGBC(0, 1, 1, 1, 1.0D, 0.0D));
            customPanel.add(getDisableNonCustomVectors(), LayoutHelper.getGBC(0, 2, 1, 1, 1.0D, 0.0D));
        }

        return customPanel;
    }

    private JButton getAddCustomButton() {
        if (addCustomButton == null) {
            addCustomButton = new JButton(Constant.messages.getString("ascan.custom.button.pt.add"));
            addCustomButton.setEnabled(false);

            addCustomButton.addActionListener(new java.awt.event.ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    // Add the selected injection point
                    int userDefStart = getRequestField().getSelectionStart();
                    if (userDefStart >= 0) {
                        int userDefEnd = getRequestField().getSelectionEnd();
                        Highlighter hl = getRequestField().getHighlighter();
                        HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.RED);
                        try {
                            Highlight hlt = (Highlight) hl.addHighlight(userDefStart, userDefEnd, painter);
                            injectionPointModel.addElement(hlt);
                            // Unselect the text
                            getRequestField().setSelectionStart(userDefEnd);
                            getRequestField().setSelectionEnd(userDefEnd);
                            getRequestField().getCaret().setVisible(true);

                        } catch (BadLocationException e1) {
                            logger.error(e1.getMessage(), e1);
                        }
                    }

                }
            });

        }
        return addCustomButton;
    }

    private JButton getRemoveCustomButton() {
        if (removeCustomButton == null) {
            removeCustomButton = new JButton(Constant.messages.getString("ascan.custom.button.pt.rem"));
            removeCustomButton.setEnabled(false);

            removeCustomButton.addActionListener(new java.awt.event.ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    // Remove any selected injection points
                    int userDefStart = getRequestField().getSelectionStart();
                    if (userDefStart >= 0) {
                        int userDefEnd = getRequestField().getSelectionEnd();
                        Highlighter hltr = getRequestField().getHighlighter();
                        Highlight[] hls = hltr.getHighlights();

                        if (hls != null && hls.length > 0) {
                            for (Highlight hl : hls) {
                                if (selectionIncludesHighlight(userDefStart, userDefEnd, hl)) {
                                    hltr.removeHighlight(hl);
                                    injectionPointModel.removeElement(hl);
                                }
                            }
                        }

                        // Unselect the text
                        getRequestField().setSelectionStart(userDefEnd);
                        getRequestField().setSelectionEnd(userDefEnd);
                        getRequestField().getCaret().setVisible(true);
                    }
                }
            });
        }

        return removeCustomButton;
    }

    private JCheckBox getDisableNonCustomVectors() {
        if (disableNonCustomVectors == null) {
            disableNonCustomVectors = new JCheckBox(Constant.messages.getString("ascan.custom.label.disableiv"));
            disableNonCustomVectors.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    // Enable/disable all of the input vector options as appropriate
                    getVariantPanel().setAllInjectableAndRPC(!disableNonCustomVectors.isSelected());

                    if (disableNonCustomVectors.isSelected()) {
                        setFieldValue(FIELD_DISABLE_VARIANTS_MSG,
                                Constant.messages.getString("ascan.custom.warn.disabled"));

                    } else {
                        setFieldValue(FIELD_DISABLE_VARIANTS_MSG, "");
                    }

                }
            });

        }
        return disableNonCustomVectors;
    }

    private JPanel getTechPanel() {
        if (techPanel == null) {
            techPanel = new JPanel(new GridBagLayout());

            JScrollPane scrollPane = new JScrollPane();
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
            scrollPane.setViewportView(getTechTree());
            scrollPane.setBorder(
                    javax.swing.BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.RAISED));

            techPanel.add(scrollPane, LayoutHelper.getGBC(0, 0, 1, 1, 1.0D, 1.0D));
        }

        return techPanel;
    }

    private JCheckBoxTree getTechTree() {
        if (techTree == null) {
            techTree = new JCheckBoxTree() {
                private static final long serialVersionUID = 1L;

                @Override
                protected void setExpandedState(TreePath path, boolean state) {
                    // Ignore all collapse requests; collapse events will not be fired
                    if (state) {
                        super.setExpandedState(path, state);
                    }
                }
            };
            this.setTech();

        }
        return techTree;
    }

    private void setFieldStates() {
        int userDefStart = getRequestField().getSelectionStart();

        if (getBoolValue(FIELD_RECURSE)) {
            // Dont support custom vectors when recursing
            customPanelStatus.setText(Constant.messages.getString("ascan.custom.status.recurse"));
            getAddCustomButton().setEnabled(false);
            getRemoveCustomButton().setEnabled(false);
            getDisableNonCustomVectors().setEnabled(false);

        } else {
            customPanelStatus.setText(Constant.messages.getString("ascan.custom.status.highlight"));
            if (userDefStart >= 0) {
                int userDefEnd = getRequestField().getSelectionEnd();
                if (selectionIncludesHighlight(userDefStart, userDefEnd,
                        getRequestField().getHighlighter().getHighlights())) {
                    getAddCustomButton().setEnabled(false);
                    getRemoveCustomButton().setEnabled(true);

                } else if (userDefStart < urlPathStart) {
                    // No point attacking the method, hostname or port 
                    getAddCustomButton().setEnabled(false);

                } else if (userDefStart < headerLength && userDefEnd > headerLength) {
                    // The users selection cross the header / body boundry - thats never going to work well
                    getAddCustomButton().setEnabled(false);
                    getRemoveCustomButton().setEnabled(false);

                } else {
                    getAddCustomButton().setEnabled(true);
                    getRemoveCustomButton().setEnabled(false);
                }

            } else {
                // Nothing selected
                getAddCustomButton().setEnabled(false);
                getRemoveCustomButton().setEnabled(false);
            }

            getDisableNonCustomVectors().setEnabled(true);
        }

        getRequestField().getCaret().setVisible(true);
    }

    private TechSet getTechSet() {
        TechSet techSet = new TechSet();

        Iterator<Entry<Tech, DefaultMutableTreeNode>> iter = techToNodeMap.entrySet().iterator();
        while (iter.hasNext()) {
            Entry<Tech, DefaultMutableTreeNode> node = iter.next();
            TreePath tp = this.getTechPath(node.getValue());
            Tech tech = node.getKey();
            if (this.getTechTree().isSelectedFully(tp)) {
                techSet.include(tech);

            } else {
                techSet.exclude(tech);
            }
        }
        return techSet;
    }

    private JList<Highlight> getInjectionPointList() {
        if (injectionPointList == null) {
            injectionPointList = new JList<>(injectionPointModel);
            injectionPointList.setCellRenderer(new ListCellRenderer<Highlight>() {
                @Override
                public Component getListCellRendererComponent(JList<? extends Highlight> list, Highlight hlt,
                        int index, boolean isSelected, boolean cellHasFocus) {

                    String str = "";
                    try {
                        str = getRequestField().getText(hlt.getStartOffset(),
                                hlt.getEndOffset() - hlt.getStartOffset());
                        if (str.length() > 8) {
                            // just show first 8 chrs (arbitrary limit;)
                            str = str.substring(0, 8) + "..";
                        }
                    } catch (BadLocationException e) {
                        // Ignore
                    }

                    return new JLabel("[" + hlt.getStartOffset() + "," + hlt.getEndOffset() + "]: " + str);
                }
            });
        }

        return injectionPointList;
    }

    private boolean selectionIncludesHighlight(int start, int end, Highlight hl) {
        if (hl.getPainter() instanceof DefaultHighlighter.DefaultHighlightPainter) {
            DefaultHighlighter.DefaultHighlightPainter ptr = (DefaultHighlighter.DefaultHighlightPainter) hl
                    .getPainter();
            if (ptr.getColor() != null && ptr.getColor().equals(Color.RED)) {
                // Test for 'RED' needed to prevent matching the users selection
                return start < hl.getEndOffset() && end > hl.getStartOffset();
            }
        }
        return false;
    }

    private boolean selectionIncludesHighlight(int start, int end, Highlight[] hls) {
        for (Highlight hl : hls) {
            if (this.selectionIncludesHighlight(start, end, hl)) {
                return true;
            }
        }
        return false;
    }

    private void reset(boolean refreshUi) {

        // From Apache Commons source code:
        // Note: This method won't work well on hierarchical configurations because it is not able to 
        // copy information about the properties' structure. 
        // So when dealing with hierarchical configuration objects their clone() methods should be used.        
        //FileConfiguration fileConfig = new XMLConfiguration();
        //ConfigurationUtils.copy(extension.getScannerParam().getConfig(), fileConfig);
        XMLConfiguration fileConfig = (XMLConfiguration) ConfigurationUtils
                .cloneConfiguration(extension.getScannerParam().getConfig());

        scannerParam = new ScannerParam();
        scannerParam.load(fileConfig);

        optionsParam = new OptionsParam();
        optionsParam.load(fileConfig);

        if (refreshUi) {
            init(target);
            repaint();
        }
    }

    @Override
    public String getSaveButtonText() {
        return Constant.messages.getString("ascan.custom.button.scan");
    }

    @Override
    public JButton[] getExtraButtons() {
        if (extraButtons == null) {
            JButton resetButton = new JButton(Constant.messages.getString("ascan.custom.button.reset"));
            resetButton.addActionListener(new java.awt.event.ActionListener() {
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    reset(true);
                }
            });

            extraButtons = new JButton[] { resetButton };
        }

        return extraButtons;
    }

    /**
     * Use the save method to launch a scan
     */
    @Override
    public void save() {
        List<Object> contextSpecificObjects = new ArrayList<Object>();
        contextSpecificObjects.add(scanPolicy);

        if (this.getBoolValue(FIELD_ADVANCED)) {

            if (target == null && this.customPanels != null) {
                // One of the custom scan panels must have specified a target 
                for (CustomScanPanel customPanel : this.customPanels) {
                    target = customPanel.getTarget();
                    if (target != null) {
                        break;
                    }
                }
            }

            // Save all Variant configurations
            getVariantPanel().saveParam(scannerParam);

            // If all other vectors has been disabled
            // force all injectable params and rpc model to NULL
            if (getDisableNonCustomVectors().isSelected()) {
                scannerParam.setTargetParamsInjectable(0);
                scannerParam.setTargetParamsEnabledRPC(0);
            }

            if (!getBoolValue(FIELD_RECURSE) && injectionPointModel.getSize() > 0) {
                int[][] injPoints = new int[injectionPointModel.getSize()][];
                for (int i = 0; i < injectionPointModel.getSize(); i++) {
                    Highlight hl = injectionPointModel.elementAt(i);
                    injPoints[i] = new int[2];
                    injPoints[i][0] = hl.getStartOffset();
                    injPoints[i][1] = hl.getEndOffset();
                }

                try {
                    if (target != null && target.getStartNode() != null) {
                        VariantUserDefined.setInjectionPoints(
                                this.target.getStartNode().getHistoryReference().getURI().toString(), injPoints);

                        enableUserDefinedRPC();
                    }

                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }

            scannerParam.setHostPerScan(extension.getScannerParam().getHostPerScan());
            scannerParam.setThreadPerHost(extension.getScannerParam().getThreadPerHost());
            scannerParam.setHandleAntiCSRFTokens(extension.getScannerParam().getHandleAntiCSRFTokens());
            scannerParam.setMaxResultsToList(extension.getScannerParam().getMaxResultsToList());

            contextSpecificObjects.add(scannerParam);
            contextSpecificObjects.add(this.getTechSet());

            if (this.customPanels != null) {
                for (CustomScanPanel customPanel : this.customPanels) {
                    Object[] objs = customPanel.getContextSpecificObjects();
                    if (objs != null) {
                        for (Object obj : objs) {
                            contextSpecificObjects.add(obj);
                        }
                    }
                }
            }
        }

        target.setRecurse(this.getBoolValue(FIELD_RECURSE));

        if (target.getContext() == null && getSelectedContext() != null) {
            target.setContext(getSelectedContext());
        }

        this.extension.startScan(target, getSelectedUser(), contextSpecificObjects.toArray());
    }

    @Override
    public void setVisible(boolean show) {
        super.setVisible(show);

        if (!show) {
            scanPolicy = null;
        }
    }

    @Override
    public String validateFields() {
        if (Control.Mode.safe == Control.getSingleton().getMode()) {
            // The dialogue shouldn't be shown when in safe mode but if it is warn.
            return Constant.messages.getString("ascan.custom.notSafe.error");
        }

        if (this.customPanels != null) {
            // Check all custom panels validate ok
            for (CustomScanPanel customPanel : this.customPanels) {
                String fail = customPanel.validateFields();
                if (fail != null) {
                    return fail;
                }
            }
            // Check if they support a custom target
            for (CustomScanPanel customPanel : this.customPanels) {
                Target target = customPanel.getTarget();
                if (target != null && target.isValid()) {
                    // They do, everythings ok
                    return null;
                }
            }
        }

        if (this.target == null || !this.target.isValid()) {
            return Constant.messages.getString("ascan.custom.nostart.error");
        }

        switch (Control.getSingleton().getMode()) {
        case protect:
            List<StructuralNode> nodes = target.getStartNodes();
            if (nodes != null) {
                for (StructuralNode node : nodes) {
                    if (node instanceof StructuralSiteNode) {
                        SiteNode siteNode = ((StructuralSiteNode) node).getSiteNode();
                        if (!siteNode.isIncludedInScope()) {
                            return Constant.messages.getString("ascan.custom.targetNotInScope.error",
                                    siteNode.getHierarchicNodeName());
                        }
                    }
                }
            }
            break;
        default:
        }

        return null;
    }

    /**
     * Force UserDefinedRPC setting
     */
    public void enableUserDefinedRPC() {
        int enabledRpc = scannerParam.getTargetParamsEnabledRPC();
        enabledRpc |= ScannerParam.RPC_USERDEF;
        scannerParam.setTargetParamsEnabledRPC(enabledRpc);
    }
}