org.zaproxy.zap.control.AddOnRunIssuesUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.zaproxy.zap.control.AddOnRunIssuesUtils.java

Source

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

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.JXTree;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.view.View;

/**
 * An utility/helper class that extract textual representations of running issues of add-ons and show warning messages of
 * add-ons that can not be run.
 *
 * @since 2.4.0
 */
public final class AddOnRunIssuesUtils {

    private static final Logger LOGGER = Logger.getLogger(AddOnRunIssuesUtils.class);

    private AddOnRunIssuesUtils() {
    }

    /**
     * Shows a warning dialogue with the add-ons and its corresponding running issues or the issues if is extensions.
     * <p>
     * The dialogue is composed with the given {@code message} and a tree in which are shown the add-ons and its issues as child
     * nodes of the add-ons.
     *
     * @param message the main message shown in the dialogue
     * @param availableAddOns the add-ons that are available, used to create and check the running issues
     * @param addOnsNotRunnable the add-ons with running issues that will be shown in the tree
     */
    public static void showWarningMessageAddOnsNotRunnable(String message, AddOnCollection availableAddOns,
            Collection<AddOn> addOnsNotRunnable) {

        Object[] msgs = { message, createScrollableTreeAddOnsNotRunnable(availableAddOns,
                addOnsNotRunnable.toArray(new AddOn[addOnsNotRunnable.size()])) };

        JOptionPane.showMessageDialog(View.getSingleton().getMainFrame(), msgs, Constant.PROGRAM_NAME,
                JOptionPane.WARNING_MESSAGE);
    }

    /**
     * Creates a scrollable tree with the given add-ons as root nodes and its issues as child nodes.
     *
     * @param availableAddOns the add-ons that are available, used to create check the running issues
     * @param addOnsNotRunnable the add-ons with running issues that will be shown in the tree
     * @return the tree wrapper in a {@code JSCrollPane}
     */
    private static JScrollPane createScrollableTreeAddOnsNotRunnable(final AddOnCollection availableAddOns,
            AddOn... addOnsNotRunnable) {
        AddOnSearcher addOnSearcher = new AddOnSearcher() {

            @Override
            public AddOn searchAddOn(String id) {
                return availableAddOns.getAddOn(id);
            }
        };

        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("");
        for (AddOn addOn : addOnsNotRunnable) {
            DefaultMutableTreeNode addOnNode = new DefaultMutableTreeNode(addOn.getName());
            AddOn.AddOnRunRequirements requirements = addOn.calculateRunRequirements(availableAddOns.getAddOns());
            List<String> issues = getUiRunningIssues(requirements, addOnSearcher);

            if (issues.isEmpty()) {
                issues.addAll(getUiExtensionsRunningIssues(requirements, addOnSearcher));
            }

            for (String issue : issues) {
                addOnNode.add(new DefaultMutableTreeNode(issue));
            }
            rootNode.add(addOnNode);
        }

        JXTree tree = new JXTree(new DefaultTreeModel(rootNode));
        tree.setVisibleRowCount(5);
        tree.setEditable(false);
        tree.setRootVisible(false);
        tree.setShowsRootHandles(true);
        tree.expandAll();

        return new JScrollPane(tree);
    }

