From 4ff4b4c2aa8f0e31e3256e0ad79dbd73b5cdefe7 Mon Sep 17 00:00:00 2001 From: kingthorin Date: Wed, 13 Nov 2024 15:16:55 -0500 Subject: [PATCH] reports: HTML add sequence support Signed-off-by: kingthorin --- addOns/reports/CHANGELOG.md | 2 +- .../reports/resources/Messages.properties | 12 ++ .../traditional-html-plus/Messages.properties | 1 + .../reports/traditional-html-plus/report.html | 110 ++++++++++++ .../resources/common.css | 55 ++++++ .../traditional-html-plus/template.yaml | 1 + .../traditional-html/Messages.properties | 1 + .../reports/traditional-html/report.html | 165 ++++++++++++++++++ .../reports/traditional-html/template.yaml | 3 +- .../reports/traditional-json-plus/report.json | 2 +- .../reports/traditional-json/report.json | 2 +- .../reports/ExtensionReportsUnitTest.java | 25 ++- .../sequence/StdActiveScanRunner.java | 44 ++++- 13 files changed, 411 insertions(+), 12 deletions(-) diff --git a/addOns/reports/CHANGELOG.md b/addOns/reports/CHANGELOG.md index 4f82eca1398..58e293c5713 100644 --- a/addOns/reports/CHANGELOG.md +++ b/addOns/reports/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased ### Added - Stats counter to the main toolbar button (Issue 8375). -- Sequence data to JSON reports. +- Sequence data to JSON & HTML reports. ### Changed - Update automation job help. diff --git a/addOns/reports/src/main/resources/org/zaproxy/addon/reports/resources/Messages.properties b/addOns/reports/src/main/resources/org/zaproxy/addon/reports/resources/Messages.properties index fd9755376d5..bc8c0707e86 100644 --- a/addOns/reports/src/main/resources/org/zaproxy/addon/reports/resources/Messages.properties +++ b/addOns/reports/src/main/resources/org/zaproxy/addon/reports/resources/Messages.properties @@ -132,6 +132,18 @@ reports.report.risk.1 = Low reports.report.risk.2 = Medium reports.report.risk.3 = High +reports.report.sequences.details.name = Sequence Details +reports.report.sequences.list.name = Name +reports.report.sequences.step.alerts = Alerts: +reports.report.sequences.step.original = Original +reports.report.sequences.step.replay = Replay +reports.report.sequences.step.req.body = Request Body +reports.report.sequences.step.req.header = Request Header +reports.report.sequences.step.resp.body = Response Body +reports.report.sequences.step.resp.header = Response Header +reports.report.sequences.step.result = Result: +reports.report.sequences.summary.name = Summary of Sequences + reports.report.site = Site: {0} reports.report.sites = Sites: {0} reports.report.sites.title = Sites diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/Messages.properties b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/Messages.properties index 91bcbfda738..43221b7dd77 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/Messages.properties +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/Messages.properties @@ -17,6 +17,7 @@ report.template.section.chart = Chart report.template.section.instancecount = Instance Count report.template.section.params = Parameters report.template.section.passingrules = Passing Rules +report.template.section.sequencedetails = Sequence Details report.template.section.statistics = Statistics report.template.stats.auth = Authentication Statistics report.template.stats.auth.none = No Authentication Statistics Found diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/report.html b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/report.html index 41a149a4be0..2c5f01b3da3 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/report.html +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/report.html @@ -518,6 +518,116 @@

Alert Detail

+ + + +

Summary of + Sequences

+ For each step: result (Pass/Fail) - risk (of highest + alert(s) for the step, if any). + + + + + + + + + +
sequenceNameisPass flag risk +
+
+ +
+ + + +

Sequences + Details

+ With the associated active scan results. + + +

sequenceName

+
+ + +

stepDesc

