From 2d130b1c64aa9f5474708fc2d37423ee7767a869 Mon Sep 17 00:00:00 2001 From: Madalin Ilie Date: Thu, 16 Nov 2023 09:04:08 +0200 Subject: [PATCH] Fix for #85 - properly replace arrays when passed in securityfuzzer file --- src/main/java/com/endava/cats/util/CatsUtil.java | 12 ++++++++++++ .../cats/fuzzer/special/SecurityFuzzerTest.java | 12 +++++++++++- .../java/com/endava/cats/util/CatsUtilTest.java | 16 ++++++++++++++++ src/test/resources/securityFuzzer-arrays.yml | 12 ++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/securityFuzzer-arrays.yml diff --git a/src/main/java/com/endava/cats/util/CatsUtil.java b/src/main/java/com/endava/cats/util/CatsUtil.java index c3a8aee9f..207de1615 100644 --- a/src/main/java/com/endava/cats/util/CatsUtil.java +++ b/src/main/java/com/endava/cats/util/CatsUtil.java @@ -15,6 +15,7 @@ import net.minidev.json.parser.ParseException; import org.apache.commons.lang3.StringUtils; import org.jboss.logmanager.LogContext; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; @@ -110,6 +111,9 @@ public FuzzingResult replaceField(String payload, String jsonPropertyForReplacem private static void replaceOldValueWithNewOne(String jsonPropertyForReplacement, DocumentContext jsonDocument, Object valueToSet) { if (JsonUtils.isValidJson(String.valueOf(valueToSet))) { + if (areBothPropertyToReplaceAndValueToReplaceArrays(jsonPropertyForReplacement, valueToSet)) { + jsonPropertyForReplacement = removeArrayTermination(jsonPropertyForReplacement); + } try { jsonDocument.set(JsonUtils.sanitizeToJsonPath(jsonPropertyForReplacement), JsonUtils.GENERIC_PERMISSIVE_PARSER.parse(String.valueOf(valueToSet))); } catch (ParseException e) { @@ -120,6 +124,14 @@ private static void replaceOldValueWithNewOne(String jsonPropertyForReplacement, } } + @NotNull + private static String removeArrayTermination(String jsonPropertyForReplacement) { + return jsonPropertyForReplacement.substring(0, jsonPropertyForReplacement.lastIndexOf("[*]")); + } + + private static boolean areBothPropertyToReplaceAndValueToReplaceArrays(String jsonPropertyForReplacement, Object valueToSet) { + return jsonPropertyForReplacement.endsWith("[*]") && valueToSet instanceof List; + } /** * When parsing the custom fuzzer files the additionalProperties element will be parsed as: diff --git a/src/test/java/com/endava/cats/fuzzer/special/SecurityFuzzerTest.java b/src/test/java/com/endava/cats/fuzzer/special/SecurityFuzzerTest.java index 40a6c532f..65ebebc5d 100644 --- a/src/test/java/com/endava/cats/fuzzer/special/SecurityFuzzerTest.java +++ b/src/test/java/com/endava/cats/fuzzer/special/SecurityFuzzerTest.java @@ -132,6 +132,16 @@ void shouldConsiderHttpBodyFuzzingWhenRunningFuzzer() throws Exception { Mockito.verify(testCaseListener, Mockito.times(22)).reportResult(Mockito.any(), Mockito.eq(data), Mockito.any(), Mockito.eq(ResponseCodeFamily.TWOXX)); } + @Test + void shouldReplaceArraysFields() throws Exception { + FuzzingData data = setContext("src/test/resources/securityFuzzer-arrays.yml", "{'name': {'first': 'Cats'}, 'id': '25'}"); + data.getHeaders().add(CatsHeader.builder().name("header").value("value").build()); + SecurityFuzzer spySecurityFuzzer = Mockito.spy(securityFuzzer); + filesArguments.loadSecurityFuzzerFile(); + spySecurityFuzzer.fuzz(data); + Mockito.verify(testCaseListener, Mockito.times(22)).reportResult(Mockito.any(), Mockito.eq(data), Mockito.any(), Mockito.eq(ResponseCodeFamily.TWOXX)); + } + private FuzzingData setContext(String fuzzerFile, String responsePayload) throws Exception { ReflectionTestUtils.setField(filesArguments, "securityFuzzerFile", new File(fuzzerFile)); @@ -148,7 +158,7 @@ private FuzzingData setContext(String fuzzerFile, String responsePayload) throws properties.put("email", email); ObjectSchema person = new ObjectSchema(); person.setProperties(properties); - FuzzingData data = FuzzingData.builder().path("/pets/{id}/move").payload("{'name':'oldValue', 'firstName':'John','lastName':'Cats','email':'john@yahoo.com'}"). + FuzzingData data = FuzzingData.builder().path("/pets/{id}/move").payload("{'name':'oldValue', 'firstName':'John','lastName':'Cats','email':'john@yahoo.com', 'arrayField':[5,4]}"). responses(responses).responseCodes(Collections.singleton("200")).method(HttpMethod.POST).reqSchema(person).headers(new HashSet<>()) .requestContentTypes(List.of("application/json")).requestPropertyTypes(properties).build(); Mockito.when(serviceCaller.call(Mockito.any())).thenReturn(catsResponse); diff --git a/src/test/java/com/endava/cats/util/CatsUtilTest.java b/src/test/java/com/endava/cats/util/CatsUtilTest.java index 0dc999c60..60d8c737c 100644 --- a/src/test/java/com/endava/cats/util/CatsUtilTest.java +++ b/src/test/java/com/endava/cats/util/CatsUtilTest.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -90,4 +91,19 @@ void shouldReplace() { FuzzingResult result = catsUtil.replaceField(payload, "arrayOfData", FuzzingStrategy.trail().withData("test")); Assertions.assertThat(result.json()).contains("test").contains("FAoe22OkDDln6qHyqALVI1test").contains("USA"); } + + @Test + void shouldReplaceWithArray() { + CatsUtil catsUtil = new CatsUtil(); + String payload = """ + { + "arrayWithInteger": [ + 88,99 + ], + "country": "USA" + } + """; + FuzzingResult result = catsUtil.replaceField(payload, "arrayWithInteger", FuzzingStrategy.replace().withData(List.of(55, 66))); + Assertions.assertThat(result.json()).contains("55").contains("66").contains("USA").doesNotContain("88").doesNotContain("99"); + } } diff --git a/src/test/resources/securityFuzzer-arrays.yml b/src/test/resources/securityFuzzer-arrays.yml new file mode 100644 index 000000000..16202b0d9 --- /dev/null +++ b/src/test/resources/securityFuzzer-arrays.yml @@ -0,0 +1,12 @@ +/pets/{id}/move: + test1: + description: XSS strings + arrayField: + - 1 + - 2 + - 3 + targetFields: + - firstName + stringsFile: xss.txt + httpMethod: POST + expectedResponseCode: 200 \ No newline at end of file