/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2011 The Zed Attack Proxy 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.pscan;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold;
import org.parosproxy.paros.model.HistoryReference;
import org.zaproxy.zap.control.AddOn;
import org.zaproxy.zap.utils.Enableable;
public abstract class PluginPassiveScanner extends Enableable implements PassiveScanner {
/**
* The (base) configuration key used to saved the configurations of a passive scanner, ID, alert threshold and enabled
* state.
*/
private static final String PSCANS_KEY = PassiveScanParam.PASSIVE_SCANS_BASE_KEY + ".pscanner";
/**
* The configuration key used to save/load the ID of a passive scanner.
*/
private static final String ID_KEY = "id";
/**
* The configuration key used to load the classname of a passive scanner, used only for backwards compatibility.
*/
private static final String CLASSNAME_KEY = "classname";
/**
* The configuration key used to save/load the alert threshold of a passive scanner.
*/
private static final String LEVEL_KEY = "level";
/**
* The configuration key used to save/load the enabled state of a passive scanner.
*/
private static final String ENABLED_KEY = "enabled";
private static final Integer[] DEFAULT_HISTORY_TYPES = new Integer[] {
HistoryReference.TYPE_PROXIED, HistoryReference.TYPE_ZAP_USER,
HistoryReference.TYPE_SPIDER, HistoryReference.TYPE_SPIDER_AJAX};
private static final Set<Integer> DEFAULT_HISTORY_TYPES_SET =
Collections.unmodifiableSet(new HashSet<Integer>(Arrays.asList(DEFAULT_HISTORY_TYPES)));
private AlertThreshold level = AlertThreshold.DEFAULT;
private AlertThreshold defaultLevel = AlertThreshold.MEDIUM;
private Configuration config = null;
private AddOn.Status status = AddOn.Status.unknown;
public PluginPassiveScanner() {
super(true);
}
/**
* Sets the current configuration of the passive scanner.
*
* @param config the configuration of the scanner
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 1.4.0
* @see #getConfig()
*/
public void setConfig(Configuration config) {
if (config == null) {
throw new IllegalArgumentException("Parameter config must not be null.");
}
this.config = config;
this.loadFrom(config);
}
public void loadFrom(Configuration conf) {
List<HierarchicalConfiguration> fields = ((HierarchicalConfiguration) getConfig()).configurationsAt(PSCANS_KEY);
for (HierarchicalConfiguration sub : fields) {
if (isPluginConfiguration(sub)) {
setLevel(AlertThreshold.valueOf(sub.getString(LEVEL_KEY, AlertThreshold.DEFAULT.name())));
setEnabled(sub.getBoolean(ENABLED_KEY, true));
break;
}
}
}
/**
* Tells whether or not the given configuration belongs to this passive scanner.
*
* @param configuration the configuration to check
* @return {@code true} if the configuration belongs to this passive scanner, {@code false} otherwise
*/
private boolean isPluginConfiguration(Configuration configuration) {
return (configuration.containsKey(ID_KEY) && getPluginId() == configuration.getInt(ID_KEY))
// To keep backwards compatibility check also the classname
|| getClass().getCanonicalName().equals(configuration.getString(CLASSNAME_KEY, ""));
}
/**
* Gets the current configuration of the passive scanner.
*
* @return the configuration of the scanner, might be {@code null}
* @since 1.4.0
* @see #setConfig(Configuration)
*/
public Configuration getConfig() {
return config;
}
/**
* Saves the configurations of the passive scanner to the current configuration.
*
* @throws IllegalStateException if no configuration was set.
* @since 1.4.0
* @see #setConfig(Configuration)
* @see #saveTo(Configuration)
*/
public void save() {
Configuration conf = getConfig();
if (conf == null) {
throw new IllegalStateException("No configuration has been set.");
}
this.saveTo(conf);
}
public void saveTo(Configuration conf) {
boolean removed = false;
List<HierarchicalConfiguration> fields = ((HierarchicalConfiguration) getConfig()).configurationsAt(PSCANS_KEY);
for (HierarchicalConfiguration sub : fields) {
if (isPluginConfiguration(sub)) {
sub.getRootNode().getParentNode().removeChild(sub.getRootNode());
removed = true;
break;
}
}
boolean persistId = false;
String entryKey = PSCANS_KEY + "(" + (removed ? fields.size() - 1 : fields.size()) + ").";
if (getLevel() != AlertThreshold.MEDIUM) {
conf.setProperty(entryKey + LEVEL_KEY, getLevel().name());
persistId = true;
}
if (!isEnabled()) {
conf.setProperty(entryKey + ENABLED_KEY, Boolean.FALSE);
persistId = true;
}
if (persistId) {
conf.setProperty(entryKey + ID_KEY, getPluginId());
}
}
@Override
public AlertThreshold getLevel() {
if (AlertThreshold.DEFAULT.equals(level)) {
return defaultLevel;
}
return level;
}
public AlertThreshold getLevel(boolean incDefault) {
return level;
}
/**
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @see #getLevel()
*/
@Override
public void setLevel(AlertThreshold level) {
if (level == null) {
throw new IllegalArgumentException("Parameter level must not be null.");
}
this.level = level;
}
/**
* Sets the alert threshold that should be returned when set to {@link AlertThreshold#DEFAULT}.
*
* @param level the value of default alert threshold
* @throws IllegalArgumentException if the given parameter is {@code null} or {@codeAlertThreshold.DEFAULT}.
* @since 2.0.0
* @see #setLevel(AlertThreshold)
*/
public void setDefaultLevel(AlertThreshold level) {
if (level == null || level == AlertThreshold.DEFAULT) {
throw new IllegalArgumentException("Parameter level must not be null or DEFAULT.");
}
this.defaultLevel = level;
}
/**
* Returns the ID of the plug-in.
*
* @return the id of the plug-in.
* @since 2.3.0
*/
public int getPluginId() {
return -1;
}
/**
* Gets the status of the passive scanner.
*
* @return the status of the scanner, never {@code null}
* @since 2.4.0
*/
public AddOn.Status getStatus() {
return status;
}
/**
* Sets the status of the passive scanner.
*
* @param status the status of the scanner
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.4.0
*/
public void setStatus(AddOn.Status status) {
if (status == null) {
throw new IllegalArgumentException("Parameter status must not be null.");
}
this.status = status;
}
public static Set<Integer> getDefaultHistoryTypes() {
return DEFAULT_HISTORY_TYPES_SET;
}
@Override
public boolean appliesToHistoryType(int historyType) {
return getDefaultHistoryTypes().contains(historyType);
}
}