diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/AbstractPlugin.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/AbstractPlugin.java
index c6d82fe9291..f41f077e408 100644
--- a/zap/src/main/java/org/parosproxy/paros/core/scanner/AbstractPlugin.java
+++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/AbstractPlugin.java
@@ -70,6 +70,8 @@
// ZAP: 2020/11/17 Use new TechSet#getAllTech().
// ZAP: 2020/11/26 Use Log4j2 getLogger() and deprecate Log4j1.x.
// ZAP: 2021/07/20 Correct message updated with the scan rule ID header (Issue 6689).
+// ZAP: 2022/01/04 Process notifications also on exception during sendAndReceive (Issue
+// 7004).
package org.parosproxy.paros.core.scanner;
import java.io.IOException;
@@ -94,6 +96,7 @@
import org.parosproxy.paros.network.HttpStatusCode;
import org.zaproxy.zap.control.AddOn;
import org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF;
+import org.zaproxy.zap.extension.ascan.ScannerTaskResult;
import org.zaproxy.zap.extension.custompages.CustomPage;
import org.zaproxy.zap.model.Tech;
import org.zaproxy.zap.model.TechSet;
@@ -309,16 +312,25 @@ protected void sendAndReceive(
// ZAP: Runs the "beforeScan" methods of any ScannerHooks
parent.performScannerHookBeforeScan(message, this);
- if (isFollowRedirect) {
- parent.getHttpSender().sendAndReceive(message, getParent().getRedirectRequestConfig());
- } else {
- parent.getHttpSender().sendAndReceive(message, false);
+ try {
+ if (isFollowRedirect) {
+ parent.getHttpSender()
+ .sendAndReceive(message, getParent().getRedirectRequestConfig());
+ } else {
+ parent.getHttpSender().sendAndReceive(message, false);
+ }
+ } catch (IOException e) {
+ message.setErrorResponse(e);
+ // ZAP: Notify parent
+ parent.notifyNewMessage(this, new ScannerTaskResult(message, e.getLocalizedMessage()));
+ return;
}
// ZAP: Notify parent
- parent.notifyNewMessage(this, message);
+ parent.notifyNewMessage(this, new ScannerTaskResult(message));
- // ZAP: Set the history reference back and run the "afterScan" methods of any ScannerHooks
+ // ZAP: Set the history reference back and run the "afterScan" methods of any
+ // ScannerHooks
parent.performScannerHookAfterScan(message, this);
}
diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/Analyser.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/Analyser.java
index 6a3e0d7bd0e..a851ba6ee07 100644
--- a/zap/src/main/java/org/parosproxy/paros/core/scanner/Analyser.java
+++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/Analyser.java
@@ -43,6 +43,7 @@
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2019/07/26 Remove null check in sendAndReceive(HttpMessage). (LGTM Issue)
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
+// ZAP: 2022/01/04 Use changed ScannerListener interface
package org.parosproxy.paros.core.scanner;
import java.io.IOException;
@@ -63,6 +64,7 @@
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpSender;
import org.parosproxy.paros.network.HttpStatusCode;
+import org.zaproxy.zap.extension.ascan.ScannerTaskResult;
import org.zaproxy.zap.model.StructuralNode;
public class Analyser {
@@ -519,7 +521,7 @@ private void sendAndReceive(HttpMessage msg) throws HttpException, IOException {
httpSender.sendAndReceive(msg, parent.getRedirectRequestConfig());
requestCount++;
- parent.notifyNewMessage(msg);
+ parent.notifyNewMessage(new ScannerTaskResult(msg));
}
public int getDelayInMs() {
diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/HostProcess.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/HostProcess.java
index c832455fdbd..eba9c098773 100644
--- a/zap/src/main/java/org/parosproxy/paros/core/scanner/HostProcess.java
+++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/HostProcess.java
@@ -99,6 +99,7 @@
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2021/09/14 No longer force single threading if Anti CSRF handling turned on.
// ZAP: 2021/09/30 Pass plugin to PluginStats instead of just the name.
+// ZAP: 2022/01/04 Use changed ScannerListener interface
package org.parosproxy.paros.core.scanner;
import java.io.IOException;
@@ -126,6 +127,7 @@
import org.parosproxy.paros.network.HttpSender;
import org.zaproxy.zap.extension.alert.ExtensionAlert;
import org.zaproxy.zap.extension.ascan.ScanPolicy;
+import org.zaproxy.zap.extension.ascan.ScannerTaskResult;
import org.zaproxy.zap.extension.ascan.filters.FilterResult;
import org.zaproxy.zap.extension.ascan.filters.ScanFilter;
import org.zaproxy.zap.extension.custompages.CustomPage;
@@ -646,7 +648,7 @@ private boolean scanMessage(Plugin plugin, int messageId) {
private boolean obtainResponse(HistoryReference hRef, HttpMessage message) {
try {
getHttpSender().sendAndReceive(message);
- notifyNewMessage(message);
+ notifyNewMessage(new ScannerTaskResult(message));
requestCount++;
return true;
} catch (IOException e) {
@@ -839,9 +841,11 @@ private void notifyHostComplete() {
*
* @param msg the new HTTP message
* @since 1.2.0
+ * @deprecated (2.12.0) Use {@link #notifyNewMessage(ScannerTaskResult)}
*/
+ @Deprecated
public void notifyNewMessage(HttpMessage msg) {
- parentScanner.notifyNewMessage(msg);
+ notifyNewMessage(new ScannerTaskResult(msg));
}
/**
@@ -852,9 +856,37 @@ public void notifyNewMessage(HttpMessage msg) {
* @throws IllegalArgumentException if the given {@code plugin} is {@code null}.
* @since 2.5.0
* @see #notifyNewMessage(Plugin)
+ * @deprecated (2.12.0) Use {@link #notifyNewMessage(Plugin, ScannerTaskResult)}
*/
+ @Deprecated
public void notifyNewMessage(Plugin plugin, HttpMessage message) {
- parentScanner.notifyNewMessage(message);
+ notifyNewMessage(plugin, new ScannerTaskResult(message));
+ }
+
+ /**
+ * Notifies interested parties that a new message was sent (and received).
+ *
+ *
{@link Plugin Plugins} should call {@link #notifyNewMessage(Plugin)} or {@link
+ * #notifyNewMessage(Plugin, ScannerTaskResult)}, instead.
+ *
+ * @param scannerTaskResult contains the new HTTP message
+ * @since 1.2.0
+ */
+ public void notifyNewMessage(ScannerTaskResult scannerTaskResult) {
+ parentScanner.notifyNewMessage(scannerTaskResult);
+ }
+
+ /**
+ * Notifies that the given {@code plugin} sent (and received) the given HTTP message.
+ *
+ * @param plugin the plugin that sent the message
+ * @param scannerTaskResult contains the message sent
+ * @throws IllegalArgumentException if the given {@code plugin} is {@code null}.
+ * @since 2.5.0
+ * @see #notifyNewMessage(Plugin)
+ */
+ public void notifyNewMessage(Plugin plugin, ScannerTaskResult scannerTaskResult) {
+ parentScanner.notifyNewMessage(scannerTaskResult);
notifyNewMessage(plugin);
}
@@ -867,7 +899,7 @@ public void notifyNewMessage(Plugin plugin, HttpMessage message) {
* @param plugin the plugin that sent a non-HTTP message
* @throws IllegalArgumentException if the given parameter is {@code null}.
* @since 2.5.0
- * @see #notifyNewMessage(Plugin, HttpMessage)
+ * @see #notifyNewMessage(Plugin, ScannerTaskResult)
*/
public void notifyNewMessage(Plugin plugin) {
if (plugin == null) {
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 384fd051a17..e24e464c154 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
@@ -52,6 +52,7 @@
// ZAP: 2020/11/17 Use new TechSet#getAllTech().
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2021/05/14 Remove redundant type arguments.
+// ZAP: 2022/01/04 Use changed ScannerListener interface
package org.parosproxy.paros.core.scanner;
import java.security.InvalidParameterException;
@@ -79,6 +80,7 @@
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.ascan.ActiveScanEventPublisher;
import org.zaproxy.zap.extension.ascan.ScanPolicy;
+import org.zaproxy.zap.extension.ascan.ScannerTaskResult;
import org.zaproxy.zap.extension.ascan.filters.ScanFilter;
import org.zaproxy.zap.extension.ruleconfig.RuleConfigParam;
import org.zaproxy.zap.extension.script.ScriptCollection;
@@ -455,10 +457,16 @@ public boolean isPaused() {
return pause;
}
+ /** @deprecated (2.12.0) Use {@link #notifyNewMessage(ScannerTaskResult)} */
+ @Deprecated
public void notifyNewMessage(HttpMessage msg) {
+ notifyNewMessage(new ScannerTaskResult(msg));
+ }
+
+ public void notifyNewMessage(ScannerTaskResult scannerTaskResult) {
for (int i = 0; i < listenerList.size(); i++) {
ScannerListener listener = listenerList.get(i);
- listener.notifyNewMessage(msg);
+ listener.notifyNewTaskResult(scannerTaskResult);
}
}
diff --git a/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerListener.java b/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerListener.java
index 324b4b50364..f8f2e10fdaa 100644
--- a/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerListener.java
+++ b/zap/src/main/java/org/parosproxy/paros/core/scanner/ScannerListener.java
@@ -25,9 +25,11 @@
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2019/12/10 Issue 5278: Adding filtered messages to active scan panel.
// ZAP: 2021/05/14 Remove empty statement.
+// ZAP: 2022/01/14 deprecated notifyNewMessage and added notifyNewTaskResult
package org.parosproxy.paros.core.scanner;
import org.parosproxy.paros.network.HttpMessage;
+import org.zaproxy.zap.extension.ascan.ScannerTaskResult;
public interface ScannerListener {
@@ -41,8 +43,13 @@ public interface ScannerListener {
void alertFound(Alert alert);
- // ZAP: Added notifyNewMessage
- void notifyNewMessage(HttpMessage msg);
+ /** @deprecated (2.12.0) Use {@link #notifyNewTaskResult(ScannerTaskResult)} */
+ @Deprecated
+ default void notifyNewMessage(HttpMessage msg) {}
+
+ default void notifyNewTaskResult(ScannerTaskResult scannerTaskResult) {
+ notifyNewMessage(scannerTaskResult.getHttpMessage());
+ }
/**
* Added to notify reason for filtering message from scanning.
diff --git a/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java b/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java
index 44b4c02eb22..fe052f9e8de 100644
--- a/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java
+++ b/zap/src/main/java/org/parosproxy/paros/network/HttpMessage.java
@@ -60,9 +60,12 @@
// ZAP: 2021/04/01 Detect WebSocket upgrade messages having multiple Connection directives
// ZAP: 2021/05/11 Fixed conversion of Request Method to/from CONNECT
// ZAP: 2021/05/14 Add missing override annotation.
+// ZAP: 2022/01/14 Added setErrorResponse method
+
package org.parosproxy.paros.network;
import java.net.HttpCookie;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -74,11 +77,14 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
+import javax.net.ssl.SSLException;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.parosproxy.paros.Constant;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Model;
import org.zaproxy.zap.eventBus.Event;
@@ -1239,4 +1245,53 @@ public Map toEventData() {
public String getType() {
return MESSAGE_TYPE;
}
+
+ public void setErrorResponse(Exception cause) {
+ StringBuilder strBuilder = new StringBuilder(250);
+ if (cause instanceof SSLException) {
+ strBuilder.append(Constant.messages.getString("network.ssl.error.connect"));
+ strBuilder.append(this.getRequestHeader().getURI().toString()).append('\n');
+ strBuilder
+ .append(Constant.messages.getString("network.ssl.error.exception"))
+ .append(cause.getMessage())
+ .append('\n');
+ strBuilder
+ .append(Constant.messages.getString("network.ssl.error.exception.rootcause"))
+ .append(ExceptionUtils.getRootCauseMessage(cause))
+ .append('\n');
+ strBuilder.append(
+ Constant.messages.getString(
+ "network.ssl.error.help",
+ Constant.messages.getString("network.ssl.error.help.url")));
+
+ strBuilder.append("\n\nStack Trace:\n");
+ for (String stackTraceFrame : ExceptionUtils.getRootCauseStackTrace(cause)) {
+ strBuilder.append(stackTraceFrame).append('\n');
+ }
+ } else {
+ strBuilder
+ .append(cause.getClass().getName())
+ .append(": ")
+ .append(cause.getLocalizedMessage())
+ .append("\n\nStack Trace:\n");
+ for (String stackTraceFrame : ExceptionUtils.getRootCauseStackTrace(cause)) {
+ strBuilder.append(stackTraceFrame).append('\n');
+ }
+ }
+
+ String message = strBuilder.toString();
+
+ HttpResponseHeader responseHeader;
+ try {
+ responseHeader = new HttpResponseHeader("HTTP/1.1 400 ZAP IO Error");
+ responseHeader.setHeader(HttpHeader.CONTENT_TYPE, "text/plain; charset=UTF-8");
+ responseHeader.setHeader(
+ HttpHeader.CONTENT_LENGTH,
+ Integer.toString(message.getBytes(StandardCharsets.UTF_8).length));
+ this.setResponseHeader(responseHeader);
+ this.setResponseBody(message);
+ } catch (HttpMalformedHeaderException e) {
+ log.error("Failed to create error response:", e);
+ }
+ }
}
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 da56e84b23d..cdd7cdbb9b1 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
@@ -75,6 +75,7 @@ public static enum State {
private int maxResultsToList = 0;
private final List hRefs = Collections.synchronizedList(new ArrayList<>());
+ private final List hRefsWithError = Collections.synchronizedList(new ArrayList<>());
private final List alerts = Collections.synchronizedList(new ArrayList<>());
private ScheduledExecutorService scheduler;
@@ -273,7 +274,8 @@ public ActiveScanTableModel getMessagesTableModel() {
}
@Override
- public void notifyNewMessage(final HttpMessage msg) {
+ public void notifyNewTaskResult(final ScannerTaskResult scannerTaskResult) {
+ HttpMessage msg = scannerTaskResult.getHttpMessage();
HistoryReference hRef = msg.getHistoryRef();
if (hRef == null) {
try {
@@ -283,12 +285,12 @@ public void notifyNewMessage(final HttpMessage msg) {
HistoryReference.TYPE_SCANNER_TEMPORARY,
msg);
msg.setHistoryRef(null);
- hRefs.add(hRef.getHistoryId());
+ addHref(hRef.getHistoryId(), scannerTaskResult.isProcessed());
} catch (HttpMalformedHeaderException | DatabaseException e) {
log.error(e.getMessage(), e);
}
} else {
- hRefs.add(hRef.getHistoryId());
+ addHref(hRef.getHistoryId(), scannerTaskResult.isProcessed());
}
this.rcTotals.incResponseCodeCount(msg.getResponseHeader().getStatusCode());
@@ -299,17 +301,26 @@ public void notifyNewMessage(final HttpMessage msg) {
if (this.rcTotals.getTotal() > this.maxResultsToList) {
removeFirstHistoryReferenceInEdt();
}
- addHistoryReferenceInEdt(hRef);
+ addHistoryReferenceInEdt(hRef, scannerTaskResult);
}
}
- private void addHistoryReferenceInEdt(final HistoryReference hRef) {
+ private void addHref(int historyId, boolean noError){
+ if(noError){
+ hRefs.add(historyId);
+ } else{
+ hRefsWithError.add(historyId);
+ }
+ }
+
+ private void addHistoryReferenceInEdt(
+ final HistoryReference hRef, ScannerTaskResult scannerTaskResult) {
EventQueue.invokeLater(
new Runnable() {
@Override
public void run() {
- messagesTableModel.addHistoryReference(hRef);
+ messagesTableModel.addEntry(hRef, scannerTaskResult);
}
});
}
@@ -374,6 +385,21 @@ public List getMessagesIds() {
return hRefs;
}
+ /**
+ * Returns the IDs of all messages sent/created during the scan which threw an error (e.g. IOException). The message must be recreated
+ * with a HistoryReference.
+ *
+ * Note: Iterations must be {@code synchronized} on returned object. Failing
+ * to do so might result in {@code ConcurrentModificationException}.
+ *
+ * @return the IDs of all the messages sent/created during the scan which threw an error (e.g. IOException)
+ * @see HistoryReference
+ * @see ConcurrentModificationException
+ */
+ public List getMessagesIdsWithError() {
+ return hRefsWithError;
+ }
+
/**
* Returns the IDs of all alerts raised during the scan.
*
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanAPI.java
index 7f06d990aa3..85b6ff07ac7 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanAPI.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanAPI.java
@@ -112,6 +112,7 @@ public class ActiveScanAPI extends ApiImplementor {
private static final String VIEW_STATUS = "status";
private static final String VIEW_SCANS = "scans";
private static final String VIEW_MESSAGES_IDS = "messagesIds";
+ private static final String VIEW_MESSAGES_IDS_WITH_ERROR = "messagesIdsWithError";
private static final String VIEW_ALERTS_IDS = "alertsIds";
private static final String VIEW_EXCLUDED_FROM_SCAN = "excludedFromScan";
private static final String VIEW_SCANNERS = "scanners";
@@ -261,6 +262,7 @@ public ActiveScanAPI(ExtensionActiveScan controller) {
this.addApiView(new ApiView(VIEW_STATUS, null, new String[] {PARAM_SCAN_ID}));
this.addApiView(new ApiView(VIEW_SCAN_PROGRESS, null, new String[] {PARAM_SCAN_ID}));
this.addApiView(new ApiView(VIEW_MESSAGES_IDS, new String[] {PARAM_SCAN_ID}));
+ this.addApiView(new ApiView(VIEW_MESSAGES_IDS_WITH_ERROR, new String[] {PARAM_SCAN_ID}));
this.addApiView(new ApiView(VIEW_ALERTS_IDS, new String[] {PARAM_SCAN_ID}));
this.addApiView(new ApiView(VIEW_SCANS));
this.addApiView(new ApiView(VIEW_SCAN_POLICY_NAMES));
@@ -1038,6 +1040,16 @@ public ApiResponse handleApiView(String name, JSONObject params) throws ApiExcep
}
result = resultList;
break;
+ case VIEW_MESSAGES_IDS_WITH_ERROR:
+ resultList = new ApiResponseList(name);
+ activeScan = getActiveScan(params);
+ synchronized (activeScan.getMessagesIdsWithError()) {
+ for (Integer id : activeScan.getMessagesIdsWithError()) {
+ resultList.addItem(new ApiResponseElement("id", id.toString()));
+ }
+ }
+ result = resultList;
+ break;
case VIEW_ALERTS_IDS:
resultList = new ApiResponseList(name);
activeScan = getActiveScan(params);
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanPanel.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanPanel.java
index 5fea48a5a90..61bd53d6891 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanPanel.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanPanel.java
@@ -39,7 +39,6 @@
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.core.scanner.HostProcess;
import org.parosproxy.paros.core.scanner.ScannerListener;
-import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.model.ScanController;
import org.zaproxy.zap.model.ScanListenner2;
@@ -75,7 +74,7 @@ public class ActiveScanPanel extends ScanPanel2 {
+
+ private final boolean successful;
+ private final String label;
+
+ public ActiveScanProcessedCellItem(boolean successful, String label) {
+ this.successful = successful;
+ this.label = label;
+ }
+
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (successful ? 1231 : 1237);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ActiveScanProcessedCellItem other = (ActiveScanProcessedCellItem) obj;
+ if (successful != other.successful) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(ActiveScanProcessedCellItem other) {
+ if (other == null) {
+ return 1;
+ }
+ if (successful && !other.successful) {
+ return 1;
+ } else if (!successful && other.successful) {
+ return -1;
+ }
+ return label.compareTo(other.label);
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTable.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTable.java
new file mode 100644
index 00000000000..88573fcc042
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTable.java
@@ -0,0 +1,113 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2022 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.extension.ascan;
+
+import java.awt.Component;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import org.jdesktop.swingx.decorator.AbstractHighlighter;
+import org.jdesktop.swingx.decorator.ComponentAdapter;
+import org.jdesktop.swingx.renderer.DefaultTableRenderer;
+import org.jdesktop.swingx.renderer.IconAware;
+import org.jdesktop.swingx.renderer.IconValues;
+import org.jdesktop.swingx.renderer.MappedValue;
+import org.jdesktop.swingx.renderer.StringValues;
+import org.zaproxy.zap.view.table.HistoryReferencesTable;
+
+public class ActiveScanTable extends HistoryReferencesTable {
+
+ private static final long serialVersionUID = 1L;
+
+ public ActiveScanTable(ActiveScanTableModel model) {
+ super(model);
+
+ getColumnExt(0)
+ .setCellRenderer(
+ new DefaultTableRenderer(
+ new MappedValue(StringValues.EMPTY, IconValues.NONE),
+ JLabel.CENTER));
+
+ getColumnExt(0).setHighlighters(new ProcessedCellItemIconHighlighter(0));
+ }
+
+ /**
+ * A {@link org.jdesktop.swingx.decorator.Highlighter Highlighter} for a column that indicates,
+ * using icons and text, whether or not an entry was processed, that is, is or not in scope.
+ *
+ * The expected type/class of the cell values is {@code ProcessedCellItem}.
+ */
+ private static class ProcessedCellItemIconHighlighter extends AbstractHighlighter {
+
+ /** The icon that indicates the entry was processed. */
+ private static final ImageIcon PROCESSED_ICON =
+ new ImageIcon(ActiveScanTable.class.getResource("/resource/icon/16/152.png"));
+
+ /** The icon that indicates the entry was not processed. */
+ private static final ImageIcon NOT_PROCESSED_ICON =
+ new ImageIcon(ActiveScanTable.class.getResource("/resource/icon/16/149.png"));
+
+ private final int columnIndex;
+
+ public ProcessedCellItemIconHighlighter(final int columnIndex) {
+ this.columnIndex = columnIndex;
+ }
+
+ @Override
+ protected Component doHighlight(Component component, ComponentAdapter adapter) {
+ ActiveScanProcessedCellItem cell =
+ (ActiveScanProcessedCellItem) adapter.getValue(columnIndex);
+
+ boolean processed = cell.isSuccessful();
+ Icon icon = getProcessedIcon(processed);
+ if (component instanceof IconAware) {
+ ((IconAware) component).setIcon(icon);
+ } else if (component instanceof JLabel) {
+ ((JLabel) component).setIcon(icon);
+ }
+
+ if (component instanceof JLabel) {
+ ((JLabel) component).setText(processed ? "" : cell.getLabel());
+ }
+
+ return component;
+ }
+
+ private static Icon getProcessedIcon(final boolean processed) {
+ return processed ? PROCESSED_ICON : NOT_PROCESSED_ICON;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
Overridden to return true if the component is of type IconAware or of type JLabel,
+ * false otherwise.
+ *
+ *
Note: special casing JLabel is for backward compatibility - application highlighting
+ * code which doesn't use the Swingx renderers would stop working otherwise.
+ */
+ // Method/JavaDoc copied from
+ // org.jdesktop.swingx.decorator.IconHighlighter#canHighlight(Component, ComponentAdapter)
+ @Override
+ protected boolean canHighlight(final Component component, final ComponentAdapter adapter) {
+ return component instanceof IconAware || component instanceof JLabel;
+ }
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableEntry.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableEntry.java
new file mode 100644
index 00000000000..284f6611fae
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableEntry.java
@@ -0,0 +1,38 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2022 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.extension.ascan;
+
+import org.parosproxy.paros.model.HistoryReference;
+import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableEntry;
+
+public class ActiveScanTableEntry extends DefaultHistoryReferencesTableEntry {
+
+ private ActiveScanProcessedCellItem processedCellItem;
+
+ public ActiveScanTableEntry(
+ HistoryReference historyReference, ActiveScanProcessedCellItem processedCellItem) {
+ super(historyReference, ActiveScanTableModel.COLUMNS);
+ this.processedCellItem = processedCellItem;
+ }
+
+ public ActiveScanProcessedCellItem getProcessedCellItem() {
+ return processedCellItem;
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableModel.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableModel.java
index f149caf989f..3ae810b5eb6 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableModel.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ActiveScanTableModel.java
@@ -19,25 +19,82 @@
*/
package org.zaproxy.zap.extension.ascan;
-import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableModel;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.parosproxy.paros.Constant;
+import org.parosproxy.paros.model.HistoryReference;
+import org.zaproxy.zap.view.table.CustomColumn;
+import org.zaproxy.zap.view.table.DefaultCustomColumnHistoryReferencesTableModel;
-public class ActiveScanTableModel extends DefaultHistoryReferencesTableModel {
+public class ActiveScanTableModel
+ extends DefaultCustomColumnHistoryReferencesTableModel {
private static final long serialVersionUID = 5732679524771190690L;
+ private static final ActiveScanProcessedCellItem SUCCESSFULLY_PROCESSED_CELL_ITEM;
+ private Map cacheProcessedCellItems;
+ public static final Column[] COLUMNS =
+ new Column[] {
+ Column.CUSTOM,
+ Column.HREF_ID,
+ Column.REQUEST_TIMESTAMP,
+ Column.RESPONSE_TIMESTAMP,
+ Column.METHOD,
+ Column.URL,
+ Column.STATUS_CODE,
+ Column.STATUS_REASON,
+ Column.RTT,
+ Column.SIZE_RESPONSE_HEADER,
+ Column.SIZE_RESPONSE_BODY
+ };
+
+ private static final List> CUSTOM_COLUMNS =
+ Arrays.asList(createProcessedColumn());
+
+ static {
+ SUCCESSFULLY_PROCESSED_CELL_ITEM =
+ new ActiveScanProcessedCellItem(
+ true,
+ Constant.messages.getString(
+ "ascan.panel.tab.scannedMessages.column.processed.successfully"));
+ }
public ActiveScanTableModel() {
- super(
- new Column[] {
- Column.HREF_ID,
- Column.REQUEST_TIMESTAMP,
- Column.RESPONSE_TIMESTAMP,
- Column.METHOD,
- Column.URL,
- Column.STATUS_CODE,
- Column.STATUS_REASON,
- Column.RTT,
- Column.SIZE_RESPONSE_HEADER,
- Column.SIZE_RESPONSE_BODY
- });
+ super(COLUMNS, CUSTOM_COLUMNS, ActiveScanTableEntry.class);
+ cacheProcessedCellItems = new HashMap<>();
+ }
+
+ private static CustomColumn createProcessedColumn() {
+ return new CustomColumn(
+ String.class,
+ Constant.messages.getString("ascan.panel.tab.scannedMessages.column.processed")) {
+
+ @Override
+ public Object getValue(ActiveScanTableEntry model) {
+ return model.getProcessedCellItem();
+ }
+ };
+ }
+
+ public void addEntry(HistoryReference hRef, ScannerTaskResult scannerTaskResult) {
+ addEntry(new ActiveScanTableEntry(hRef, getProcessedCellItem(scannerTaskResult)));
+ }
+
+ private ActiveScanProcessedCellItem getProcessedCellItem(ScannerTaskResult scannerTaskResult) {
+ if (scannerTaskResult.isProcessed()) {
+ return SUCCESSFULLY_PROCESSED_CELL_ITEM;
+ }
+ ActiveScanProcessedCellItem processedCellItem =
+ cacheProcessedCellItems.get(scannerTaskResult.getReasonNotProcessed());
+ if (processedCellItem == null) {
+ processedCellItem =
+ new ActiveScanProcessedCellItem(
+ scannerTaskResult.isProcessed(),
+ scannerTaskResult.getReasonNotProcessed());
+ cacheProcessedCellItems.put(
+ scannerTaskResult.getReasonNotProcessed(), processedCellItem);
+ }
+ return processedCellItem;
}
}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/AttackModeScanner.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/AttackModeScanner.java
index aafaa8c6e8e..4a9a0f1dd12 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/ascan/AttackModeScanner.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/AttackModeScanner.java
@@ -38,7 +38,6 @@
import org.parosproxy.paros.model.Session;
import org.parosproxy.paros.model.SiteMapEventPublisher;
import org.parosproxy.paros.model.SiteNode;
-import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.ZAP;
import org.zaproxy.zap.eventBus.Event;
@@ -381,8 +380,8 @@ public void alertFound(Alert alert) {
}
@Override
- public void notifyNewMessage(HttpMessage msg) {
- ascanWrapper.notifyNewMessage(msg);
+ public void notifyNewTaskResult(ScannerTaskResult scannerTaskResult) {
+ ascanWrapper.notifyNewMessage(scannerTaskResult);
}
public void shutdown() {
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScannerTaskResult.java b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScannerTaskResult.java
new file mode 100644
index 00000000000..bbb4d843982
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/extension/ascan/ScannerTaskResult.java
@@ -0,0 +1,64 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2022 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.extension.ascan;
+
+import org.parosproxy.paros.network.HttpMessage;
+
+/**
+ * Contains the message and processing details.
+ *
+ * @since 2.12.0
+ */
+public class ScannerTaskResult {
+
+ private final HttpMessage httpMessage;
+ private final boolean processed;
+ private final String reasonNotProcessed;
+
+ public ScannerTaskResult(HttpMessage httpMessage) {
+ this(httpMessage, true, "");
+ }
+
+ public ScannerTaskResult(HttpMessage httpMessage, String reasonNotProcessed) {
+ this(httpMessage, false, reasonNotProcessed);
+ }
+
+ private ScannerTaskResult(
+ HttpMessage httpMessage, boolean processed, String reasonNotProcessed) {
+ if (reasonNotProcessed == null) {
+ throw new IllegalArgumentException("Parameter reason must not be null.");
+ }
+ this.httpMessage = httpMessage;
+ this.processed = processed;
+ this.reasonNotProcessed = reasonNotProcessed;
+ }
+
+ public HttpMessage getHttpMessage() {
+ return httpMessage;
+ }
+
+ public boolean isProcessed() {
+ return processed;
+ }
+
+ public String getReasonNotProcessed() {
+ return reasonNotProcessed;
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/CustomColumn.java b/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/CustomColumn.java
index 48a1c216446..7481da82916 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/CustomColumn.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/CustomColumn.java
@@ -19,7 +19,7 @@
*/
package org.zaproxy.zap.extension.callback.ui;
-/** @deprecated (2.11.0) Superseded by the OAST add-on. */
+/** @deprecated (2.11.0) {@link org.zaproxy.zap.view.table.CustomColumn}. */
@Deprecated
public abstract class CustomColumn {
Class> columnClass;
diff --git a/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/DefaultCustomColumnHistoryReferencesTableModel.java b/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/DefaultCustomColumnHistoryReferencesTableModel.java
index f8ab7903a3c..249fd077e67 100644
--- a/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/DefaultCustomColumnHistoryReferencesTableModel.java
+++ b/zap/src/main/java/org/zaproxy/zap/extension/callback/ui/DefaultCustomColumnHistoryReferencesTableModel.java
@@ -25,7 +25,10 @@
import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableEntry;
import org.zaproxy.zap.view.table.DefaultHistoryReferencesTableModel;
-/** @deprecated (2.11.0) Superseded by the OAST add-on. */
+/**
+ * @deprecated (2.11.0) Superseded by {@link
+ * org.zaproxy.zap.view.table.DefaultCustomColumnHistoryReferencesTableModel}.
+ */
@Deprecated
public class DefaultCustomColumnHistoryReferencesTableModel<
T extends DefaultHistoryReferencesTableEntry>
diff --git a/zap/src/main/java/org/zaproxy/zap/spider/SpiderTask.java b/zap/src/main/java/org/zaproxy/zap/spider/SpiderTask.java
index fd45ec4b1e8..8b59fd3f09d 100644
--- a/zap/src/main/java/org/zaproxy/zap/spider/SpiderTask.java
+++ b/zap/src/main/java/org/zaproxy/zap/spider/SpiderTask.java
@@ -24,16 +24,12 @@
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
-import java.nio.charset.StandardCharsets;
import java.util.List;
-import javax.net.ssl.SSLException;
import net.htmlparser.jericho.Source;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
-import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.extension.history.ExtensionHistory;
@@ -43,7 +39,6 @@
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
-import org.parosproxy.paros.network.HttpResponseHeader;
import org.zaproxy.zap.spider.filters.ParseFilter;
import org.zaproxy.zap.spider.filters.ParseFilter.FilterResult;
import org.zaproxy.zap.spider.parser.SpiderParser;
@@ -177,7 +172,7 @@ private void runImpl() {
try {
fetchResource(msg);
} catch (Exception e) {
- setErrorResponse(msg, e);
+ msg.setErrorResponse(e);
parent.notifyListenersSpiderTaskResult(
new SpiderTaskResult(msg, getSkippedMessage("ioerror")));
return;
@@ -304,55 +299,6 @@ private void deleteHistoryReference() {
}
}
- private void setErrorResponse(HttpMessage msg, Exception cause) {
- StringBuilder strBuilder = new StringBuilder(250);
- if (cause instanceof SSLException) {
- strBuilder.append(Constant.messages.getString("network.ssl.error.connect"));
- strBuilder.append(msg.getRequestHeader().getURI().toString()).append('\n');
- strBuilder
- .append(Constant.messages.getString("network.ssl.error.exception"))
- .append(cause.getMessage())
- .append('\n');
- strBuilder
- .append(Constant.messages.getString("network.ssl.error.exception.rootcause"))
- .append(ExceptionUtils.getRootCauseMessage(cause))
- .append('\n');
- strBuilder.append(
- Constant.messages.getString(
- "network.ssl.error.help",
- Constant.messages.getString("network.ssl.error.help.url")));
-
- strBuilder.append("\n\nStack Trace:\n");
- for (String stackTraceFrame : ExceptionUtils.getRootCauseStackTrace(cause)) {
- strBuilder.append(stackTraceFrame).append('\n');
- }
- } else {
- strBuilder
- .append(cause.getClass().getName())
- .append(": ")
- .append(cause.getLocalizedMessage())
- .append("\n\nStack Trace:\n");
- for (String stackTraceFrame : ExceptionUtils.getRootCauseStackTrace(cause)) {
- strBuilder.append(stackTraceFrame).append('\n');
- }
- }
-
- String message = strBuilder.toString();
-
- HttpResponseHeader responseHeader;
- try {
- responseHeader = new HttpResponseHeader("HTTP/1.1 400 ZAP IO Error");
- responseHeader.setHeader(HttpHeader.CONTENT_TYPE, "text/plain; charset=UTF-8");
- responseHeader.setHeader(
- HttpHeader.CONTENT_LENGTH,
- Integer.toString(message.getBytes(StandardCharsets.UTF_8).length));
- msg.setResponseHeader(responseHeader);
- msg.setResponseBody(message);
- } catch (HttpMalformedHeaderException e) {
- log.error("Failed to create error response:", e);
- }
- }
-
/**
* Process a resource, searching for links (uris) to other resources.
*
diff --git a/zap/src/main/java/org/zaproxy/zap/view/table/CustomColumn.java b/zap/src/main/java/org/zaproxy/zap/view/table/CustomColumn.java
new file mode 100644
index 00000000000..cb0c581b8f1
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/view/table/CustomColumn.java
@@ -0,0 +1,44 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2022 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.view.table;
+
+public abstract class CustomColumn {
+ Class> columnClass;
+ String name;
+
+ public CustomColumn(Class> columnClass, String name) {
+ this.columnClass = columnClass;
+ this.name = name;
+ }
+
+ public Class> getColumnClass() {
+ return columnClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public abstract Object getValue(T model);
+
+ public Object getPrototypeValue() {
+ return null;
+ }
+}
diff --git a/zap/src/main/java/org/zaproxy/zap/view/table/DefaultCustomColumnHistoryReferencesTableModel.java b/zap/src/main/java/org/zaproxy/zap/view/table/DefaultCustomColumnHistoryReferencesTableModel.java
new file mode 100644
index 00000000000..03a6b23a693
--- /dev/null
+++ b/zap/src/main/java/org/zaproxy/zap/view/table/DefaultCustomColumnHistoryReferencesTableModel.java
@@ -0,0 +1,141 @@
+/*
+ * Zed Attack Proxy (ZAP) and its related class files.
+ *
+ * ZAP is an HTTP/HTTPS proxy for assessing web application security.
+ *
+ * Copyright 2022 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.view.table;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultCustomColumnHistoryReferencesTableModel<
+ T extends DefaultHistoryReferencesTableEntry>
+ extends DefaultHistoryReferencesTableModel {
+
+ private static final long serialVersionUID = 1L;
+ private Map> customColumns;
+ private Class type;
+
+ /**
+ * The class adds custom columns to the {@code DefaultHistoryReferencesTableModel}.
+ *
+ * @param columns Add the {@code Column.CUSTOM} at the desired position in the column list.
+ * @param customColumns Provide the implementations of the custom columns you passed to the
+ * {@code columns} parameter. When the count does not match then empty columns are created
+ * on that particular index.
+ */
+ public DefaultCustomColumnHistoryReferencesTableModel(
+ final Column[] columns, final List> customColumns, Class type) {
+ super(columns);
+ this.type = type;
+ this.customColumns = createCustomColumnMap(columns, customColumns);
+ }
+
+ private Map> createCustomColumnMap(
+ Column[] columns, List> customColumns) {
+ Map> customColumnMap = new HashMap<>();
+
+ int customColumnIndex = 0;
+ for (int i = 0; i < columns.length; i++) {
+ if (columns[i] == Column.CUSTOM) {
+ CustomColumn customColumn =
+ getCustomColumnIfExists(customColumns, customColumnIndex);
+ customColumnMap.put(i, customColumn);
+ customColumnIndex++;
+ }
+ }
+
+ return customColumnMap;
+ }
+
+ private CustomColumn getCustomColumnIfExists(
+ List> customColumns, Integer index) {
+ if (index < customColumns.size()) {
+ return customColumns.get(index);
+ }
+ return emptyColumn();
+ }
+
+ private CustomColumn emptyColumn() {
+ return new CustomColumn(String.class, "") {
+
+ @Override
+ public Object getValue(T model) {
+ return "";
+ }
+ };
+ }
+
+ private CustomColumn getCustomColumn(int columnIndex) {
+ return customColumns.get(columnIndex);
+ }
+
+ @Override
+ public Class> getColumnClass(int columnIndex) {
+ if (isCustomColumn(columnIndex)) {
+ return getCustomColumn(columnIndex).getColumnClass();
+ }
+
+ return super.getColumnClass(getColumn(columnIndex));
+ }
+
+ @Override
+ public String getColumnName(int columnIndex) {
+ if (isCustomColumn(columnIndex)) {
+ return getCustomColumn(columnIndex).getName();
+ }
+
+ return super.getColumnName(columnIndex);
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ if (isCustomColumn(columnIndex)) {
+ if (rowIndex >= getRowCount()) {
+ return null;
+ }
+
+ DefaultHistoryReferencesTableEntry entry = getEntry(rowIndex);
+ return getCustomColumn(columnIndex).getValue(uncheckedCast(entry));
+ }
+
+ return super.getValueAt(rowIndex, columnIndex);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private T uncheckedCast(DefaultHistoryReferencesTableEntry entry) {
+ if (!type.isInstance(entry)) {
+ return null;
+ }
+
+ try {
+ return (T) entry;
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object getPrototypeValue(int columnIndex) {
+ if (isCustomColumn(columnIndex)) {
+ return getCustomColumn(columnIndex).getPrototypeValue();
+ }
+
+ return super.getPrototypeValue(columnIndex);
+ }
+}
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 31cff469c40..00e1bb7bf12 100644
--- a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties
+++ b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties
@@ -395,7 +395,9 @@ ascan.api.view.attackModeQueue =
ascan.api.view.excludedParams = Gets all the parameters that are excluded. For each parameter the following are shown: the name, the URL, and the parameter type.
ascan.api.view.excludedParamTypes = Gets all the types of excluded parameters. For each type the following are shown: the ID and the name.
ascan.api.view.messagesIds = Gets the IDs of the messages sent during the scan with the given ID. A message can be obtained with 'message' core view.
-ascan.api.view.messagesIds.param.scanId =
+ascan.api.view.messagesIds.param.scanId =
+ascan.api.view.messagesIdsWithError = Gets the IDs of the messages sent during the scan with the given ID and which threw an error (e.g. IOException). A message can be obtained with 'message' core view.
+ascan.api.view.messagesIdsWithError.param.scanId =
ascan.api.view.optionAddQueryParam = Tells whether or not the active scanner should add a query parameter to GET request that don't have parameters to start with.
ascan.api.view.optionAllowAttackOnStart =
ascan.api.view.optionAttackPolicy =
@@ -590,13 +592,15 @@ ascan.toolbar.button.new = New Scan
ascan.toolbar.button.stop = Stop Active Scan
ascan.toolbar.button.unpause = Resume Active Scan
ascan.toolbar.confirm.clear = Are you sure you want to clear all completed scans?
- ascan.toolbar.newalerts.label = New Alerts:
+ascan.toolbar.newalerts.label = New Alerts:
ascan.toolbar.requests.label = Num Requests:
ascan.toolbar.progress.label = Progress:
ascan.toolbar.progress.select = --Select Scan--
ascan.url.popup = Active Scan single URL
ascan.panel.tab.scannedMessages = Sent Messages
-ascan.panel.tab.filteredMessages = Filtered Messages
+ascan.panel.tab.scannedMessages.column.processed = Processed
+ascan.panel.tab.scannedMessages.column.processed.successfully = Successfully
+ascan.panel.tab.filteredMessages = Filtered Messages
ascan.filter.table.header.url = URL
ascan.filter.table.header.reason = Reason
diff --git a/zap/zap.gradle.kts b/zap/zap.gradle.kts
index be95ba65ade..4a6afcf7a08 100644
--- a/zap/zap.gradle.kts
+++ b/zap/zap.gradle.kts
@@ -160,7 +160,7 @@ val japicmp by tasks.registering(JapicmpTask::class) {
classExcludes = listOf()
- methodExcludes = listOf()
+ methodExcludes = listOf("org.parosproxy.paros.core.scanner.ScannerListener#notifyNewTaskResult(org.zaproxy.zap.extension.ascan.ScannerTaskResult)")
richReport {
destinationDir = file("$buildDir/reports/japicmp/")