Skip to content

Commit 6006739

Browse files
committed
reports: Support nodeNames and systemic counts
Signed-off-by: Simon Bennetts <[email protected]>
1 parent cb79654 commit 6006739

File tree

23 files changed

+218
-39
lines changed

23 files changed

+218
-39
lines changed

addOns/reports/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
## Unreleased
77
### Changed
88
- Update dependencies.
9+
- All reports to support nodeName and systemic counts.
910

1011
## [0.41.0] - 2025-09-04
1112
### Changed

addOns/reports/src/main/java/org/zaproxy/addon/reports/ReportHelper.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.zaproxy.addon.reports;
2121

22+
import java.lang.reflect.Method;
2223
import java.net.URI;
2324
import java.net.URISyntaxException;
2425
import java.util.ArrayList;
@@ -302,4 +303,64 @@ public static HttpMessage getHttpMessage(int id) {
302303
}
303304
return null;
304305
}
306+
307+
/**
308+
* Returns the nodeName for the alert. This will only work from ZAP 2.17 onwards, it is not
309+
* available in earlier versions.
310+
*/
311+
public static String getNodeName(Alert alert) {
312+
if (alert == null) {
313+
return null;
314+
}
315+
try {
316+
Method method = alert.getClass().getMethod("getNodeName");
317+
Object ret = method.invoke(alert);
318+
if (ret != null && ret instanceof String str) {
319+
return str;
320+
}
321+
} catch (Exception e) {
322+
// Ignore
323+
}
324+
return null;
325+
}
326+
327+
/**
328+
* Returns whether the alert node is systemic. This will only work from ZAP 2.17 onwards, it is
329+
* not available in earlier versions.
330+
*/
331+
public static boolean isSystemic(AlertNode node) {
332+
if (node == null) {
333+
return false;
334+
}
335+
try {
336+
Method method = node.getClass().getMethod("isSystemic");
337+
Object ret = method.invoke(node);
338+
if (ret != null && ret instanceof Boolean bool) {
339+
return bool;
340+
}
341+
} catch (Exception e) {
342+
// Ignore
343+
}
344+
return false;
345+
}
346+
347+
/**
348+
* Returns whether the alert is systemic. This will only work from ZAP 2.17 onwards, it is not
349+
* available in earlier versions.
350+
*/
351+
public static boolean isSystemic(Alert alert) {
352+
if (alert == null) {
353+
return false;
354+
}
355+
try {
356+
Method method = alert.getClass().getMethod("isSystemic");
357+
Object ret = method.invoke(alert);
358+
if (ret != null && ret instanceof Boolean bool) {
359+
return bool;
360+
}
361+
} catch (Exception e) {
362+
// Ignore
363+
}
364+
return false;
365+
}
305366
}

addOns/reports/src/main/javahelp/org/zaproxy/addon/reports/resources/help/contents/report-traditional-json-plus.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ <H3>Sample</H3>
5353
"instances":[
5454
{
5555
"uri": "http://localhost:8080/bodgeit/search.jsp?q=%3C%2Ffont%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Cfont%3E",
56+
"nodeName": "http://localhost:8080/bodgeit/search.jsp (q)",
5657
"method": "GET",
5758
"param": "q",
5859
"attack": "&lt;/font&gt;&lt;scrIpt&gt;alert(1);&lt;/scRipt&gt;&lt;font&gt;",
@@ -65,6 +66,7 @@ <H3>Sample</H3>
6566
},
6667
{
6768
"uri": "http://localhost:8080/bodgeit/contact.jsp",
69+
"nodeName": "http://localhost:8080/bodgeit/contact.jsp",
6870
"method": "POST",
6971
"param": "comments",
7072
"attack": "&lt;/td&gt;&lt;scrIpt&gt;alert(1);&lt;/scRipt&gt;&lt;td&gt;",
@@ -77,6 +79,7 @@ <H3>Sample</H3>
7779
}
7880
],
7981
"count": "2",
82+
"systemic": false,
8083
"solution": "&lt;p&gt;Phase: Architecture and Design&lt;/p&gt;&lt;p&gt;Use a vetted library or framework that does not ...&lt;/p&gt;",
8184
"otherinfo": "",
8285
"reference": "&lt;p&gt;http://projects.webappsec.org/Cross-Site-Scripting&lt;/p&gt;&lt;p&gt;http://cwe.mitre.org/data/definitions/79.html&lt;/p&gt;",

