Skip to content

Commit 7b93b7b

Browse files
authored
Merge pull request #7310 from kingthorin/ex-alerts
ascanrulesBeta: Add Example Alerts for Proxy Disclosure
2 parents 14b5743 + eff36b5 commit 7b93b7b

3 files changed

Lines changed: 100 additions & 99 deletions

File tree

addOns/ascanrulesBeta/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88
- The following scan rules now include example alert functionality for documentation generation purposes (Issue 6119) and alert references (Issue 7100):
99
- Insecure HTTP Method
1010
- Session Fixation
11+
- Proxy Disclosure
1112
- Dependency update.
1213

1314
## [65] - 2026-04-14

addOns/ascanrulesBeta/src/main/java/org/zaproxy/zap/extension/ascanrulesBeta/ProxyDisclosureScanRule.java

Lines changed: 82 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.apache.logging.log4j.Logger;
4040
import org.parosproxy.paros.Constant;
4141
import org.parosproxy.paros.core.scanner.AbstractAppPlugin;
42+
import org.parosproxy.paros.core.scanner.AbstractPlugin.AlertBuilder;
4243
import org.parosproxy.paros.core.scanner.Alert;
4344
import org.parosproxy.paros.core.scanner.Category;
4445
import org.parosproxy.paros.network.HtmlParameter;
@@ -668,105 +669,9 @@ public void scan() {
668669
// nodes.
669670
if (step2numberOfNodes > 1 || silentProxySet.size() > 0) {
670671
// bingo with the list of nodes (proxies+origin web server) that we detected.
671-
String unknown = Constant.messages.getString(MESSAGE_PREFIX + "extrainfo.unknown");
672-
String proxyServerHeader =
673-
Constant.messages.getString(
674-
MESSAGE_PREFIX + "extrainfo.proxyserver.header");
675-
String webServerHeader =
676-
Constant.messages.getString(MESSAGE_PREFIX + "extrainfo.webserver.header");
677-
String silentProxyServerHeader =
678-
Constant.messages.getString(
679-
MESSAGE_PREFIX + "extrainfo.silentproxyserver.header");
680-
681-
// get the proxy server information (ie, all but the last node)
682-
String proxyServerInfo = "";
683-
if (step2numberOfNodes > 0) {
684-
StringBuilder sb = new StringBuilder();
685-
sb.append(proxyServerHeader);
686-
sb.append("\n");
687-
for (int nodei = 0; nodei < step2numberOfNodes - 1; nodei++) {
688-
String proxyServerNode =
689-
Constant.messages.getString(
690-
MESSAGE_PREFIX + "extrainfo.proxyserver",
691-
(!nodeServers[nodei].equals("")
692-
? nodeServers[nodei]
693-
: unknown));
694-
sb.append(proxyServerNode);
695-
sb.append("\n");
696-
}
697-
proxyServerInfo = sb.toString();
698-
}
699-
// get the origin web server information (ie, the last node)
700-
String webServerInfo = "";
701-
if (step2numberOfNodes > 0) {
702-
StringBuilder sb = new StringBuilder();
703-
sb.append(webServerHeader);
704-
sb.append("\n");
705-
String webServerNode =
706-
Constant.messages.getString(
707-
MESSAGE_PREFIX + "extrainfo.webserver",
708-
(!nodeServers[step2numberOfNodes - 1].equals("")
709-
? nodeServers[step2numberOfNodes - 1]
710-
: unknown));
711-
sb.append(webServerNode);
712-
sb.append("\n");
713-
webServerInfo = sb.toString();
714-
}
715-
// get the silent proxy information
716-
String silentProxyServerInfo = "";
717-
if (silentProxySet.size() > 0) {
718-
StringBuilder sb = new StringBuilder();
719-
sb.append(silentProxyServerHeader);
720-
sb.append("\n");
721-
for (String silentServer : silentProxySet) {
722-
// LOGGER.debug("Silent Proxy: {}",
723-
// (!silentServer.equals("")?silentServer:"Unknown"));
724-
String silentProxyServerNode =
725-
Constant.messages.getString(
726-
MESSAGE_PREFIX + "extrainfo.silentproxyserver",
727-
(!silentServer.equals("") ? silentServer : unknown));
728-
sb.append(silentProxyServerNode);
729-
sb.append("\n");
730-
}
731-
silentProxyServerInfo = sb.toString();
732-
}
733-
String traceInfo = "";
734-
if (endToEndTraceEnabled || proxyTraceEnabled) {
735-
traceInfo =
736-
Constant.messages.getString(MESSAGE_PREFIX + "extrainfo.traceenabled");
737-
}
738-
739-
// all the info is collated nicely. raise the alert.
740-
String extraInfo = "";
741-
if (!proxyServerInfo.equals("")) {
742-
extraInfo += proxyServerInfo;
743-
}
744-
if (!webServerInfo.equals("")) {
745-
extraInfo += webServerInfo;
746-
}
747-
if (!silentProxyServerInfo.equals("")) {
748-
extraInfo += silentProxyServerInfo;
749-
}
750-
if (!traceInfo.equals("")) {
751-
extraInfo += traceInfo;
752-
}
753-
754-
// raise the alert on the original message
755-
// there are multiple messages on which the issue could have been raised, but each
756-
// individual attack message
757-
// tells only a small part of the story. Explain it in the "extra info" instead.
758-
newAlert()
759-
.setRisk(
760-
endToEndTraceEnabled || proxyTraceEnabled
761-
? Alert.RISK_HIGH
762-
: getRisk())
763-
.setConfidence(Alert.CONFIDENCE_MEDIUM)
764-
.setDescription(
765-
Constant.messages.getString(
766-
MESSAGE_PREFIX + "desc",
767-
step2numberOfNodes - 1 + silentProxySet.size()))
768-
.setAttack(getAttack())
769-
.setOtherInfo(extraInfo)
672+
boolean traceEnabled = endToEndTraceEnabled || proxyTraceEnabled;
673+
buildProxyDisclosureAlert(
674+
step2numberOfNodes, nodeServers, silentProxySet, traceEnabled)
770675
.setMessage(getBaseMsg())
771676
.raise();
772677
}
@@ -778,6 +683,84 @@ public void scan() {
778683
}
779684
}
780685

