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

@ -236,6 +236,7 @@ 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()

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);
}
}