Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions addOns/insights/insights.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ zapAddOn {

manifest {
author.set("ZAP Dev Team")
extensions {
register("org.zaproxy.addon.insights.report.ExtensionInsightsReport") {
classnames {
allowed.set(listOf("org.zaproxy.addon.insights.report"))
}
dependencies {
addOns {
register("automation") {
version.set(">=0.45.0")
}
register("reports") {
version.set(">=0.39.0")
}
}
}
}
}
}
}

Expand All @@ -17,5 +34,6 @@ crowdin {
}

dependencies {
zapAddOn("reports")
testImplementation(project(":testutils"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.ExtensionAdaptor;
Expand Down Expand Up @@ -85,6 +86,10 @@ public InsightsParam getParam() {
return param;
}

public StatsMonitor getStatsMonitor() {
return statsMonitor;
}

@Override
public void optionsLoaded() {
pollThread = new PollThread();
Expand All @@ -106,12 +111,18 @@ public void recordInsight(Insight ins) {
}
}

protected List<Insight> getInsights() {
public List<Insight> getInsights() {
return this.insights.getInsightList();
}

protected StatsMonitor getStatsMonitor() {
return statsMonitor;
public Map<String, Map<String, Insight>> getInsightMap() {
return this.insights.getInsightMap();
}

public void processStats() {
if (statsMonitor != null) {
this.statsMonitor.processStats();
}
}

public void clearInsights() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public String toString() {
}

public enum Reason {
NA,
INFO,
WARNING,
EXCEEDED_LOW,
EXCEEDED_HIGH;

Expand All @@ -63,11 +64,17 @@ public String toString() {
private boolean percent;

public Insight(String site, String key, String description, long statistic) {
this(Level.INFO, Reason.NA, site, key, description, statistic, false);
this(Level.INFO, Reason.INFO, site, key, description, statistic, false);
}

public Insight(String site, String key, String description, long statistic, boolean percent) {
this(Level.INFO, Reason.NA, site, key, description, statistic, percent);
this(Level.INFO, Reason.INFO, site, key, description, statistic, percent);
}

public String getStatisticStr() {
return isPercent()
? Constant.messages.getString("insights.insight.statistic.percent", getStatistic())
: Constant.messages.getString("insights.insight.statistic.plain", getStatistic());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public List<Insight> getInsightList() {
return insightList;
}

public Map<String, Map<String, Insight>> getInsightMap() {
return insightMap;
}

public void setModel(InsightsTableModel model) {
this.model = model;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,7 @@ public Object getValueAt(int rowIndex, int columnIndex) {
return switch (columnIndex) {
case 0 -> ins.getLevel();
case 1 -> ins.getReason();
case 2 ->
ins.isPercent()
? Constant.messages.getString(
"insights.table.percent", ins.getStatistic())
: Constant.messages.getString(
"insights.table.plain", ins.getStatistic());
case 2 -> ins.getStatisticStr();
case 3 -> ins.getSite();
case 4 -> ins.getDescription();
default -> null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ private long unbox(Long l) {
return l == null ? 0 : l;
}

private void recordStatsInsight(Insight.Level level, String statsKey, String insightsKey) {
recordInsight(level, Insight.Reason.NA, insightsKey, unbox(stats.getStat(statsKey)), 0);
private void recordWarningStatsInsight(
Insight.Level level, String statsKey, String insightsKey) {
recordInsight(
level, Insight.Reason.WARNING, insightsKey, unbox(stats.getStat(statsKey)), 0);
}

private void recordInsight(
Expand All @@ -130,8 +132,11 @@ private void recordInsight(
}
boolean isPercent = total > 0;
long value = isPercent ? percent(stat, total) : stat;
ext.recordInsight(
new Insight(level, reason, site, key, getStatsDescription(key), value, isPercent));
if (value > 0) {
ext.recordInsight(
new Insight(
level, reason, site, key, getStatsDescription(key), value, isPercent));
}
}

public void processStats() {
Expand All @@ -142,10 +147,11 @@ public void processStats() {
processAuthStats();

// ZAP errors and warnings
recordStatsInsight(Insight.Level.LOW, STATS_ERROR, "insight.log.error");
recordStatsInsight(Insight.Level.LOW, STATS_WARN, "insight.log.warn");
recordStatsInsight(Insight.Level.HIGH, STATS_DATABASE_FULL, "insight.database.full");
recordStatsInsight(Insight.Level.HIGH, STATS_DISKSPACE_FULL, "insight.diskspace.full");
recordWarningStatsInsight(Insight.Level.LOW, STATS_ERROR, "insight.log.error");
recordWarningStatsInsight(Insight.Level.LOW, STATS_WARN, "insight.log.warn");
recordWarningStatsInsight(Insight.Level.HIGH, STATS_DATABASE_FULL, "insight.database.full");
recordWarningStatsInsight(
Insight.Level.HIGH, STATS_DISKSPACE_FULL, "insight.diskspace.full");

checkMemoryUsage();
}
Expand Down Expand Up @@ -175,7 +181,7 @@ private void processStatusCodeStats() {
for (Entry<String, Long> entry : codeCounts.entrySet()) {
recordInsight(
Insight.Level.INFO,
Insight.Reason.NA,
Insight.Reason.INFO,
site,
INSIGHT_CODE_PREFIX + entry.getKey() + "xx",
entry.getValue(),
Expand Down Expand Up @@ -212,7 +218,7 @@ private void processEndpointStats() {
if (total != null) {
recordInsight(
Insight.Level.INFO,
Insight.Reason.NA,
Insight.Reason.INFO,
site,
INSIGHTS_ENDPOINTS_TOTAL,
total);
Expand All @@ -221,7 +227,7 @@ private void processEndpointStats() {
if (!INSIGHTS_ENDPOINTS_TOTAL.equals(k2stat.getKey())) {
recordInsight(
Insight.Level.INFO,
Insight.Reason.NA,
Insight.Reason.INFO,
site,
k2stat.getKey(),
k2stat.getValue(),
Expand All @@ -241,7 +247,7 @@ private void recordMessageInsightWithLimits(
long total,
long bad) {
if (bad > 0) {
recordInsight(Insight.Level.INFO, Insight.Reason.NA, site, key, bad, total);
recordInsight(Insight.Level.INFO, Insight.Reason.INFO, site, key, bad, total);
}

if (total < min) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2025 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.addon.insights.report;

import java.util.List;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.extension.Extension;
import org.parosproxy.paros.extension.ExtensionAdaptor;
import org.zaproxy.addon.insights.ExtensionInsights;
import org.zaproxy.addon.reports.ExtensionReports;
import org.zaproxy.addon.reports.ReportData;

public class ExtensionInsightsReport extends ExtensionAdaptor {

public static final String NAME = "ExtensionInsightsReport";

public static final String INSIGHTS_LIST = "insightsList";

private static final List<Class<? extends Extension>> DEPENDENCIES =
List.of(ExtensionReports.class);

private InsightsReportDataHandler insightsReportDataHandler;

public ExtensionInsightsReport() {
super(NAME);
}

@Override
public void init() {}

@Override
public boolean supportsDb(String type) {
return true;
}

@Override
public List<Class<? extends Extension>> getDependencies() {
return DEPENDENCIES;
}

@Override
public void optionsLoaded() {
insightsReportDataHandler = new InsightsReportDataHandler();
getExt(ExtensionReports.class).addReportDataHandler(insightsReportDataHandler);
}

@Override
public void unload() {
if (insightsReportDataHandler != null) {
getExt(ExtensionReports.class).removeReportDataHandler(insightsReportDataHandler);
}
}

private static <T extends Extension> T getExt(Class<T> clazz) {
return Control.getSingleton().getExtensionLoader().getExtension(clazz);
}

@Override
public boolean canUnload() {
return true;
}

@Override
public String getDescription() {
return Constant.messages.getString("insights.report.desc");
}

@Override
public String getUIName() {
return Constant.messages.getString("insights.report.name");
}

protected static class InsightsReportDataHandler implements ExtensionReports.ReportDataHandler {

@Override
public void handle(ReportData reportData) {
ExtensionInsights ext = getExt(ExtensionInsights.class);
// Force the processing so we get the very latest stats
ext.processStats();
reportData.addReportObjects(INSIGHTS_LIST, ext.getInsights());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@ insights.insight.log.warn = ZAP warnings logged - see the zap.log file for detai
insights.insight.memory.usage = Percentage of memory used
insights.insight.network.failure = Percentage of network failures
insights.insight.response.slow = Percentage of slow responses

insights.insight.statistic.percent = {0} %
insights.insight.statistic.plain = {0}
insights.options.memHighThreshold = Memory High Threshold %
insights.options.memLowThreshold = Memory Low Threshold %
insights.options.msgHighThreshold = Message High Threshold %

insights.options.msgLowThreshold = Message Low Threshold %
insights.options.slowResponse = Slow Response in Milliseconds

insights.optionspanel.name = Insights

insights.panel.title = Insights
insights.report.desc = Adds Insights to the report data

insights.report.name = Insights Reports Extension

insights.table.header.desc = Description
insights.table.header.level = Level
insights.table.header.reason = Reason
Expand All @@ -31,8 +36,7 @@ insights.table.level.high = High
insights.table.level.info = Info
insights.table.level.low = Low
insights.table.level.medium = Medium
insights.table.percent = {0} %
insights.table.plain = {0}
insights.table.reason.exceeded_high = Exceeded High
insights.table.reason.exceeded_low = Exceeded Low
insights.table.reason.na =
insights.table.reason.info = Informational
insights.table.reason.warning = Warning
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,15 @@ void shouldRecordZapErrorsAndWarnings() {
assertInsight(
0,
Insight.Level.LOW,
Insight.Reason.WARNING,
"",
"insight.log.error",
"ZAP errors logged - see the zap.log file for details",
1L);
assertInsight(
1,
Insight.Level.LOW,
Insight.Reason.WARNING,
"",
"insight.log.warn",
"ZAP warnings logged - see the zap.log file for details",
Expand All @@ -115,8 +117,22 @@ void shouldRecordSpaceProblems() {

// Then
assertThat(ext.getInsights().size(), is(equalTo(2)));
assertInsight(0, Insight.Level.HIGH, "", "insight.database.full", "Database full", 1L);
assertInsight(1, Insight.Level.HIGH, "", "insight.diskspace.full", "Diskspace full", 1L);
assertInsight(
0,
Insight.Level.HIGH,
Insight.Reason.WARNING,
"",
"insight.database.full",
"Database full",
1L);
assertInsight(
1,
Insight.Level.HIGH,
Insight.Reason.WARNING,
"",
"insight.diskspace.full",
"Diskspace full",
1L);
}

@Test
Expand Down Expand Up @@ -525,12 +541,12 @@ void shouldRecordContentTypesAdded() throws Exception {
}

private void assertInsight(int index, String site, String key, String desc, long stat) {
this.assertInsight(index, Insight.Level.INFO, Insight.Reason.NA, site, key, desc, stat);
this.assertInsight(index, Insight.Level.INFO, Insight.Reason.INFO, site, key, desc, stat);
}

private void assertInsight(
int index, Insight.Level level, String site, String key, String desc, long stat) {
this.assertInsight(index, level, Insight.Reason.NA, site, key, desc, stat);
this.assertInsight(index, level, Insight.Reason.INFO, site, key, desc, stat);
}

private void assertInsight(
Expand Down
Loading