Skip to content

Commit ca20f98

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

File tree

18 files changed

+198
-37
lines changed

18 files changed

+198
-37
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/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>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,12 @@ <h3 th:text="#{report.alerts.list}">Alerts</h3>
324324
th:text="${alert.nodeName}" href="#oluginId">Alert Name</a></td>
325325
<td align="center" th:class="${'risk-' + alert.risk}"
326326
th:text="${helper.getRiskString(alert.risk)}">Risk</td>
327+
<th:block th:if="${helper.isSystemic(alert)}">
328+
<td align="center" th:text="#{report.alerts.list.systemic}">Systemic</td>
329+
</th:block>
330+
<th:block th:unless="${helper.isSystemic(alert)}">
327331
<td align="center" th:text="${alert.childCount}">Count</td>
332+
</th:block>
328333
</tr>
329334
</table>
330335
<div class="spacer-lg"></div>
@@ -359,6 +364,11 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
359364
<td width="80%"><a th:href="${instance.userObject.uri}"
360365
th:text="${instance.userObject.uri}" href="url.html">URL</a></td>
361366
</tr>
367+
<th:block th:if="${helper.getNodeName(instance.userObject) != null}"><tr>
368+
<td th:text="#{report.alerts.detail.nodename}" width="20%"
369+
class="indent2">Node Name</td>
370+
<td th:text="${helper.getNodeName(instance.userObject)}" width="80%">Node Name</td>
371+
</tr></th:block>
362372
<tr>
363373
<td th:text="#{report.alerts.detail.method}" width="20%"
364374
class="indent2">Method</td>
@@ -388,7 +398,12 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
388398
</th:block>
389399
<tr>
390400
<td th:text="#{report.alerts.detail.instances}" width="20%">Instances</td>
401+
<th:block th:if="${helper.isSystemic(alert)}">
402+
<td th:text="#{report.alerts.list.systemic}" width="80%">Systemic</td>
403+
</th:block>
404+
<th:block th:unless="${helper.isSystemic(alert)}">
391405
<td th:text="${alert.childCount}" width="80%">Instances</td>
406+
</th:block>
392407
</tr>
393408
<tr>
394409
<td th:text="#{report.alerts.detail.solution}" width="20%">Solution</td>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
{
2424
"id": "[(${instance.alertId})]",
2525
"uri": "[(${helper.legacyEscapeText(instance.uri, true)})]",
26+
[#th:block th:if="${helper.getNodeName(instance) != null}"]"nodeName": "[(${helper.legacyEscapeText(helper.getNodeName(instance))})]",[/th:block]
2627
"method": "[(${helper.legacyEscapeText(instance.method, true)})]",
2728
"param": "[(${helper.legacyEscapeTextAlertParam(instance, true)})]",
2829
"attack": "[(${helper.legacyEscapeText(instance.attack, true)})]",
@@ -35,6 +36,7 @@
3536
}[/th:block]
3637
],
3738
"count": "[(${instances.size})]",
39+
"systemic": "[(${helper.isSystemic(alert)})]",
3840
"solution": "[(${helper.legacyEscapeParagraph(alert.solution, true)})]",
3941
"otherinfo": "[(${helper.legacyEscapeParagraph(alert.otherinfo, true)})]",
4042
"reference": "[(${helper.legacyEscapeParagraph(alert.reference, true)})]",

addOns/reports/src/main/zapHomeFiles/reports/traditional-json/report.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
{
2424
"id": "[(${instance.alertId})]",
2525
"uri": "[(${helper.legacyEscapeText(instance.uri, true)})]",
26+
[#th:block th:if="${helper.getNodeName(instance) != null}"]"nodeName": "[(${helper.legacyEscapeText(helper.getNodeName(instance))})]",[/th:block]
2627
"method": "[(${helper.legacyEscapeText(instance.method, true)})]",
2728
"param": "[(${helper.legacyEscapeTextAlertParam(instance, true)})]",
2829
"attack": "[(${helper.legacyEscapeText(instance.attack, true)})]",
@@ -31,6 +32,7 @@
3132
}[/th:block]
3233
],
3334
"count": "[(${instances.size})]",
35+
"systemic": "[(${helper.isSystemic(alert)})]",
3436
"solution": "[(${helper.legacyEscapeParagraph(alert.solution, true)})]",
3537
"otherinfo": "[(${helper.legacyEscapeParagraph(alert.otherinfo, true)})]",
3638
"reference": "[(${helper.legacyEscapeParagraph(alert.reference, true)})]",

