Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docker/policies/API-Minimal.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>API Minimal</policy>
<statsId>dock-api-min<statsId>
<statsId>dock-api-min</statsId>
<readonly>true</readonly>
<scanner>
<level>OFF</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/Default Policy.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>Default Policy</policy>
<statsId>dock-default<statsId>
<statsId>dock-default</statsId>
<readonly>true</readonly>
<scanner>
<level>MEDIUM</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-High-Th-High.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-High-Th-High</policy>
<statsId>dock-high-high<statsId>
<statsId>dock-high-high</statsId>
<readonly>true</readonly>
<scanner>
<level>HIGH</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-High-Th-Low.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-High-Th-Low</policy>
<statsId>dock-high-low<statsId>
<statsId>dock-high-low</statsId>
<readonly>true</readonly>
<scanner>
<level>LOW</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-High-Th-Med.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-High-Th-Med</policy>
<statsId>dock-high-med<statsId>
<statsId>dock-high-med</statsId>
<readonly>true</readonly>
<scanner>
<level>MEDIUM</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Ins-Th-High.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Ins-Th-High</policy>
<statsId>dock-ins-high<statsId>
<statsId>dock-ins-high</statsId>
<readonly>true</readonly>
<scanner>
<level>HIGH</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Ins-Th-Low.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Ins-Th-Low</policy>
<statsId>dock-ins-low<statsId>
<statsId>dock-ins-low</statsId>
<readonly>true</readonly>
<scanner>
<level>LOW</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Ins-Th-Med.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Ins-Th-Med</policy>
<statsId>dock-ins-med<statsId>
<statsId>dock-ins-med</statsId>
<readonly>true</readonly>
<scanner>
<level>MEDIUM</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Low-Th-High.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Low-Th-High</policy>
<statsId>dock-low-high<statsId>
<statsId>dock-low-high</statsId>
<readonly>true</readonly>
<scanner>
<level>HIGH</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Low-Th-Low.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Low-Th-Low</policy>
<statsId>dock-low-low<statsId>
<statsId>dock-low-low</statsId>
<readonly>true</readonly>
<scanner>
<level>LOW</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Low-Th-Med.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Low-Th-Med</policy>
<statsId>dock-low-med<statsId>
<statsId>dock-low-med</statsId>
<readonly>true</readonly>
<scanner>
<level>MEDIUM</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Med-Th-High.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Med-Th-High</policy>
<statsId>dock-med-high<statsId>
<statsId>dock-med-high</statsId>
<readonly>true</readonly>
<scanner>
<level>HIGH</level>
Expand Down
2 changes: 1 addition & 1 deletion docker/policies/St-Med-Th-Low.policy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<policy>St-Med-Th-Low</policy>
<statsId>dock-med-low<statsId>
<statsId>dock-med-low</statsId>
<readonly>true</readonly>
<scanner>
<level>LOW</level>
Expand Down
24 changes: 21 additions & 3 deletions zap/src/main/java/org/zaproxy/zap/extension/alert/AlertAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public class AlertAPI extends ApiImplementor {
private static final String PARAM_RECURSE = "recurse";
private static final String PARAM_RISK = "riskId";
private static final String PARAM_START = "start";
private static final String PARAM_FALSE_POSITIVE = "falsePositive";

private static final String PARAM_MESSAGE_ID = "messageId";
private static final String PARAM_ALERT_ID = "id";
Expand Down Expand Up @@ -127,7 +128,12 @@ public AlertAPI(ExtensionAlert ext) {
VIEW_ALERTS,
null,
new String[] {
PARAM_BASE_URL, PARAM_START, PARAM_COUNT, PARAM_RISK, PARAM_CONTEXT_NAME
PARAM_BASE_URL,
PARAM_START,
PARAM_COUNT,
PARAM_RISK,
PARAM_CONTEXT_NAME,
PARAM_FALSE_POSITIVE
}));
this.addApiView(new ApiView(VIEW_ALERTS_SUMMARY, null, new String[] {PARAM_BASE_URL}));
this.addApiView(
Expand Down Expand Up @@ -236,7 +242,8 @@ public ApiResponse handleApiView(String name, JSONObject params) throws ApiExcep
if (context == null || context.isInContext(alert.getUri())) {
resultList.addItem(alertToSet(alert));
}
});
},
this.getParam(params, PARAM_FALSE_POSITIVE, false));
result = resultList;
} else if (VIEW_NUMBER_OF_ALERTS.equals(name)) {
CounterProcessor<Alert> counter = new CounterProcessor<>();
Expand Down Expand Up @@ -471,6 +478,17 @@ private static ApiResponseList filterAlertInstances(
private static void processAlerts(
String baseUrl, int start, int count, int riskId, Processor<Alert> processor)
throws ApiException {
processAlerts(baseUrl, start, count, riskId, processor, false);
}

private static void processAlerts(
String baseUrl,
int start,
int count,
int riskId,
Processor<Alert> processor,
boolean falsePositive)
throws ApiException {
List<Alert> alerts = new ArrayList<>();
try {
TableAlert tableAlert = Model.getSingleton().getDb().getTableAlert();
Expand All @@ -485,7 +503,7 @@ private static void processAlerts(
RecordAlert recAlert = tableAlert.read(alertId);
Alert alert = new Alert(recAlert);

if (alert.getConfidence() != Alert.CONFIDENCE_FALSE_POSITIVE
if ((falsePositive || alert.getConfidence() != Alert.CONFIDENCE_FALSE_POSITIVE)
&& !alerts.contains(alert)) {
if (baseUrl != null && !alert.getUri().startsWith(baseUrl)) {
// Not subordinate to the specified URL
Expand Down
2 changes: 1 addition & 1 deletion zap/src/main/java/org/zaproxy/zap/utils/ApiUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static String getOptionalStringParam(JSONObject params, String paramName)
* @param params the params
* @param paramName the param name
* @return the non empty string param
* @throws ApiException the api exception thown if param not found or string empty
* @throws ApiException the api exception thrown if param not found or string empty
*/
public static String getNonEmptyStringParam(JSONObject params, String paramName)
throws ApiException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ alert.api.view.alerts = Gets the alerts raised by ZAP, optionally filtering by U
alert.api.view.alerts.param.baseurl = The highest URL in the Sites tree under which alerts should be included.
alert.api.view.alerts.param.contextName = Optionally, the Context name which the Alerts' URLs are associated with.
alert.api.view.alerts.param.count =
alert.api.view.alerts.param.falsePositive = Optionally, a boolean indicating whether the results should include False Positive alerts.
alert.api.view.alerts.param.riskId =
alert.api.view.alerts.param.start =
alert.api.view.alertsByRisk = Gets a summary of the alerts, optionally filtered by a 'url'. If 'recurse' is true then all alerts that apply to urls that start with the specified 'url' will be returned, otherwise only those on exactly the same 'url' (ignoring url parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,30 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import net.sf.json.JSONObject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.quality.Strictness;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.db.Database;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.RecordAlert;
import org.parosproxy.paros.db.TableAlert;
import org.parosproxy.paros.model.Model;
import org.zaproxy.zap.WithConfigsTest;
import org.zaproxy.zap.db.TableAlertTag;
import org.zaproxy.zap.extension.api.ApiResponse;
import org.zaproxy.zap.extension.api.ApiResponseElement;
import org.zaproxy.zap.extension.api.ApiResponseList;

/** Unit test for {@link AlertAPI}. */
public class AlertAPIUnitTest {

private int alertIdCount;
private TableAlert tableAlert;
private TableAlertTag tableAlertTag;
private ExtensionAlert extensionAlert;
Expand All @@ -64,6 +72,7 @@ void setUp() {
given(db.getTableAlert()).willReturn(tableAlert);
tableAlertTag = mock(TableAlertTag.class);
given(db.getTableAlertTag()).willReturn(tableAlertTag);
alertIdCount = 1;

api = new AlertAPI(extensionAlert);
}
Expand Down Expand Up @@ -119,4 +128,63 @@ void shouldReturnAlertData() throws Exception {
equalTo(
"{\"alert\":{\"sourceid\":\"2\",\"other\":\"other info\",\"method\":\"\",\"evidence\":\"evidence\",\"pluginId\":\"1234\",\"cweid\":\"10\",\"confidence\":\"Medium\",\"sourceMessageId\":1234,\"wascid\":\"11\",\"description\":\"Alert Description\",\"messageId\":\"123\",\"inputVector\":\"input Vector\",\"url\":\"uri\",\"tags\":{},\"reference\":\"reference\",\"solution\":\"solution\",\"alert\":\"Alert Name\",\"param\":\"param\",\"attack\":\"attack\",\"name\":\"Alert Name\",\"risk\":\"Low\",\"id\":\"1\",\"alertRef\":\"1234-1\"}}")));
}

@Test
void shouldNotReturnFalsePositiveAlertsByDefault() throws Exception {
// Given
String name = "alerts";
JSONObject params = new JSONObject();
alertsWithConfidence(Alert.CONFIDENCE_HIGH, Alert.CONFIDENCE_FALSE_POSITIVE);

// When
ApiResponse response = api.handleApiView(name, params);
// Then
assertThat(response.getName(), is(equalTo(name)));
assertThat(response, is(instanceOf(ApiResponseList.class)));
assertThat(
response.toJSON().toString(),
is(
equalTo(
"{\"alerts\":[{\"sourceid\":\"0\",\"other\":\"\",\"method\":\"\",\"evidence\":\"\",\"pluginId\":\"0\",\"cweid\":\"0\",\"confidence\":\"High\",\"sourceMessageId\":0,\"wascid\":\"0\",\"description\":\"\",\"messageId\":\"0\",\"inputVector\":\"\",\"url\":\"\",\"tags\":{},\"reference\":\"\",\"solution\":\"\",\"alert\":\"\",\"param\":\"\",\"attack\":\"\",\"name\":\"\",\"risk\":\"Informational\",\"id\":\"1\",\"alertRef\":\"0\"}]}")));
}

@Test
void shouldReturnFalsePositiveAlertsWhenSpecified() throws Exception {
// Given
String name = "alerts";
JSONObject params = new JSONObject();
params.put("falsePositive", true);
alertsWithConfidence(Alert.CONFIDENCE_HIGH, Alert.CONFIDENCE_FALSE_POSITIVE);

// When
ApiResponse response = api.handleApiView(name, params);
// Then
assertThat(response.getName(), is(equalTo(name)));
assertThat(response, is(instanceOf(ApiResponseList.class)));
assertThat(
response.toJSON().toString(),
is(
equalTo(
"{\"alerts\":[{\"sourceid\":\"0\",\"other\":\"\",\"method\":\"\",\"evidence\":\"\",\"pluginId\":\"0\",\"cweid\":\"0\",\"confidence\":\"High\",\"sourceMessageId\":0,\"wascid\":\"0\",\"description\":\"\",\"messageId\":\"0\",\"inputVector\":\"\",\"url\":\"\",\"tags\":{},\"reference\":\"\",\"solution\":\"\",\"alert\":\"\",\"param\":\"\",\"attack\":\"\",\"name\":\"\",\"risk\":\"Informational\",\"id\":\"1\",\"alertRef\":\"0\"},"
+ "{\"sourceid\":\"0\",\"other\":\"\",\"method\":\"\",\"evidence\":\"\",\"pluginId\":\"0\",\"cweid\":\"0\",\"confidence\":\"False Positive\",\"sourceMessageId\":0,\"wascid\":\"0\",\"description\":\"\",\"messageId\":\"0\",\"inputVector\":\"\",\"url\":\"\",\"tags\":{},\"reference\":\"\",\"solution\":\"\",\"alert\":\"\",\"param\":\"\",\"attack\":\"\",\"name\":\"\",\"risk\":\"Informational\",\"id\":\"2\",\"alertRef\":\"0\"}]}")));
}

private void alertsWithConfidence(int... confidences) throws DatabaseException {
List<RecordAlert> alerts = Arrays.stream(confidences).mapToObj(this::recordAlert).toList();
Vector<Integer> alertIds =
alerts.stream()
.map(RecordAlert::getAlertId)
.collect(Collectors.toCollection(Vector::new));
given(tableAlert.getAlertList()).willReturn(alertIds);
for (RecordAlert alert : alerts) {
given(tableAlert.read(alert.getAlertId())).willReturn(alert);
}
}

private RecordAlert recordAlert(int confidence) {
RecordAlert recordAlert = mock();
given(recordAlert.getAlertId()).willReturn(alertIdCount++);
given(recordAlert.getConfidence()).willReturn(confidence);
return recordAlert;
}
}
Loading