addOns/reports/src/main/javahelp/org/zaproxy/addon/reports/resources/help/contents/report-traditional-json.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ <H3>Sample</H3>
3232
"instances":[
3333
{
3434
"uri": "http://localhost:8080/bodgeit/search.jsp?q=%3C%2Ffont%3E%3CscrIpt%3Ealert%281%29%3B%3C%2FscRipt%3E%3Cfont%3E",
35+
"nodeName": "http://localhost:8080/bodgeit/search.jsp (q)",
3536
"method": "GET",
3637
"param": "q",
3738
"attack": "&lt;/font&gt;&lt;scrIpt&gt;alert(1);&lt;/scRipt&gt;&lt;font&gt;",
@@ -40,6 +41,7 @@ <H3>Sample</H3>
4041
},
4142
{
4243
"uri": "http://localhost:8080/bodgeit/contact.jsp",
44+
"nodeName": "http://localhost:8080/bodgeit/contact.jsp",
4345
"method": "POST",
4446
"param": "comments",
4547
"attack": "&lt;/td&gt;&lt;scrIpt&gt;alert(1);&lt;/scRipt&gt;&lt;td&gt;",
@@ -48,6 +50,7 @@ <H3>Sample</H3>
4850
}
4951
],
5052
"count": "2",
53+
"systemic": false,
5154
"solution": "&lt;p&gt;Phase: Architecture and Design&lt;/p&gt;&lt;p&gt;Use a vetted library or framework that does not ...&lt;/p&gt;",
5255
"otherinfo": "",
5356
"reference": "&lt;p&gt;http://projects.webappsec.org/Cross-Site-Scripting&lt;/p&gt;&lt;p&gt;http://cwe.mitre.org/data/definitions/79.html&lt;/p&gt;",

addOns/reports/src/main/javahelp/org/zaproxy/addon/reports/resources/help/contents/report-traditional-markdown.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,21 @@ <H4>
7171
CSRF has primarily been used to perform an action against a target site using the victim's privileges, but recent techniques have been discovered to disclose information by gaining access to the response. The risk of information disclosure is dramatically increased when the target site is vulnerable to XSS, because XSS can be used as a platform for CSRF, allowing the attack to operate within the bounds of the same-origin policy.
7272

7373
* URL: http://localhost:8080/bodgeit/advanced.jsp
74+
* Node Name: http://localhost:8080/bodgeit/advanced.jsp
7475
* Method: `GET`
7576
* Parameter: ``
7677
* Attack: ``
7778
* Evidence: `&lt;form id="advanced" name="advanced" method="POST" onsubmit="return validateForm(this);false;"&gt;`
7879
* Other Info: ``
7980
* URL: http://localhost:8080/bodgeit/advanced.jsp
81+
* Node Name: http://localhost:8080/bodgeit/advanced.jsp
8082
* Method: `GET`
8183
* Parameter: ``
8284
* Attack: ``
8385
* Evidence: `&lt;form id="query" name="advanced" method="POST"&gt;`
8486
* Other Info: ``
8587
* URL: http://localhost:8080/bodgeit/basket.jsp
88+
* Node Name: http://localhost:8080/bodgeit/basket.jsp
8689
* Method: `GET`
8790
* Parameter: ``
8891
* Attack: ``

addOns/reports/src/main/javahelp/org/zaproxy/addon/reports/resources/help/contents/report-traditional-xml-plus.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ <H3>Sample</H3>
2828

2929
&lt;instance&gt;
3030
&lt;uri&gt;http://localhost:8080/bodgeit/js&lt;/uri&gt;
31+
&lt;nodeName&gt;http://localhost:8080/bodgeit/js&lt;/nodeName&gt;
3132
&lt;method&gt;GET&lt;/method&gt;
3233
&lt;param&gt;&lt;/param&gt;
3334
&lt;attack&gt;&lt;/attack&gt;
@@ -61,6 +62,7 @@ <H3>Sample</H3>
6162

6263
&lt;instance&gt;
6364
&lt;uri&gt;http://localhost:8080/bodgeit/js/util.js&lt;/uri&gt;
65+
&lt;nodeName&gt;http://localhost:8080/bodgeit/js/util.js&lt;/nodeName&gt;
6466
&lt;method&gt;GET&lt;/method&gt;
6567
&lt;param&gt;&lt;/param&gt;
6668
&lt;attack&gt;&lt;/attack&gt;
@@ -157,6 +159,7 @@ <H3>Sample</H3>
157159