+ +
+ + Result result
Alerts +
+ +   + Alert + Name + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OriginalReplay
reqHdrorigReqHdrSizereqHdrreplayReqHdrSize
reqBodyorigReqBodySizereqBodyreplayReqBodySize
respHdrorigRespHdrSizerespHdrreplayRespHdrSize
respBodyorigRespBodySizerespBodyreplayRespBodySize
+
+
+
+
+
+ +
+ +
+
+
+
diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/resources/common.css b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/resources/common.css index dc988bef446..2b375202f28 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/resources/common.css +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/resources/common.css @@ -87,3 +87,58 @@ th { .left-header { display: inline-block; } + +.pass { + background: green; + color: white +} + +.fail { + background: red; + color: white +} + +.alert-3b::before { + content: '\2691'; + color: red +} + +.alert-2b::before { + content: '\2691 '; + color: orange +} + +.alert-1b::before { + content: '\2691'; + color: yellow +} + +.alert-0b::before { + content: '\2691'; + color: blue +} + + +.alert-3a::after { + content: '\2691'; + color: red +} + +.alert-2a::after { + content: '\2691 '; + color: orange +} + +.alert-1a::after { + content: '\2691'; + color: yellow +} + +.alert-0a::after { + content: '\2691'; + color: blue +} + +.lm2 { + margin-left: 2em; +} diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/template.yaml b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/template.yaml index 8bda2984f3c..bc0e4935ec6 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/template.yaml +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html-plus/template.yaml @@ -10,6 +10,7 @@ sections: - alertdetails - statistics - params + - sequencedetails themes: - light - dark \ No newline at end of file diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/Messages.properties b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/Messages.properties index fe7e56ed5b3..76fb0780459 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/Messages.properties +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/Messages.properties @@ -2,3 +2,4 @@ report.template.section.alertcount = Alert Count report.template.section.alertdetails = Alert Details report.template.section.instancecount = Instance Chart +report.template.section.sequencedetails = Sequence Details diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/report.html b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/report.html index 0234b29b7c4..55d4115bad4 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/report.html +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/report.html @@ -107,6 +107,60 @@ .left-header { display: inline-block; } + +.pass { + background: green; + color: white +} + +.fail { + background: red; + color: white +} + +.alert-3b::before { + content: '\2691'; + color: red +} + +.alert-2b::before { + content: '\2691 '; + color: orange +} + +.alert-1b::before { + content: '\2691'; + color: yellow +} + +.alert-0b::before { + content: '\2691'; + color: blue +} + +.alert-3a::after { + content: '\2691'; + color: red +} + +.alert-2a::after { + content: '\2691 '; + color: orange +} + +.alert-1a::after { + content: '\2691'; + color: yellow +} + +.alert-0a::after { + content: '\2691'; + color: blue +} + +.lm2 { + margin-left: 2em; +} @@ -292,6 +346,117 @@

Alert Detail

+ + + +

Summary of + Sequences

+ For each step: result (Pass/Fail) - risk (of highest + alert(s) for the step, if any). + + + + + + + + + +
sequenceNameisPass flag risk +
+
+ +
+ + + +

Sequences + Details

+ With the associated active scan results. + + +

sequenceName

+
+ + +

stepDesc

+ +
+ + Result result
Alerts +
+ +   + Alert + Name + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OriginalReplay
reqHdrorigReqHdrSizereqHdrreplayReqHdrSize
reqBodyorigReqBodySizereqBodyreplayReqBodySize
respHdrorigRespHdrSizerespHdrreplayRespHdrSize
respBodyorigRespBodySizerespBodyreplayRespBodySize
+
+
+
+
+
+ +
+ +
+
+
+
+ diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/template.yaml b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/template.yaml index 830e8352ca4..0b8dd236e1e 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/template.yaml +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-html/template.yaml @@ -5,4 +5,5 @@ extension: html sections: - alertcount - instancecount - - alertdetails \ No newline at end of file + - alertdetails + - sequencedetails \ No newline at end of file diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-json-plus/report.json b/addOns/reports/src/main/zapHomeFiles/reports/traditional-json-plus/report.json index 7a9f093bccf..aa7c47bc8b1 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-json-plus/report.json +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-json-plus/report.json @@ -58,7 +58,7 @@ "step": "[(${step.step})]", "pass": "[(${step.pass})]", "resultDetails": "[(${step.result})]", - "alertIds": [[(${step.alertIds})]], + "alertIds": [[(${step.getAlertIds()})]], "original": { "uri": "[(${helper.legacyEscapeText(step.originalMsg.requestHeader.uri, true)})]", diff --git a/addOns/reports/src/main/zapHomeFiles/reports/traditional-json/report.json b/addOns/reports/src/main/zapHomeFiles/reports/traditional-json/report.json index 3bd847ac447..4911cdf46ee 100644 --- a/addOns/reports/src/main/zapHomeFiles/reports/traditional-json/report.json +++ b/addOns/reports/src/main/zapHomeFiles/reports/traditional-json/report.json @@ -48,7 +48,7 @@ "step": "[(${step.step})]", "pass": "[(${step.pass})]", "resultDetails": "[(${step.result})]", - "alertIds": [[(${step.alertIds})]], + "alertIds": [[(${step.getAlertIds()})]], "original": { "uri": "[(${helper.legacyEscapeText(step.originalMsg.requestHeader.uri, true)})]", diff --git a/addOns/reports/src/test/java/org/zaproxy/addon/reports/ExtensionReportsUnitTest.java b/addOns/reports/src/test/java/org/zaproxy/addon/reports/ExtensionReportsUnitTest.java index c07c9853a1d..7c8e25e20e4 100644 --- a/addOns/reports/src/test/java/org/zaproxy/addon/reports/ExtensionReportsUnitTest.java +++ b/addOns/reports/src/test/java/org/zaproxy/addon/reports/ExtensionReportsUnitTest.java @@ -920,8 +920,7 @@ void shouldGenerateValidJsonReport() throws Exception { checkAlert(site.getJSONObject(0)); } - private static File generateReportWithSequence(Template template, File f) - throws IOException, DocumentException { + private static File generateReportWithSequence(Template template, File f) throws IOException { ExtensionReports extRep = new ExtensionReports(); ReportData reportData = new ReportData(); reportData.setTitle("Test Title"); @@ -940,15 +939,33 @@ private static File generateReportWithSequence(Template template, File f) 1, true, "Pass", - new ArrayList(), + new ArrayList(), + -1, newMsg("https://www.example.com/step1"), newMsg("https://www.example.com/step1"))); + Alert alert1 = + Alert.builder() + .setName("Alert1") + .setUri("https://www.example.com/step2") + .setPluginId(111) + .setRisk(Alert.RISK_HIGH) + .setAlertId(2) + .build(); + Alert alert2 = + Alert.builder() + .setName("Alert2") + .setUri("https://www.example.com/step2") + .setPluginId(111) + .setRisk(Alert.RISK_INFO) + .setAlertId(4) + .build(); steps.add( new SequenceStepData( 2, false, "Fail", - Arrays.asList(2, 4), + Arrays.asList(alert1, alert2), + Alert.RISK_HIGH, newMsg("https://www.example.com/step2"), newMsg("https://www.example.com/step2"))); seqData.addSequenceData("Seq name", steps); diff --git a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/StdActiveScanRunner.java b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/StdActiveScanRunner.java index 1e86993a05c..524978257b9 100644 --- a/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/StdActiveScanRunner.java +++ b/addOns/sequence/src/main/java/org/zaproxy/zap/extension/sequence/StdActiveScanRunner.java @@ -23,16 +23,19 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.Getter; 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.core.scanner.Alert; import org.parosproxy.paros.extension.history.ExtensionHistory; import org.parosproxy.paros.model.HistoryReference; import org.parosproxy.paros.model.SiteNode; import org.parosproxy.paros.network.HttpMessage; import org.zaproxy.addon.network.ExtensionNetwork; +import org.zaproxy.zap.extension.alert.ExtensionAlert; import org.zaproxy.zap.extension.ascan.ActiveScan; import org.zaproxy.zap.extension.zest.ExtensionZest; import org.zaproxy.zap.extension.zest.ZestScriptWrapper; @@ -153,12 +156,22 @@ public ZestResponse runStatement( } } + ExtensionAlert extAlert = + Control.getSingleton() + .getExtensionLoader() + .getExtension(ExtensionAlert.class); + List alerts = + extAlert.getAllAlerts().stream() + .filter(alert -> ascan.getAlertsIds().contains(alert.getAlertId())) + .collect(Collectors.toList()); + steps.add( new SequenceStepData( step, passed, result, - ascan.getAlertsIds(), + alerts, + StdActiveScanRunner.highestAlert(alerts), ZestZapUtils.toHttpMessage(req, req.getResponse()), msg)); } @@ -167,6 +180,19 @@ public ZestResponse runStatement( return resp; } + private static int highestAlert(List alerts) { + if (alerts.isEmpty()) { + return -1; + } + int[] max = {0}; + alerts.stream() + .forEach( + alert -> { + max[0] = Math.max(alert.getRisk(), max[0]); + }); + return max[0]; + } + private SiteNode messageToSiteNode(HttpMessage msg, int step) { SiteNode temp = null; try { @@ -202,7 +228,8 @@ public static class SequenceStepData { private int step; private boolean pass; private String result; - private List alertIds; + private List alerts; + private int highestAlert; private HttpMessage originalMsg; private HttpMessage replayMsg; @@ -210,15 +237,24 @@ public SequenceStepData( int step, boolean pass, String result, - List alertIds, + List alerts, + int highestAlert, HttpMessage originalMsg, HttpMessage replayMsg) { this.step = step; this.pass = pass; this.result = result; - this.alertIds = alertIds; + this.alerts = alerts; + this.highestAlert = highestAlert; this.originalMsg = originalMsg; this.replayMsg = replayMsg; } + + public List getAlertIds() { + return getAlerts().stream() + .mapToInt(Alert::getAlertId) + .boxed() + .collect(Collectors.toList()); + } } }