Race condition in counting number of attempts #567 (#697)

Add version to Hibernate mapping so we get optimistic locking this solves
number of parallel calls trying to update/guess and mess with the lesson
counter
This commit is contained in:
Nanne Baars 2019-11-03 18:14:15 +01:00 committed by René Zubcevic
parent 1a83e2825e
commit f7b794bf68
4 changed files with 66 additions and 19 deletions

View File

@ -54,6 +54,8 @@ public class LessonTracker {
private final Set<Assignment> allAssignments = new HashSet<>(); private final Set<Assignment> allAssignments = new HashSet<>();
@Getter @Getter
private int numberOfAttempts = 0; private int numberOfAttempts = 0;
@Version
private Integer version;
private LessonTracker() { private LessonTracker() {
//JPA //JPA

View File

@ -0,0 +1 @@
ALTER TABLE CONTAINER.LESSON_TRACKER ADD VERSION INTEGER;

View File

@ -37,8 +37,8 @@ public abstract class IntegrationTest {
@BeforeClass @BeforeClass
public static void beforeAll() { public static void beforeAll() {
if (WG_SSL) { if (WG_SSL) {
WEBGOAT_URL = WEBGOAT_URL.replace("http:","https:"); WEBGOAT_URL = WEBGOAT_URL.replace("http:", "https:");
} }
if (!started) { if (!started) {
started = true; started = true;
@ -236,7 +236,8 @@ if (WG_SSL) {
.statusCode(200) .statusCode(200)
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
} }
public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {
public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {
Assert.assertThat( Assert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()

View File

@ -0,0 +1,43 @@
package org.owasp.webgoat;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ProgressRaceConditionTest extends IntegrationTest {
@Test
public void runTests() throws InterruptedException {
startLesson("Challenge1");
Callable<Response> call = () ->
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.formParams(Map.of("flag", "test"))
.post(url("/challenge/flag/"));
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<? extends Callable<Response>> flagCalls = IntStream.range(0, 20).mapToObj(i -> call).collect(Collectors.toList());
var responses = executorService.invokeAll(flagCalls);
//A certain amount of parallel calls should fail as optimistic locking in DB is applied
Assertions.assertThat(responses.stream().filter(r -> {
try {
return r.get().getStatusCode() == 500;
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
}
}).count()).isGreaterThan(10);
}
}