158160
&lt;/instances&gt;
159161
&lt;count&gt;3&lt;/count&gt;
162+
&lt;systemic&gt;false&lt;/systemic&gt;
160163
&lt;solution&gt;&lt;/solution&gt;
161164
&lt;otherinfo&gt;NOTE: Because of its name this cookie may be important, but dropping it appears to have no effect: [JSESSIONID]
162165
Cookies that don&amp;apos;t have expected effects can reveal flaws in application logic. In the worst case, this can reveal where authentication via cookie token(s) is not actually enforced.

addOns/reports/src/main/javahelp/org/zaproxy/addon/reports/resources/help/contents/report-traditional-xml.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ <H3>Sample</H3>
2626
&lt;confidencedesc&gt;Medium&lt;/confidencedesc&gt;
2727
&lt;desc&gt;&lt;p&gt;A cross-site request forgery is an attack that involves forcing a victim to send an HTTP request to a target destination without their knowledge...&lt;/desc&gt;
2828
&lt;instances&gt;
29-
3029
&lt;instance&gt;
3130
&lt;uri&gt;http://localhost:8080/bodgeit/advanced.jsp&lt;/uri&gt;
31+
&lt;nodeName&gt;http://localhost:8080/bodgeit/advanced.jsp&lt;/nodeName&gt;
3232
&lt;method&gt;GET&lt;/method&gt;
3333
&lt;param&gt;&lt;/param&gt;
3434
&lt;attack&gt;&lt;/attack&gt;
@@ -38,6 +38,7 @@ <H3>Sample</H3>
3838

3939
&lt;instance&gt;
4040
&lt;uri&gt;http://localhost:8080/bodgeit/advanced.jsp&lt;/uri&gt;
41+
&lt;nodeName&gt;http://localhost:8080/bodgeit/advanced.jsp&lt;/nodeName&gt;
4142
&lt;method&gt;GET&lt;/method&gt;
4243
&lt;param&gt;&lt;/param&gt;
4344
&lt;attack&gt;&lt;/attack&gt;
@@ -47,13 +48,17 @@ <H3>Sample</H3>
4748

4849
&lt;instance&gt;
4950
&lt;uri&gt;http://localhost:8080/bodgeit/basket.jsp&lt;/uri&gt;
51+
&lt;nodeName&gt;http://localhost:8080/bodgeit/basket.jsp&lt;/nodeName&gt;
5052
&lt;method&gt;GET&lt;/method&gt;
5153
&lt;param&gt;&lt;/param&gt;
5254
&lt;attack&gt;&lt;/attack&gt;
5355
&lt;evidence&gt;&lt;form action=&quot;basket.jsp&quot; method=&quot;post&quot;&gt;&lt;/evidence&gt;
5456
&lt;otherinfo&gt;&lt;/otherinfo&gt;
5557
&lt;/instance&gt;
56-
58+
&lt;count&gt;2&lt;/count&gt;
59+
&lt;systemic&gt;false&lt;/systemic&gt;
60+
&lt;solution&gt;The solution&lt;/solution&gt;
61+
&lt;otherinfo&gt;The other info&lt;/otherinfo&gt;
5762

5863
</pre>
5964

addOns/reports/src/main/resources/org/zaproxy/addon/reports/resources/Messages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ reports.report.alerts.detail.description = Description
9595
reports.report.alerts.detail.evidence = Evidence
9696
reports.report.alerts.detail.instances = Instances
9797
reports.report.alerts.detail.method = Method
98+
reports.report.alerts.detail.nodename = Node Name
9899
reports.report.alerts.detail.otherinfo = Other Info
99100
reports.report.alerts.detail.param = Parameter
100101
reports.report.alerts.detail.pluginid = Plugin Id
@@ -113,6 +114,7 @@ reports.report.alerts.list = Alerts
113114
reports.report.alerts.list.name = Name
114115
reports.report.alerts.list.numinstances = Number of Instances
115116
reports.report.alerts.list.risklevel = Risk Level
117+
reports.report.alerts.list.systemic = Systemic
116118
reports.report.alerts.summary = Summary of Alerts
117119
reports.report.alerts.summary.numalerts = Number of Alerts
118120
reports.report.alerts.summary.risklevel = Risk Level