686+
private AlertBuilder buildProxyDisclosureAlert(
687+
int step2numberOfNodes,
688+
String[] nodeServers,
689+
Set<String> silentProxySet,
690+
boolean traceEnabled) {
691+
int proxyCountForDescription = step2numberOfNodes - 1 + silentProxySet.size();
692+
String unknown = Constant.messages.getString(MESSAGE_PREFIX + "extrainfo.unknown");
693+
StringBuilder otherInfo = new StringBuilder();
694+
if (step2numberOfNodes > 0) {
695+
otherInfo
696+
.append(
697+
Constant.messages.getString(
698+
MESSAGE_PREFIX + "extrainfo.proxyserver.header"))
699+
.append('\n');
700+
for (int nodei = 0; nodei < step2numberOfNodes - 1; nodei++) {
701+
otherInfo
702+
.append(
703+
Constant.messages.getString(
704+
MESSAGE_PREFIX + "extrainfo.proxyserver",
705+
nodeServers[nodei].equals("")
706+
? unknown
707+
: nodeServers[nodei]))
708+
.append('\n');
709+
}
710+
otherInfo
711+
.append(
712+
Constant.messages.getString(
713+
MESSAGE_PREFIX + "extrainfo.webserver.header"))
714+
.append('\n')
715+
.append(
716+
Constant.messages.getString(
717+
MESSAGE_PREFIX + "extrainfo.webserver",
718+
nodeServers[step2numberOfNodes - 1].equals("")
719+
? unknown
720+
: nodeServers[step2numberOfNodes - 1]))
721+
.append('\n');
722+
}
723+
if (silentProxySet.size() > 0) {
724+
otherInfo
725+
.append(
726+
Constant.messages.getString(
727+
MESSAGE_PREFIX + "extrainfo.silentproxyserver.header"))
728+
.append('\n');
729+
for (String silentServer : silentProxySet) {
730+
otherInfo
731+
.append(
732+
Constant.messages.getString(
733+
MESSAGE_PREFIX + "extrainfo.silentproxyserver",
734+
silentServer.equals("") ? unknown : silentServer))
735+
.append('\n');
736+
}
737+
}
738+
if (traceEnabled) {
739+
otherInfo.append(
740+
Constant.messages.getString(MESSAGE_PREFIX + "extrainfo.traceenabled"));
741+
}
742+
return newAlert()
743+
.setAlertRef(getId() + (traceEnabled ? "-1" : "-2"))
744+
.setRisk(traceEnabled ? Alert.RISK_HIGH : getRisk())
745+
.setConfidence(Alert.CONFIDENCE_MEDIUM)
746+
.setDescription(
747+
Constant.messages.getString(
748+
MESSAGE_PREFIX + "desc", proxyCountForDescription))
749+
.setAttack(getAttack())
750+
.setOtherInfo(otherInfo.toString());
751+
}
752+
753+
@Override
754+
public List<Alert> getExampleAlerts() {
755+
String[] exampleNodeServers = new String[] {"nginx/1.22", "Apache/2.4.58"};
756+
int exampleStep2Nodes = exampleNodeServers.length;
757+
return List.of(
758+
buildProxyDisclosureAlert(exampleStep2Nodes, exampleNodeServers, Set.of(), true)
759+
.build(),
760+
buildProxyDisclosureAlert(exampleStep2Nodes, exampleNodeServers, Set.of(), false)
761+
.build());
762+
}
763+
781764
private static String randomAlphanumeric(int count) {
782765
return RandomStringUtils.secure().nextAlphanumeric(count);
783766
}

