diff --git a/docker/policies/API-Minimal.policy b/docker/policies/API-Minimal.policy index af7b925a91c..a930737cdd4 100644 --- a/docker/policies/API-Minimal.policy +++ b/docker/policies/API-Minimal.policy @@ -1,7 +1,7 @@ API Minimal -dock-api-min +dock-api-min true OFF diff --git a/docker/policies/Default Policy.policy b/docker/policies/Default Policy.policy index 2fff5f11548..d7527e549ee 100644 --- a/docker/policies/Default Policy.policy +++ b/docker/policies/Default Policy.policy @@ -1,7 +1,7 @@ Default Policy - dock-default + dock-default true MEDIUM diff --git a/docker/policies/St-High-Th-High.policy b/docker/policies/St-High-Th-High.policy index 56e6de6dd3e..1a9b6cb4a1c 100644 --- a/docker/policies/St-High-Th-High.policy +++ b/docker/policies/St-High-Th-High.policy @@ -1,7 +1,7 @@ St-High-Th-High - dock-high-high + dock-high-high true HIGH diff --git a/docker/policies/St-High-Th-Low.policy b/docker/policies/St-High-Th-Low.policy index 733aa40717f..46c509c11b4 100644 --- a/docker/policies/St-High-Th-Low.policy +++ b/docker/policies/St-High-Th-Low.policy @@ -1,7 +1,7 @@ St-High-Th-Low - dock-high-low + dock-high-low true LOW diff --git a/docker/policies/St-High-Th-Med.policy b/docker/policies/St-High-Th-Med.policy index 89cd5f3df5f..81da14d05be 100644 --- a/docker/policies/St-High-Th-Med.policy +++ b/docker/policies/St-High-Th-Med.policy @@ -1,7 +1,7 @@ St-High-Th-Med - dock-high-med + dock-high-med true MEDIUM diff --git a/docker/policies/St-Ins-Th-High.policy b/docker/policies/St-Ins-Th-High.policy index 64457d5af3f..a62b95bd9ec 100644 --- a/docker/policies/St-Ins-Th-High.policy +++ b/docker/policies/St-Ins-Th-High.policy @@ -1,7 +1,7 @@ St-Ins-Th-High - dock-ins-high + dock-ins-high true HIGH diff --git a/docker/policies/St-Ins-Th-Low.policy b/docker/policies/St-Ins-Th-Low.policy index d6616c44ee5..e2653fb153a 100644 --- a/docker/policies/St-Ins-Th-Low.policy +++ b/docker/policies/St-Ins-Th-Low.policy @@ -1,7 +1,7 @@ St-Ins-Th-Low - dock-ins-low + dock-ins-low true LOW diff --git a/docker/policies/St-Ins-Th-Med.policy b/docker/policies/St-Ins-Th-Med.policy index dbdc33e3cb2..7a16a34b4a9 100644 --- a/docker/policies/St-Ins-Th-Med.policy +++ b/docker/policies/St-Ins-Th-Med.policy @@ -1,7 +1,7 @@ St-Ins-Th-Med - dock-ins-med + dock-ins-med true MEDIUM diff --git a/docker/policies/St-Low-Th-High.policy b/docker/policies/St-Low-Th-High.policy index ad681b55888..bc0f73ee24c 100644 --- a/docker/policies/St-Low-Th-High.policy +++ b/docker/policies/St-Low-Th-High.policy @@ -1,7 +1,7 @@ St-Low-Th-High - dock-low-high + dock-low-high true HIGH diff --git a/docker/policies/St-Low-Th-Low.policy b/docker/policies/St-Low-Th-Low.policy index c85f13b35c7..6282b7b051d 100644 --- a/docker/policies/St-Low-Th-Low.policy +++ b/docker/policies/St-Low-Th-Low.policy @@ -1,7 +1,7 @@ St-Low-Th-Low - dock-low-low + dock-low-low true LOW diff --git a/docker/policies/St-Low-Th-Med.policy b/docker/policies/St-Low-Th-Med.policy index a566b603f1e..65d449461d5 100644 --- a/docker/policies/St-Low-Th-Med.policy +++ b/docker/policies/St-Low-Th-Med.policy @@ -1,7 +1,7 @@ St-Low-Th-Med - dock-low-med + dock-low-med true MEDIUM diff --git a/docker/policies/St-Med-Th-High.policy b/docker/policies/St-Med-Th-High.policy index ed976d2c4eb..b35d1fb0594 100644 --- a/docker/policies/St-Med-Th-High.policy +++ b/docker/policies/St-Med-Th-High.policy @@ -1,7 +1,7 @@ St-Med-Th-High - dock-med-high + dock-med-high true HIGH diff --git a/docker/policies/St-Med-Th-Low.policy b/docker/policies/St-Med-Th-Low.policy index 5c004000071..9bea685563d 100644 --- a/docker/policies/St-Med-Th-Low.policy +++ b/docker/policies/St-Med-Th-Low.policy @@ -1,7 +1,7 @@ St-Med-Th-Low - dock-med-low + dock-med-low true LOW diff --git a/zap/src/main/java/org/zaproxy/zap/extension/alert/AlertAPI.java b/zap/src/main/java/org/zaproxy/zap/extension/alert/AlertAPI.java index 6210eeaef29..ba0554be1b4 100644 --- a/zap/src/main/java/org/zaproxy/zap/extension/alert/AlertAPI.java +++ b/zap/src/main/java/org/zaproxy/zap/extension/alert/AlertAPI.java @@ -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"; @@ -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( @@ -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 counter = new CounterProcessor<>(); @@ -471,6 +478,17 @@ private static ApiResponseList filterAlertInstances( private static void processAlerts( String baseUrl, int start, int count, int riskId, Processor processor) throws ApiException { + processAlerts(baseUrl, start, count, riskId, processor, false); + } + + private static void processAlerts( + String baseUrl, + int start, + int count, + int riskId, + Processor processor, + boolean falsePositive) + throws ApiException { List alerts = new ArrayList<>(); try { TableAlert tableAlert = Model.getSingleton().getDb().getTableAlert(); @@ -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 diff --git a/zap/src/main/java/org/zaproxy/zap/utils/ApiUtils.java b/zap/src/main/java/org/zaproxy/zap/utils/ApiUtils.java index 699542bd2dd..4d91588810d 100644 --- a/zap/src/main/java/org/zaproxy/zap/utils/ApiUtils.java +++ b/zap/src/main/java/org/zaproxy/zap/utils/ApiUtils.java @@ -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 { diff --git a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties index 43f70aba9cd..66467353cda 100644 --- a/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties +++ b/zap/src/main/resources/org/zaproxy/zap/resources/Messages.properties @@ -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) diff --git a/zap/src/test/java/org/zaproxy/zap/extension/alert/AlertAPIUnitTest.java b/zap/src/test/java/org/zaproxy/zap/extension/alert/AlertAPIUnitTest.java index deb4c6ce6d0..da6e020ebe1 100644 --- a/zap/src/test/java/org/zaproxy/zap/extension/alert/AlertAPIUnitTest.java +++ b/zap/src/test/java/org/zaproxy/zap/extension/alert/AlertAPIUnitTest.java @@ -27,11 +27,17 @@ 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; @@ -39,10 +45,12 @@ 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; @@ -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); } @@ -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 alerts = Arrays.stream(confidences).mapToObj(this::recordAlert).toList(); + Vector 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; + } }