addOns/reports/src/main/zapHomeFiles/reports/modern/report.html

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,12 @@ <h3 th:text="#{report.alerts.list}">Alerts</h3>
205205
Name</a></td>
206206
<td align="center" th:class="${'risk-' + alert.risk}"
207207
th:text="${helper.getRiskString(alert.risk)}">Risk</td>
208-
<td align="center" th:text="${alert.childCount}">Count</td>
208+
<th:block th:if="${helper.isSystemic(alert)}">
209+
<td align="center" th:text="#{report.alerts.list.systemic}">Systemic</td>
210+
</th:block>
211+
<th:block th:unless="${helper.isSystemic(alert)}">
212+
<td align="center" th:text="${alert.childCount}">Count</td>
213+
</th:block>
209214
</tr>
210215
</table>
211216
</div>
@@ -437,6 +442,15 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
437442
<td width="80%"><a th:href="${instance.userObject.uri}"
438443
th:text="${instance.userObject.uri}" href="url.html">URL</a></td>
439444
</tr>
445+
<th:block
446+
th:if="${helper.getNodeName(instance.userObject) != null}">
447+
<tr>
448+
<td th:text="#{report.alerts.detail.nodename}" width="20%"
449+
class="indent2">Node Name</td>
450+
<td th:text="${helper.getNodeName(instance.userObject)}"
451+
width="80%">Node Name</td>
452+
</tr>
453+
</th:block>
440454
<tr>
441455
<td th:text="#{report.alerts.detail.method}" width="20%"
442456
class="indent2">Method</td>
@@ -543,7 +557,12 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
543557
</th:block>
544558
<tr>
545559
<td th:text="#{report.alerts.detail.instances}" width="20%">Instances</td>
546-
<td th:text="${alert.childCount}" width="80%">Instances</td>
560+
<th:block th:if="${helper.isSystemic(alert)}">
561+
<td th:text="#{report.alerts.list.systemic}" width="80%">Systemic</td>
562+
</th:block>
563+
<th:block th:unless="${helper.isSystemic(alert)}">
564+
<td th:text="${alert.childCount}" width="80%">Instances</td>
565+
</th:block>
547566
</tr>
548567
<tr>
549568
<td th:text="#{report.alerts.detail.solution}" width="20%">Solution</td>

addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/report.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,12 @@ <h3 th:text="#{report.alerts.list}">Alerts</h3>
199199
th:text="${alert.nodeName}" href="#plugin-pluginId">Alert Name</a></td>
200200
<td align="center" th:class="${'risk-' + alert.risk}"
201201
th:text="${helper.getRiskString(alert.risk)}">Risk</td>
202+
<th:block th:if="${helper.isSystemic(alert)}">
203+
<td align="center" th:text="#{report.alerts.list.systemic}">Systemic</td>
204+
</th:block>
205+
<th:block th:unless="${helper.isSystemic(alert)}">
202206
<td align="center" th:text="${alert.childCount}">Count</td>
207+
</th:block>
203208
</tr>
204209
</table>
205210
<div class="spacer-lg"></div>
@@ -410,6 +415,11 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
410415
<td width="80%"><a th:href="${instance.userObject.uri}"
411416
th:text="${instance.userObject.uri}" href="url.html">URL</a></td>
412417
</tr>
418+
<th:block th:if="${helper.getNodeName(instance.userObject) != null}"><tr>
419+
<td th:text="#{report.alerts.detail.nodename}" width="20%"
420+
class="indent2">Node Name</td>
421+
<td th:text="${helper.getNodeName(instance.userObject)}" width="80%">Node Name</td>
422+
</tr></th:block>
413423
<tr>
414424
<td th:text="#{report.alerts.detail.method}" width="20%"
415425
class="indent2">Method</td>
@@ -518,7 +528,12 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
518528
</th:block>
519529
<tr>
520530
<td th:text="#{report.alerts.detail.instances}" width="20%">Instances</td>
531+
<th:block th:if="${helper.isSystemic(alert)}">
532+
<td th:text="#{report.alerts.list.systemic}" width="80%">Systemic</td>
533+
</th:block>
534+
<th:block th:unless="${helper.isSystemic(alert)}">
521535
<td th:text="${alert.childCount}" width="80%">Instances</td>
536+
</th:block>
522537
</tr>
523538
<tr>
524539
<td th:text="#{report.alerts.detail.solution}" width="20%">Solution</td>

0 commit comments

Comments
 (0)