From b806d2955e05059010932c4adce934f129e3cda6 Mon Sep 17 00:00:00 2001 From: thc202 Date: Tue, 23 Sep 2025 09:49:19 +0100 Subject: [PATCH] Add option for temp active scan msgs persistence Allow to control whether or not the temporary HTTP messages sent while active scanning should be persisted. By default the HTTP messages are persisted unless in command line mode, where sessions are usually discarded once ZAP finishes. Signed-off-by: thc202 --- .../paros/core/scanner/ScannerParam.java | 45 +++++++++++++++++++ .../zap/extension/ascan/ActiveScan.java | 12 ++++- .../extension/ascan/OptionsScannerPanel.java | 17 +++++++ .../zaproxy/zap/resources/Messages.properties | 4 ++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerParam.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerParam.java index 18ac9a50c7f..e3292608922 100644 --- a/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerParam.java +++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerParam.java @@ -72,6 +72,7 @@ import org.apache.logging.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.common.AbstractParam; +import org.zaproxy.zap.ZAP; import org.zaproxy.zap.extension.api.ZapApiIgnore; public class ScannerParam extends AbstractParam { @@ -145,6 +146,9 @@ public class ScannerParam extends AbstractParam { private static final String MAX_ALERTS_PER_RULE = ACTIVE_SCAN_BASE_KEY + ".maxAlertsPerRule"; + private static final String PERSIST_TEMPORARY_MESSAGES = + ACTIVE_SCAN_BASE_KEY + ".persistTemporaryMessages"; + // ZAP: Configuration constants public static final int TARGET_QUERYSTRING = 1; public static final int TARGET_POSTDATA = 1 << 1; @@ -239,6 +243,8 @@ public class ScannerParam extends AbstractParam { // ZAP: internal Logger private static final Logger LOGGER = LogManager.getLogger(ScannerParam.class); + private boolean persistTemporaryMessages; + public ScannerParam() {} @Override @@ -338,6 +344,11 @@ protected void parse() { } maxAlertsPerRule = Math.max(0, getInt(MAX_ALERTS_PER_RULE, 0)); + + persistTemporaryMessages = + getBoolean( + PERSIST_TEMPORARY_MESSAGES, + ZAP.getProcessType() != ZAP.ProcessType.cmdline); } private void migrateOldOptions() { @@ -775,4 +786,38 @@ public boolean isEncodeCookieValues() { public void setEncodeCookieValues(boolean encodeCookieValues) { this.encodeCookieValues = encodeCookieValues; } + + /** + * Tells whether or not the temporary HTTP messages sent while active scanning should be + * persisted. + * + * @return {@code true} if the temporary HTTP messages should be persisted, {@code false} + * otherwise. + * @since 2.17.0 + * @see #setPersistTemporaryMessages(boolean) + */ + public boolean isPersistTemporaryMessages() { + return persistTemporaryMessages; + } + + /** + * Sets whether or not the temporary HTTP messages sent while active scanning should be + * persisted. + * + *

By default the HTTP messages are persisted unless in command line mode, where sessions are + * usually discarded once ZAP finishes. + * + * @param value {@code true} if the temporary HTTP messages should be persisted, {@code false} + * otherwise. + * @since 2.17.0 + * @see #isPersistTemporaryMessages() + */ + public void setPersistTemporaryMessages(boolean value) { + if (persistTemporaryMessages == value) { + return; + } + + this.persistTemporaryMessages = value; + getConfig().setProperty(PERSIST_TEMPORARY_MESSAGES, this.persistTemporaryMessages); + } } diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScan.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScan.java index 223d6cad58b..6a04c0f9ed5 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScan.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScan.java @@ -81,6 +81,8 @@ public static enum State { private static final Logger LOGGER = LogManager.getLogger(ActiveScan.class); + private boolean persistTemporaryMessages; + @Deprecated public ActiveScan( String displayName, @@ -110,6 +112,8 @@ public ActiveScan( ScanPolicy scanPolicy, RuleConfigParam ruleConfigParam) { super(scannerParam, scanPolicy, ruleConfigParam); + persistTemporaryMessages = scannerParam.isPersistTemporaryMessages(); + this.displayName = displayName; this.maxResultsToList = scannerParam.getMaxResultsToList(); // Easiest way to get the messages and alerts ;) @@ -277,6 +281,12 @@ public ActiveScanTableModel getMessagesTableModel() { @Override public void notifyNewMessage(final HttpMessage msg) { + this.rcTotals.incResponseCodeCount(msg.getResponseHeader().getStatusCode()); + + if (!persistTemporaryMessages) { + return; + } + HistoryReference hRef = msg.getHistoryRef(); if (hRef == null) { try { @@ -294,8 +304,6 @@ public void notifyNewMessage(final HttpMessage msg) { hRefs.add(hRef.getHistoryId()); } - this.rcTotals.incResponseCodeCount(msg.getResponseHeader().getStatusCode()); - if (hRef != null && View.isInitialised()) { // Very large lists significantly impact the UI responsiveness // limiting them makes large scans _much_ quicker diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/OptionsScannerPanel.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/OptionsScannerPanel.java index df7ede84f8e..91dbafef7ff 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/OptionsScannerPanel.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/OptionsScannerPanel.java @@ -50,6 +50,7 @@ public class OptionsScannerPanel extends AbstractParamPanel { private ZapNumberSpinner spinnerMaxRuleDuration = null; private ZapNumberSpinner spinnerMaxScanDuration = null; private ZapNumberSpinner maxAlertsPerRule; + private JCheckBox persistTemporaryMessages; private ZapNumberSpinner spinnerMaxResultsList = null; private JCheckBox chkInjectPluginIdInHeader = null; private JCheckBox chkHandleAntiCsrfTokens = null; @@ -123,6 +124,20 @@ private JPanel getPanelScanner() { GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2))); + persistTemporaryMessages = + new JCheckBox( + Constant.messages.getString("ascan.options.persistTempMessages.label")); + panelScanner.add( + persistTemporaryMessages, + LayoutHelper.getGBC( + 0, + row++, + 2, + 0.0D, + 0, + GridBagConstraints.HORIZONTAL, + new Insets(2, 2, 2, 2))); + panelScanner.add( new JLabel(Constant.messages.getString("ascan.options.maxRes.label")), LayoutHelper.getGBC( @@ -418,6 +433,7 @@ public void initParam(Object obj) { getSliderHostPerScan().setValue(param.getHostPerScan()); getSpinnerThreadsPerHost().setValue(param.getThreadPerHost()); getSpinnerDelayInMs().setValue(param.getDelayInMs()); + persistTemporaryMessages.setSelected(param.isPersistTemporaryMessages()); getSpinnerMaxResultsList().setValue(param.getMaxResultsToList()); getSpinnerMaxRuleDuration().setValue(param.getMaxRuleDurationInMins()); getSpinnerMaxScanDuration().setValue(param.getMaxScanDurationInMins()); @@ -444,6 +460,7 @@ public void saveParam(Object obj) throws Exception { param.setHostPerScan(getSliderHostPerScan().getValue()); param.setThreadPerHost(getSpinnerThreadsPerHost().getValue()); param.setDelayInMs(getDelayInMs()); + param.setPersistTemporaryMessages(persistTemporaryMessages.isSelected()); param.setMaxResultsToList(this.getSpinnerMaxResultsList().getValue()); param.setMaxRuleDurationInMins(this.getSpinnerMaxRuleDuration().getValue()); param.setMaxScanDurationInMins(this.getSpinnerMaxScanDuration().getValue()); 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 bae183a7281..ff859943c27 100644 --- a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties +++ b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties @@ -337,6 +337,8 @@ ascan.api.action.setOptionMaxScanDurationInMins = ascan.api.action.setOptionMaxScanDurationInMins.param.Integer = ascan.api.action.setOptionMaxScansInUI = ascan.api.action.setOptionMaxScansInUI.param.Integer = +ascan.api.action.setOptionPersistTemporaryMessages = Sets whether or not the temporary HTTP messages sent while active scanning should be persisted. +ascan.api.action.setOptionPersistTemporaryMessages.param.Boolean = true if the temporary HTTP messages should be persisted, false otherwise. ascan.api.action.setOptionPromptInAttackMode = ascan.api.action.setOptionPromptInAttackMode.param.Boolean = ascan.api.action.setOptionPromptToClearFinishedScans = @@ -408,6 +410,7 @@ ascan.api.view.optionMaxResultsToList = ascan.api.view.optionMaxRuleDurationInMins = ascan.api.view.optionMaxScanDurationInMins = ascan.api.view.optionMaxScansInUI = +ascan.api.view.optionPersistTemporaryMessages = Tells whether or not the temporary HTTP messages sent while active scanning should be persisted. ascan.api.view.optionPromptInAttackMode = ascan.api.view.optionPromptToClearFinishedScans = ascan.api.view.optionRescanInAttackMode = @@ -495,6 +498,7 @@ ascan.options.maxRule.label = Maximum Rule Duration (minutes; 0 is unlimited): ascan.options.maxScan.label = Maximum Scan Duration (minutes; 0 is unlimited): ascan.options.numHosts.label = Number of Hosts Scanned Concurrently: ascan.options.numThreads.label = Concurrent Scanning Threads per Host: +ascan.options.persistTempMessages.label = Persist temporary HTTP messages. ascan.options.pluginHeader.label = Inject plugin ID in header for all active scan requests. ascan.options.policy.ascan.label = Default Active Scan Policy: ascan.options.policy.attack.label = Attack Mode Scan Policy: