diff --git a/docker/CHANGELOG.md b/docker/CHANGELOG.md index 79fafc566d8..b0ca78770fd 100644 --- a/docker/CHANGELOG.md +++ b/docker/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog All notable changes to the docker containers will be documented in this file. +### 2025-11-03 +- Set statsId and readonly for scan policies. + ### 2025-10-31 - Added config options for recording which packaged scan is being run. diff --git a/docker/policies/API-Minimal.policy b/docker/policies/API-Minimal.policy index f1f69e3c9c6..af7b925a91c 100644 --- a/docker/policies/API-Minimal.policy +++ b/docker/policies/API-Minimal.policy @@ -1,6 +1,8 @@ API Minimal +dock-api-min +true OFF MEDIUM diff --git a/docker/policies/Default Policy.policy b/docker/policies/Default Policy.policy index 59795e176e9..2fff5f11548 100644 --- a/docker/policies/Default Policy.policy +++ b/docker/policies/Default Policy.policy @@ -1,6 +1,8 @@ Default Policy + dock-default + true MEDIUM MEDIUM diff --git a/docker/policies/St-High-Th-High.policy b/docker/policies/St-High-Th-High.policy index 44ff0caca19..56e6de6dd3e 100644 --- a/docker/policies/St-High-Th-High.policy +++ b/docker/policies/St-High-Th-High.policy @@ -1,6 +1,8 @@ St-High-Th-High + dock-high-high + true HIGH HIGH diff --git a/docker/policies/St-High-Th-Low.policy b/docker/policies/St-High-Th-Low.policy index 392454c2c68..733aa40717f 100644 --- a/docker/policies/St-High-Th-Low.policy +++ b/docker/policies/St-High-Th-Low.policy @@ -1,6 +1,8 @@ St-High-Th-Low + dock-high-low + true LOW HIGH diff --git a/docker/policies/St-High-Th-Med.policy b/docker/policies/St-High-Th-Med.policy index adc656f728f..89cd5f3df5f 100644 --- a/docker/policies/St-High-Th-Med.policy +++ b/docker/policies/St-High-Th-Med.policy @@ -1,6 +1,8 @@ St-High-Th-Med + dock-high-med + true MEDIUM HIGH diff --git a/docker/policies/St-Ins-Th-High.policy b/docker/policies/St-Ins-Th-High.policy index 43b18abda7c..64457d5af3f 100644 --- a/docker/policies/St-Ins-Th-High.policy +++ b/docker/policies/St-Ins-Th-High.policy @@ -1,6 +1,8 @@ St-Ins-Th-High + dock-ins-high + true HIGH INSANE diff --git a/docker/policies/St-Ins-Th-Low.policy b/docker/policies/St-Ins-Th-Low.policy index be6aaee3ade..d6616c44ee5 100644 --- a/docker/policies/St-Ins-Th-Low.policy +++ b/docker/policies/St-Ins-Th-Low.policy @@ -1,6 +1,8 @@ St-Ins-Th-Low + dock-ins-low + true LOW INSANE diff --git a/docker/policies/St-Ins-Th-Med.policy b/docker/policies/St-Ins-Th-Med.policy index 89f782caecd..dbdc33e3cb2 100644 --- a/docker/policies/St-Ins-Th-Med.policy +++ b/docker/policies/St-Ins-Th-Med.policy @@ -1,6 +1,8 @@ St-Ins-Th-Med + dock-ins-med + true MEDIUM INSANE diff --git a/docker/policies/St-Low-Th-High.policy b/docker/policies/St-Low-Th-High.policy index eaf229c61ef..ad681b55888 100644 --- a/docker/policies/St-Low-Th-High.policy +++ b/docker/policies/St-Low-Th-High.policy @@ -1,6 +1,8 @@ - St-Low-Th-Med + St-Low-Th-High + dock-low-high + true HIGH LOW diff --git a/docker/policies/St-Low-Th-Low.policy b/docker/policies/St-Low-Th-Low.policy index 17e331265d4..c85f13b35c7 100644 --- a/docker/policies/St-Low-Th-Low.policy +++ b/docker/policies/St-Low-Th-Low.policy @@ -1,6 +1,8 @@ St-Low-Th-Low + dock-low-low + true LOW LOW diff --git a/docker/policies/St-Low-Th-Med.policy b/docker/policies/St-Low-Th-Med.policy index aafe6d4c92b..a566b603f1e 100644 --- a/docker/policies/St-Low-Th-Med.policy +++ b/docker/policies/St-Low-Th-Med.policy @@ -1,6 +1,8 @@ St-Low-Th-Med + dock-low-med + true MEDIUM LOW diff --git a/docker/policies/St-Med-Th-High.policy b/docker/policies/St-Med-Th-High.policy index 08fffbdd0d4..ed976d2c4eb 100644 --- a/docker/policies/St-Med-Th-High.policy +++ b/docker/policies/St-Med-Th-High.policy @@ -1,6 +1,8 @@ St-Med-Th-High + dock-med-high + true HIGH MEDIUM diff --git a/docker/policies/St-Med-Th-Low.policy b/docker/policies/St-Med-Th-Low.policy index 642899f43ee..5c004000071 100644 --- a/docker/policies/St-Med-Th-Low.policy +++ b/docker/policies/St-Med-Th-Low.policy @@ -1,6 +1,8 @@ St-Med-Th-Low + dock-med-low + true LOW MEDIUM diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/Scanner.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/Scanner.java index f7f5de99081..0335e71cb7a 100644 --- a/zap/src/main/java/org/parosproxy/paros/core/scanner/Scanner.java +++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/Scanner.java @@ -59,6 +59,7 @@ // ZAP: 2023/01/10 Tidy up logger. // ZAP: 2023/05/30 Stop HostProcess to stop the Analyser. // ZAP: 2024/11/20 Include ID of the scan in relevant log messages. +// ZAP: 2025/11/03 Record stats for built-in policies. package org.parosproxy.paros.core.scanner; import java.security.InvalidParameterException; @@ -213,6 +214,10 @@ public void start(Target target) { if (this.user != null) { Stats.incCounter(ASCAN_SCAN_STARTED_USER_STATS); } + Stats.incCounter( + "stats.ascan.started.policy." + this.scanPolicy.getStatsId() != null + ? this.scanPolicy.getStatsId() + : "user"); } public void stop() { diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyAllCategoryPanel.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyAllCategoryPanel.java index ab0cfd4b1b2..25d4b1f5544 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyAllCategoryPanel.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyAllCategoryPanel.java @@ -541,6 +541,9 @@ public void validateParam(Object obj) throws Exception { throw new Exception(Constant.messages.getString("ascan.policy.warn.exists")); } } + if (policy.isReadOnly() && newName.equals(currentName)) { + throw new Exception(Constant.messages.getString("ascan.policy.warn.readonly")); + } } @Override diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyDialog.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyDialog.java index fe82ab9431d..e1cb32c2dc0 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyDialog.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyDialog.java @@ -102,8 +102,7 @@ public PolicyAllCategoryPanel getPolicyAllCategoryPanel() { @Override public void saveParam() throws Exception { super.saveParam(); - - extension.getPolicyManager().savePolicy(policy, currentName); + extension.getPolicyManager().savePolicy(policy, policy.isReadOnly() ? null : currentName); pmd.policyNamesChanged(); } } diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManager.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManager.java index 9f88099dcf9..9e21261df96 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManager.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManager.java @@ -98,11 +98,7 @@ public void savePolicy(ScanPolicy policy, String previousName) throws Configurat File file = new File(Constant.getPoliciesDir(), policy.getName() + POLICY_EXTENSION); ZapXmlConfiguration conf = new ZapXmlConfiguration(); - conf.setProperty("policy", policy.getName()); - conf.setProperty("scanner.level", policy.getDefaultThreshold().name()); - conf.setProperty("scanner.strength", policy.getDefaultStrength().name()); - - policy.getPluginFactory().saveTo(conf); + policy.saveTo(conf); if (previousName != null && !previousName.equals(policy.getName())) { File oldFile = new File(Constant.getPoliciesDir(), previousName + POLICY_EXTENSION); diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManagerDialog.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManagerDialog.java index 5f8af0b8daa..cbf610ff331 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManagerDialog.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/PolicyManagerDialog.java @@ -155,7 +155,19 @@ public void actionPerformed(ActionEvent e) { .getValueAt( getParamsTable().getSelectedRow(), 0); if (name != null) { - if (View.getSingleton() + ScanPolicy policy = null; + try { + policy = extension.getPolicyManager().getPolicy(name); + } catch (ConfigurationException e1) { + // Ignore + } + if (policy != null && policy.isReadOnly()) { + View.getSingleton() + .showWarningDialog( + PolicyManagerDialog.this, + Constant.messages.getString( + "ascan.policymgr.warn.builtin")); + } else if (View.getSingleton() .showConfirmDialog( PolicyManagerDialog.this, Constant.messages.getString( diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScanPolicy.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScanPolicy.java index be755acbe1c..7493dc7c762 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScanPolicy.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScanPolicy.java @@ -19,11 +19,13 @@ */ package org.zaproxy.zap.extension.ascan; +import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.FileConfiguration; import org.apache.commons.lang3.EnumUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.parosproxy.paros.Constant; import org.parosproxy.paros.core.scanner.Plugin; import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold; import org.parosproxy.paros.core.scanner.Plugin.AttackStrength; @@ -35,6 +37,8 @@ public class ScanPolicy { private static final Logger LOGGER = LogManager.getLogger(ScanPolicy.class); private String name; + private String statsId; + private boolean readOnly; private PluginFactory pluginFactory = new PluginFactory(); private AlertThreshold defaultThreshold; private AttackStrength defaultStrength; @@ -51,6 +55,12 @@ public ScanPolicy() { public ScanPolicy(ZapXmlConfiguration conf) throws ConfigurationException { this.conf = conf; name = conf.getString("policy", ""); + statsId = conf.getString("statsId", null); + readOnly = conf.getBoolean("readonly", false); + if (statsId == null + && name.equals(Constant.messages.getString("ascan.policymgr.default.name"))) { + statsId = "default"; + } pluginFactory.loadAllPlugin(conf); setDefaultThreshold(getAlertThresholdFromConfig()); @@ -73,6 +83,20 @@ public void cloneInto(ScanPolicy policy) { policy.pluginFactory.loadFrom(this.pluginFactory); policy.defaultStrength = this.getDefaultStrength(); policy.defaultThreshold = this.getDefaultThreshold(); + policy.statsId = this.statsId; + policy.readOnly = this.readOnly; + } + + /** + * Saves the policy to the specified file. Will not maintain the readonly or statsId properties. + * + * @since 2.17.0 + */ + public void saveTo(Configuration conf) throws ConfigurationException { + conf.setProperty("policy", getName()); + conf.setProperty("scanner.level", getDefaultThreshold().name()); + conf.setProperty("scanner.strength", getDefaultStrength().name()); + getPluginFactory().saveTo(conf); } public String getName() { @@ -146,4 +170,24 @@ private AttackStrength getAttackStrengthFromConfig() { } return AttackStrength.valueOf(attackStrength); } + + /** + * Returns a string to be used when recording stats. Only expected to be supplied for 'built in' + * policies. + * + * @since 2.17.0 + */ + public String getStatsId() { + return statsId; + } + + /** + * Returns true if the policy is 'built in' (according to the configs) - the UI should ensure it + * is not changed. + * + * @since 2.17.0 + */ + public boolean isReadOnly() { + return readOnly; + } } diff --git a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties index a99df4dc8ee..43f70aba9cd 100644 --- a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties +++ b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties @@ -553,8 +553,9 @@ ascan.policy.table.threshold = Threshold ascan.policy.title = Scan Policy ascan.policy.unfulfilled.dependencies = The scan rule cannot be enabled because of unfulfilled dependencies. ascan.policy.warn.badname = The Policy name must not contain any of the characters: {0} -ascan.policy.warn.exists = A policy with this name already exists -ascan.policy.warn.noname = You must supply a policy name +ascan.policy.warn.exists = A policy with this name already exists. +ascan.policy.warn.noname = You must supply a policy name. +ascan.policy.warn.readonly = You cannot change a built-in policy unless you also change the name. ascan.policymgr.button.add = Add ascan.policymgr.button.export = Export ascan.policymgr.button.import = Import @@ -563,6 +564,7 @@ ascan.policymgr.button.remove = Remove ascan.policymgr.default.name = Default Policy ascan.policymgr.table.policy = Policy Name ascan.policymgr.title = Scan Policy Manager +ascan.policymgr.warn.builtin = You cannot delete built-in policies. ascan.policymgr.warn.delete = Are you sure you want to delete this Policy? ascan.progress.chart.1xx = 1xx ascan.progress.chart.2xx = 2xx