addOns/ascanrulesBeta/src/test/java/org/zaproxy/zap/extension/ascanrulesBeta/ProxyDisclosureScanRuleUnitTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121

2222
import static org.hamcrest.MatcherAssert.assertThat;
2323
import static org.hamcrest.Matchers.equalTo;
24+
import static org.hamcrest.Matchers.hasSize;
2425
import static org.hamcrest.Matchers.is;
2526

27+
import java.util.List;
2628
import java.util.Map;
2729
import org.junit.jupiter.api.Test;
30+
import org.parosproxy.paros.core.scanner.Alert;
2831
import org.zaproxy.addon.commonlib.CommonAlertTag;
2932
import org.zaproxy.addon.commonlib.PolicyTag;
3033

@@ -69,4 +72,18 @@ void shouldReturnExpectedMappings() {
6972
tags.get(CommonAlertTag.SYSTEMIC.getTag()),
7073
is(equalTo(CommonAlertTag.SYSTEMIC.getValue())));
7174
}
75+
76+
@Test
77+
void shouldHaveExpectedExampleAlerts() {
78+
// Given / When
79+
List<Alert> alerts = rule.getExampleAlerts();
80+
// Then
81+
assertThat(alerts, hasSize(2));
82+
Alert highRiskAlert = alerts.get(0);
83+
assertThat(highRiskAlert.getRisk(), is(equalTo(Alert.RISK_HIGH)));
84+
assertThat(highRiskAlert.getConfidence(), is(equalTo(Alert.CONFIDENCE_MEDIUM)));
85+
Alert mediumRiskAlert = alerts.get(1);
86+
assertThat(mediumRiskAlert.getRisk(), is(equalTo(Alert.RISK_MEDIUM)));
87+
assertThat(mediumRiskAlert.getConfidence(), is(equalTo(Alert.CONFIDENCE_MEDIUM)));
88+
}
7289
}

0 commit comments

Comments
 (0)