    /**
     * Shows a confirmation dialogue (yes-no question), with the given message at the top, followed by a tree in which is shown
     * the add-on with its corresponding running issues and, at the bottom before the yes and no buttons, the given question.
     *
     * @param message the main message shown in the dialogue
     * @param question the questions shown at the bottom of the dialogue, before the buttons
     * @param availableAddOns the add-ons that are available, used to create check the running issues
     * @param addOnNotRunnable the add-on with running issues that will be shown in the tree
     * @return {@code true} if it is confirmed, {@code false} otherwise
     */
    public static boolean askConfirmationAddOnNotRunnable(String message, String question,
            AddOnCollection availableAddOns, AddOn addOnNotRunnable) {
        Object[] msgs = { message, createScrollableTreeAddOnsNotRunnable(availableAddOns, addOnNotRunnable),
                question };

        return JOptionPane.showConfirmDialog(View.getSingleton().getMainFrame(), msgs, Constant.PROGRAM_NAME,
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
    }

    /**
     * Returns the textual representations of the running issues (Java version and dependency), if any.
     * <p>
     * The messages are internationalised thus suitable for UI components.
     *
     * @param requirements the run requirements of the add-on
     * @param addOnSearcher the class responsible for searching add-ons with a given id, used to search for add-ons that are
     *            missing for the add-on
     * @return a {@code List} containing all the running issues of the add-on, empty if none
     * @see #getRunningIssues(AddOn.BaseRunRequirements)
     * @see #getUiExtensionsRunningIssues(AddOn.AddOnRunRequirements, AddOnSearcher)
     */
    public static List<String> getUiRunningIssues(AddOn.BaseRunRequirements requirements,
            AddOnSearcher addOnSearcher) {
        List<String> issues = new ArrayList<>(2);
        if (requirements.isNewerJavaVersionRequired()) {
            if (requirements.getAddOn() != requirements.getAddOnMinimumJavaVersion()) {
                issues.add(MessageFormat.format(
                        Constant.messages
                                .getString("cfu.warn.addon.with.missing.requirements.javaversion.dependency"),
                        requirements.getMinimumJavaVersion(),
                        (SystemUtils.JAVA_VERSION == null
                                ? Constant.messages.getString("cfu.warn.unknownJavaVersion")
                                : SystemUtils.JAVA_VERSION),
                        requirements.getAddOnMinimumJavaVersion().getName()));
            } else {
                issues.add(MessageFormat.format(
                        Constant.messages.getString("cfu.warn.addon.with.missing.requirements.javaversion"),
                        requirements.getMinimumJavaVersion(),
                        (SystemUtils.JAVA_VERSION == null
                                ? Constant.messages.getString("cfu.warn.unknownJavaVersion")
                                : SystemUtils.JAVA_VERSION)));
            }
        }

        if (requirements.hasDependencyIssue()) {
            List<Object> issueDetails = requirements.getDependencyIssueDetails();
            AddOn addOn;
            String message = null;
            switch (requirements.getDependencyIssue()) {
            case CYCLIC:
                message = Constant.messages.getString("cfu.warn.addon.with.missing.requirements.addon.id");
                break;
            case OLDER_VERSION:
                // Do not set a message, the state is already reported as requiring an update.
                break;
            case MISSING:
                String addOnId = (String) issueDetails.get(0);
                if (addOnSearcher != null) {
                    addOn = addOnSearcher.searchAddOn(addOnId);
                } else {
                    addOn = null;
                }
                if (addOn == null) {
                    message = MessageFormat.format(
                            Constant.messages.getString("cfu.warn.addon.with.missing.requirements.addon.id"),
                            addOnId);

                } else {
                    message = MessageFormat.format(
                            Constant.messages.getString("cfu.warn.addon.with.missing.requirements.addon"),
                            addOn.getName());
                }
                break;
            case PACKAGE_VERSION_NOT_BEFORE:
                addOn = (AddOn) issueDetails.get(0);
                message = MessageFormat.format(
                        Constant.messages
                                .getString("cfu.warn.addon.with.missing.requirements.addon.version.notBefore"),
                        addOn.getName(), issueDetails.get(1), Integer.valueOf(addOn.getFileVersion()));
                break;
            case PACKAGE_VERSION_NOT_FROM:
                addOn = (AddOn) issueDetails.get(0);
                message = MessageFormat.format(
                        Constant.messages
                                .getString("cfu.warn.addon.with.missing.requirements.addon.version.notAfter"),
                        addOn.getName(), issueDetails.get(1), Integer.valueOf(addOn.getFileVersion()));
                break;
            case VERSION:
                addOn = (AddOn) issueDetails.get(0);
                if (addOn.getVersion() == null) {
                    message = MessageFormat.format(
                            Constant.messages.getString(
                                    "cfu.warn.addon.with.missing.requirements.addon.semver.notAvailable"),
                            addOn.getName(), issueDetails.get(1));
                } else {
                    message = MessageFormat.format(
                            Constant.messages.getString("cfu.warn.addon.with.missing.requirements.addon.semver"),
                            addOn.getName(), issueDetails.get(1), addOn.getVersion());
                }
                break;
            default:
                message = Constant.messages.getString("cfu.warn.addon.with.missing.requirements.unknown");
                LOGGER.warn("Failed to handle dependency issue with name \""
                        + requirements.getDependencyIssue().name() + "\" and details: " + issueDetails);
                break;
            }

            if (message != null) {
                issues.add(message);
            }
        }
        return issues;
    }

    /**
     * Returns the textual representations of the running issues (Java version and dependency) of the extensions of hte add-on,
     * if any.
     * <p>
     * The messages are internationalised thus suitable for UI components.
     *
     * @param requirements the run requirements of the add-on, whose extensions' run requirements will be used
     * @param addOnSearcher the class responsible for searching add-ons with a given id, used to search for add-ons that are
     *            missing for the add-on
     * @return a {@code List} containing all the running issues of the add-on, empty if none
     * @see #getRunningIssues(AddOn.BaseRunRequirements)
     * @see #getUiExtensionsRunningIssues(AddOn.AddOnRunRequirements, AddOnSearcher)
     */
    public static List<String> getUiExtensionsRunningIssues(AddOn.AddOnRunRequirements requirements,
            AddOnSearcher addOnSearcher) {
        if (!requirements.hasExtensionsWithRunningIssues()) {
            return new ArrayList<>(0);
        }

        List<String> issues = new ArrayList<>(10);
        for (AddOn.ExtensionRunRequirements extReqs : requirements.getExtensionRequirements()) {
            issues.addAll(getUiRunningIssues(extReqs, addOnSearcher));
        }
        return issues;
    }

    /**
     * Returns the textual representations of the running issues (Java version and dependency), if any.
     * <p>
     * The messages are not internationalised, should be used only for logging and non UI uses.
     *
     * @param requirements the run requirements of the add-on or extension
     * @return a {@code List} containing all the running issues of the add-on or extension, empty if none
     * @see #getUiRunningIssues(AddOn.BaseRunRequirements, AddOnSearcher)
     * @see #getUiExtensionsRunningIssues(AddOn.AddOnRunRequirements, AddOnSearcher)
     */
    public static List<String> getRunningIssues(AddOn.BaseRunRequirements requirements) {
        List<String> issues = new ArrayList<>(2);
        String issue = getJavaVersionIssue(requirements);
        if (issue != null) {
            issues.add(issue);
        }
        issue = getDependencyIssue(requirements);
        if (issue != null) {
            issues.add(issue);
        }
        return issues;
    }

    /**
     * Returns the textual representation of the issues that prevent the extensions of the add-on from being run, if any.
     * <p>
     * The messages are not internationalised, should be used only for logging and non UI uses.
     *
     * @param requirements the run requirements of the add-on whose extensions' run requirements will be used
     * @return a {@code String} representing the running issue, {@code null} if none.
     * @see AddOn.AddOnRunRequirements#getExtensionRequirements()
     */
    public static List<String> getExtensionsRunningIssues(AddOn.AddOnRunRequirements requirements) {
        if (!requirements.hasExtensionsWithRunningIssues()) {
            return new ArrayList<>(0);
        }

        List<String> issues = new ArrayList<>(10);
        for (AddOn.ExtensionRunRequirements extReqs : requirements.getExtensionRequirements()) {
            issues.addAll(getRunningIssues(extReqs));
        }
        return issues;
    }

    /**
     * Returns the textual representation of the Java version issue that prevents the add-on or extension from being run, if
     * any.
     * <p>
     * The message is not internationalised, should be used only for logging and non UI uses.
     *
     * @param requirements the run requirements of the add-on or extension
     * @return a {@code String} representing the running issue, {@code null} if none.
     */
    public static String getJavaVersionIssue(AddOn.BaseRunRequirements requirements) {
        if (!requirements.isNewerJavaVersionRequired()) {
            return null;
        }

        if (requirements.getAddOn() != requirements.getAddOnMinimumJavaVersion()) {
            return MessageFormat.format("Minimum Java version: {0} (\"{1}\" add-on)",
                    requirements.getMinimumJavaVersion(), requirements.getAddOnMinimumJavaVersion().getName());
        }
        return MessageFormat.format("Minimum Java version: {0}", requirements.getMinimumJavaVersion());
    }

    /**
     * Returns the textual representation of the issue that prevents the add-on or extension from being run, if any.
     * <p>
     * The messages are not internationalised, should be used only for logging and non UI uses.
     *
     * @param requirements the run requirements of the add-on or extension
     * @return a {@code String} representing the running issue, {@code null} if none.
     */
    public static String getDependencyIssue(AddOn.BaseRunRequirements requirements) {
        if (!requirements.hasDependencyIssue()) {
            return null;
        }

        List<Object> issueDetails = requirements.getDependencyIssueDetails();
        switch (requirements.getDependencyIssue()) {
        case CYCLIC:
            return "Cyclic dependency with: " + issueDetails.get(0);
        case OLDER_VERSION:
            return "Older version still installed: " + issueDetails.get(0);
        case MISSING:
            String addOnId = (String) issueDetails.get(0);
            return MessageFormat.format("Add-On with ID \"{0}\"", addOnId);
        case PACKAGE_VERSION_NOT_BEFORE:
            AddOn addOn = (AddOn) issueDetails.get(0);
            return MessageFormat.format("Add-on \"{0}\" with version not before {1} (found version {2})",
                    addOn.getName(), issueDetails.get(1), Integer.valueOf(addOn.getFileVersion()));
        case PACKAGE_VERSION_NOT_FROM:
            addOn = (AddOn) issueDetails.get(0);
            return MessageFormat.format("Add-on \"{0}\" with version not after {1} (found version {2})",
                    addOn.getName(), issueDetails.get(1), Integer.valueOf(addOn.getFileVersion()));
        case VERSION:
            addOn = (AddOn) issueDetails.get(0);
            if (addOn.getVersion() == null) {
                return MessageFormat.format(
                        "Add-on \"{0}\" with semantic version >= {1} (found no semantic version)", addOn.getName(),
                        issueDetails.get(1));
            }
            return MessageFormat.format("Add-on \"{0}\" with semantic version >= {1} (found version {2})",
                    addOn.getName(), issueDetails.get(1), addOn.getVersion());
        default:
            LOGGER.warn("Failed to handle dependency issue with name \"" + requirements.getDependencyIssue().name()
                    + "\" and details: " + issueDetails);
            return null;
        }
    }

    /**
     * Interface to search for add-ons with a given id.
     * <p>
     * Used to search for missing add-ons as reported by run requirements of an add-on.
     * 
     * @see AddOn.BaseRunRequirements.DependencyIssue#MISSING
     */
    public static interface AddOnSearcher {

        /**
         * Returns an add-on with the given id, if none is found returns {@code null}.
         *
         * @param id the id of the add-on
         * @return the add-on, or {@code null} if not found
         */
        AddOn searchAddOn(String id);
    }
}