addOns/reports/src/main/zapHomeFiles/reports/traditional-md/report.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ZAP by [Checkmarx](https://checkmarx.com/).
1616

1717
| [(#{report.alerts.list.name})] | [(#{report.alerts.list.risklevel})] | [(#{report.alerts.list.numinstances})] |
1818
| --- | --- | --- |
19-
[#th:block th:each="alert: ${alertTree.children}"]| [(${alert.nodeName})] | [(${helper.getRiskString(alert.risk)})] | [(${alert.childCount})] |
19+
[#th:block th:each="alert: ${alertTree.children}"]| [(${alert.nodeName})] | [(${helper.getRiskString(alert.risk)})] | [#th:block th:if="${helper.isSystemic(alert)}"][(#{report.alerts.list.systemic})][/th:block][#th:block th:unless="${helper.isSystemic(alert)}"][(${alert.childCount})][/th:block] |
2020
[/th:block]
2121
[/th:block]
2222

@@ -38,13 +38,15 @@ ZAP by [Checkmarx](https://checkmarx.com/).
3838
[(${alert.userObject.description})]
3939
[#th:block th:each="instance: ${alert.children}"]
4040
* [(#{report.alerts.detail.url})]: [(${#strings.replace(#uris.escapePath(instance.userObject.uri), ')', '&29')})]
41+
[#th:block th:if="${helper.getNodeName(instance.userObject) != null}"] * [(#{report.alerts.detail.nodename})]: `[(${helper.getNodeName(instance.userObject)})]`[/th:block]
4142
* [(#{report.alerts.detail.method})]: `[(${instance.userObject.method})]`
4243
* [(#{report.alerts.detail.param})]: `[(${instance.userObject.param})]`
4344
* [(#{report.alerts.detail.attack})]: `[(${instance.userObject.attack})]`
4445
* [(#{report.alerts.detail.evidence})]: `[(${instance.userObject.evidence})]`
4546
* [(#{report.alerts.detail.otherinfo})]: `[(${instance.userObject.otherinfo})]`
4647
[/th:block]
47-
[(#{report.alerts.detail.instances})]: [(${alert.childCount})]
48+
[#th:block th:if="${helper.isSystemic(alert)}"][(#{report.alerts.detail.instances})]: [(#{report.alerts.list.systemic})][/th:block]
49+
[#th:block th:unless="${helper.isSystemic(alert)}"][(#{report.alerts.detail.instances})]: [(${alert.childCount})][/th:block]
4850

4951
### [(#{report.alerts.detail.solution})]
5052

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,12 @@ <h3 th:text="#{report.alerts.list}">Alerts</h3>
186186
Name</a></td>
187187
<td align="center" th:class="${'risk-' + alert.risk}"
188188
th:text="${helper.getRiskString(alert.risk)}">Risk</td>
189-
<td align="center" th:text="${alert.childCount}">Count</td>
189+
<th:block th:if="${helper.isSystemic(alert)}">
190+
<td align="center" th:text="#{report.alerts.list.systemic}">Systemic</td>
191+
</th:block>
192+
<th:block th:unless="${helper.isSystemic(alert)}">
193+
<td align="center" th:text="${alert.childCount}">Count</td>
194+
</th:block>
190195
</tr>
191196
</table>
192197
<div class="spacer-lg"></div>
@@ -223,6 +228,15 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
223228
th:text="${helper.escapeXml(instance.userObject.uri)}"
224229
href="url.html">URL</a></td>
225230
</tr>
231+
<th:block
232+
th:if="${helper.getNodeName(instance.userObject) != null}">
233+
<tr>
234+
<td th:text="#{report.alerts.detail.nodename}" width="20%"
235+
class="indent2">Node Name</td>
236+
<td th:text="${helper.getNodeName(instance.userObject)}"
237+
width="80%">Node Name</td>
238+
</tr>
239+
</th:block>
226240
<tr>
227241
<td th:text="#{report.alerts.detail.method}" width="20%"
228242
class="indent2">Method</td>
@@ -249,7 +263,12 @@ <h3 th:text="#{report.alerts.detail}">Alert Detail</h3>
249263
</th:block>
250264
<tr>
251265
<td th:text="#{report.alerts.detail.instances}" width="20%">Instances</td>
252-
<td th:text="${alert.childCount}" width="80%">Instances</td>
266+
<th:block th:if="${helper.isSystemic(alert)}">
267+
<td th:text="#{report.alerts.list.systemic}" width="80%">Systemic</td>
268+
</th:block>
269+
<th:block th:unless="${helper.isSystemic(alert)}">
270+
<td th:text="${alert.childCount}" width="80%">Instances</td>
271+
</th:block>
253272
</tr>
254273
<tr>
255274
<td th:text="#{report.alerts.detail.solution}" width="20%">Solution</td>

0 commit comments

Comments
 (0)