Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
// ZAP: 2022/02/03 Removed loadedPlugin and unloadedPlugin.
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
// ZAP: 2025/11/17 Support locked policy.
package org.parosproxy.paros.core.scanner;

import java.util.ArrayList;
Expand All @@ -77,6 +78,7 @@
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold;
import org.zaproxy.zap.control.ExtensionFactory;

public class PluginFactory {
Expand All @@ -96,6 +98,7 @@ public class PluginFactory {
private int totalPluginToRun = 0;
private boolean init = false;
private Configuration config;
private boolean locked;

public PluginFactory() {
super();
Expand All @@ -104,6 +107,30 @@ public PluginFactory() {
config = configuration;
}

/**
* Tells whether or not this factory is locked.
*
* <p>Locked factories automatically disable any rules not defined in the loaded configuration.
*
* @return {@code true} if the factory is locked, {@code false} otherwise.
* @since 2.17.0
* @see #loadAllPlugin(Configuration)
*/
public boolean isLocked() {
return locked;
}

/**
* Sets whether or not this factory should be locked.
*
* @param locked {@code true} if the factory should be locked, {@code false} otherwise.
* @since 2.17.0
* @see #isLocked()
*/
public void setLocked(boolean locked) {
this.locked = locked;
}

private static synchronized void initPlugins() {
if (loadedPlugins == null) {
init(true);
Expand Down Expand Up @@ -396,7 +423,14 @@ public synchronized void loadAllPlugin(Configuration config) {
continue;
}

boolean disable =
locked && !config.getKeys("plugins.p" + loadedPlugin.getId()).hasNext();

Plugin plugin = createNewPlugin(loadedPlugin, config);
if (disable) {
plugin.setAlertThreshold(AlertThreshold.OFF);
}

LOGGER.debug(
"loaded plugin {} with: Threshold={} Strength={}",
plugin.getName(),
Expand Down
41 changes: 31 additions & 10 deletions zap/src/main/java/org/parosproxy/paros/model/SiteMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
// ZAP: 2022/09/21 Use format specifiers instead of concatenation when logging.
// ZAP: 2023/01/10 Tidy up logger.
// ZAP: 2024/01/19 Store clean node name when adding leaf node.
// ZAP: 2025/11/17 Add contentType to ADD events.
package org.parosproxy.paros.model;

import java.awt.EventQueue;
Expand Down Expand Up @@ -530,7 +531,11 @@ private SiteNode findAndAddChild(
hrefMap.put(result.getHistoryReference().getHistoryId(), result);

applyFilter(newNode);
handleEvent(parent, result, EventType.ADD);
handleEvent(
parent,
result,
EventType.ADD,
getAddEventParameters(HistoryReference.TYPE_TEMPORARY, baseMsg));
}
// ZAP: Cope with getSiteNode() returning null
if (baseRef.getSiteNode() == null) {
Expand Down Expand Up @@ -597,7 +602,8 @@ private SiteNode findAndAddLeaf(

this.applyFilter(node);

handleEvent(parent, node, EventType.ADD);
handleEvent(
parent, node, EventType.ADD, getAddEventParameters(ref.getHistoryType(), msg));
} else if (hrefMap.get(ref.getHistoryId()) != node) {

// Give preference to successful requests but update same statuses'.
Expand All @@ -615,6 +621,16 @@ private SiteNode findAndAddLeaf(
return node;
}

private Map<String, String> getAddEventParameters(int hrefType, HttpMessage msg) {
if (hrefType != HistoryReference.TYPE_TEMPORARY) {
String contentType = msg.getResponseHeader().getNormalisedContentTypeValue();
if (contentType != null) {
return Map.of("contentType", contentType);
}
}
return Map.of();
}

public HistoryReference createReference(
SiteNode node, HistoryReference baseRef, HttpMessage base)
throws HttpMalformedHeaderException,
Expand Down Expand Up @@ -761,7 +777,7 @@ private void clearParentFilter(SiteNode parent) {
public void removeNodeFromParent(MutableTreeNode node) {
SiteNode parent = (SiteNode) node.getParent();
super.removeNodeFromParent(node);
handleEvent(parent, (SiteNode) node, EventType.REMOVE);
handleEvent(parent, (SiteNode) node, EventType.REMOVE, Map.of());
}

/**
Expand All @@ -774,18 +790,19 @@ public void removeNodeFromParent(MutableTreeNode node) {
* @see EventType
* @since 2.5.0
*/
private void handleEvent(SiteNode parent, SiteNode node, EventType eventType) {
private void handleEvent(
SiteNode parent, SiteNode node, EventType eventType, Map<String, String> parameters) {
switch (eventType) {
case ADD:
publishEvent(SiteMapEventPublisher.SITE_NODE_ADDED_EVENT, node);
publishEvent(SiteMapEventPublisher.SITE_NODE_ADDED_EVENT, node, parameters);
if (parent == getRoot()) {
publishEvent(SiteMapEventPublisher.SITE_ADDED_EVENT, node);
publishEvent(SiteMapEventPublisher.SITE_ADDED_EVENT, node, parameters);
}
break;
case REMOVE:
publishEvent(SiteMapEventPublisher.SITE_NODE_REMOVED_EVENT, node);
publishEvent(SiteMapEventPublisher.SITE_NODE_REMOVED_EVENT, node, parameters);
if (parent == getRoot()) {
publishEvent(SiteMapEventPublisher.SITE_REMOVED_EVENT, node);
publishEvent(SiteMapEventPublisher.SITE_REMOVED_EVENT, node, parameters);
}
}
}
Expand All @@ -797,11 +814,15 @@ private void handleEvent(SiteNode parent, SiteNode node, EventType eventType) {
* @param node the node being acted upon
* @since 2.5.0
*/
private static void publishEvent(String event, SiteNode node) {
private static void publishEvent(String event, SiteNode node, Map<String, String> parameters) {
ZAP.getEventBus()
.publishSyncEvent(
SiteMapEventPublisher.getPublisher(),
new Event(SiteMapEventPublisher.getPublisher(), event, new Target(node)));
new Event(
SiteMapEventPublisher.getPublisher(),
event,
new Target(node),
parameters));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
// not important).
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2025/11/17 Support locked policy.
package org.zaproxy.zap.extension.ascan;

import java.awt.GridBagConstraints;
Expand All @@ -44,6 +45,7 @@
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
Expand Down Expand Up @@ -75,6 +77,7 @@ public class PolicyAllCategoryPanel extends AbstractParamPanel {
private static final Logger LOGGER = LogManager.getLogger(PolicyAllCategoryPanel.class);

private ZapTextField policyName = null;
private JCheckBox locked;
private JTable tableTest = null;
private JScrollPane jScrollPane = null;
private AllCategoryTableModel allCategoryTableModel = null;
Expand Down Expand Up @@ -146,6 +149,30 @@ private void initialize() {
0,
GridBagConstraints.HORIZONTAL,
new Insets(2, 2, 2, 2)));

locked = new JCheckBox();
locked.setSelected(policy.isLocked());
row++;
this.add(
new JLabel(Constant.messages.getString("ascan.policy.locked.label")),
LayoutHelper.getGBC(
0,
row,
1,
0.0D,
0,
GridBagConstraints.HORIZONTAL,
new Insets(2, 2, 2, 2)));
this.add(
locked,
LayoutHelper.getGBC(
1,
row,
2,
1.0D,
0,
GridBagConstraints.HORIZONTAL,
new Insets(2, 2, 2, 2)));
}

row++;
Expand Down Expand Up @@ -549,6 +576,9 @@ public void validateParam(Object obj) throws Exception {
@Override
public void saveParam(Object obj) throws Exception {
this.policy.setName(getPolicyName().getText());
if (locked != null) {
policy.setLocked(locked.isSelected());
}
}

/**
Expand Down
30 changes: 30 additions & 0 deletions zap/src/main/java/org/zaproxy/zap/extension/ascan/ScanPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class ScanPolicy {
private String name;
private String statsId;
private boolean readOnly;
private boolean locked;
private PluginFactory pluginFactory = new PluginFactory();
private AlertThreshold defaultThreshold;
private AttackStrength defaultStrength;
Expand All @@ -57,10 +58,12 @@ public ScanPolicy(ZapXmlConfiguration conf) throws ConfigurationException {
name = conf.getString("policy", "");
statsId = conf.getString("statsId", null);
readOnly = conf.getBoolean("readonly", false);
locked = conf.getBoolean("locked", false);
if (statsId == null
&& name.equals(Constant.messages.getString("ascan.policymgr.default.name"))) {
statsId = "default";
}
pluginFactory.setLocked(locked);
pluginFactory.loadAllPlugin(conf);

setDefaultThreshold(getAlertThresholdFromConfig());
Expand All @@ -85,6 +88,7 @@ public void cloneInto(ScanPolicy policy) {
policy.defaultThreshold = this.getDefaultThreshold();
policy.statsId = this.statsId;
policy.readOnly = this.readOnly;
policy.locked = this.locked;
}

/**
Expand All @@ -94,6 +98,7 @@ public void cloneInto(ScanPolicy policy) {
*/
public void saveTo(Configuration conf) throws ConfigurationException {
conf.setProperty("policy", getName());
conf.setProperty("locked", isLocked());
conf.setProperty("scanner.level", getDefaultThreshold().name());
conf.setProperty("scanner.strength", getDefaultStrength().name());
getPluginFactory().saveTo(conf);
Expand Down Expand Up @@ -190,4 +195,29 @@ public String getStatsId() {
public boolean isReadOnly() {
return readOnly;
}

/**
* Tells whether or not the policy is locked.
*
* <p>Locked policies do not allow the use of any other scan rules than the ones defined in
* their configuration.
*
* @return {@code true} if the policy is locked, {@code false} otherwise.
* @since 2.17.0
*/
public boolean isLocked() {
return locked;
}

/**
* Sets whether or not this policy should be locked.
*
* @param locked {@code true} if the policy should be locked, {@code false} otherwise.
* @since 2.17.0
* @see #isLocked()
*/
public void setLocked(boolean locked) {
this.locked = locked;
pluginFactory.setLocked(locked);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ ascan.policy.level.low = Low
ascan.policy.level.medium = Medium
ascan.policy.level.off = OFF
ascan.policy.load.error = Failed to load policy file, see log for detail
ascan.policy.locked.label = Locked:
ascan.policy.name.default = Default Policy
ascan.policy.name.label = Policy:
ascan.policy.namedialog.name.label = New Policy Name:
Expand Down
Loading
Loading