fix: SQL advanced assignment 5 (#2047)

- Add and show correct hints
- Fix solving the lesson immediately when you register as tom. Now uses `informationMessage` to display a message in the UI
- Add Playwright test

Closes: gh-2045
This commit is contained in:
Nanne Baars
2025-03-02 20:31:05 +01:00
committed by GitHub
parent c37a8e8c19
commit e9f79cc739
20 changed files with 189 additions and 97 deletions

View File

@ -4,11 +4,9 @@
*/
package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.i18n.PluginMessages;
public class AttackResultBuilder {
private boolean lessonCompleted;
private boolean assignmentCompleted;
private Object[] feedbackArgs;
private String feedbackResourceBundleKey;
private String output;
@ -16,15 +14,8 @@ public class AttackResultBuilder {
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = "lesson.completed";
return this;
}
public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = resourceBundleKey;
public AttackResultBuilder assignmentCompleted(boolean lessonCompleted) {
this.assignmentCompleted = lessonCompleted;
return this;
}
@ -55,7 +46,7 @@ public class AttackResultBuilder {
public AttackResult build() {
return new AttackResult(
lessonCompleted,
assignmentCompleted,
feedbackResourceBundleKey,
feedbackArgs,
output,
@ -81,7 +72,7 @@ public class AttackResultBuilder {
*/
public static AttackResultBuilder success(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.lessonCompleted(true)
.assignmentCompleted(true)
.attemptWasMade()
.feedback("assignment.solved")
.assignment(assignment);
@ -99,13 +90,13 @@ public class AttackResultBuilder {
*/
public static AttackResultBuilder failed(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.lessonCompleted(false)
.assignmentCompleted(false)
.attemptWasMade()
.feedback("assignment.not.solved")
.assignment(assignment);
}
public static AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return new AttackResultBuilder().lessonCompleted(false).assignment(assignment);
return new AttackResultBuilder().assignmentCompleted(false).assignment(assignment);
}
}

View File

@ -84,7 +84,7 @@ public class CourseConfiguration {
@Bean
public Course course() {
assignments.stream().forEach(this::attachToLesson);
assignments.forEach(this::attachToLesson);
// Check if all assignments are attached to a lesson
var assignmentsAttachedToLessons =
@ -99,7 +99,7 @@ public class CourseConfiguration {
private List<String> findDiff() {
var matchedToLessons =
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList();
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(Assignment::getName).toList();
var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList();
var diff = new ArrayList<>(allAssignments);

View File

@ -5,7 +5,7 @@
package org.owasp.webgoat.lessons.sqlinjection.advanced;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage;
import java.sql.*;
import lombok.extern.slf4j.Slf4j;
@ -19,13 +19,17 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author nbaars
* @since 4/8/17.
*/
@RestController
@AssignmentHints(
value = {"SqlInjectionChallenge1", "SqlInjectionChallenge2", "SqlInjectionChallenge3"})
value = {
"SqlInjectionChallenge1",
"SqlInjectionChallenge2",
"SqlInjectionChallenge3",
"SqlInjectionChallenge4",
"SqlInjectionChallenge5",
"SqlInjectionChallenge6",
"SqlInjectionChallenge7"
})
@Slf4j
public class SqlInjectionChallenge implements AssignmentEndpoint {
@ -35,38 +39,34 @@ public class SqlInjectionChallenge implements AssignmentEndpoint {
this.dataSource = dataSource;
}
@PutMapping("/SqlInjectionAdvanced/challenge")
@PutMapping("/SqlInjectionAdvanced/register")
// assignment path is bounded to class so we use different http method :-)
@ResponseBody
public AttackResult registerNewUser(
@RequestParam String username_reg,
@RequestParam String email_reg,
@RequestParam String password_reg)
throws Exception {
AttackResult attackResult = checkArguments(username_reg, email_reg, password_reg);
@RequestParam("username_reg") String username,
@RequestParam("email_reg") String email,
@RequestParam("password_reg") String password) {
AttackResult attackResult = checkArguments(username, email, password);
if (attackResult == null) {
try (Connection connection = dataSource.getConnection()) {
String checkUserQuery =
"select userid from sql_challenge_users where userid = '" + username_reg + "'";
"select userid from sql_challenge_users where userid = '" + username + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(checkUserQuery);
if (resultSet.next()) {
if (username_reg.contains("tom'")) {
attackResult = success(this).feedback("user.exists").build();
} else {
attackResult = failed(this).feedback("user.exists").feedbackArgs(username_reg).build();
}
attackResult = failed(this).feedback("user.exists").feedbackArgs(username).build();
} else {
PreparedStatement preparedStatement =
connection.prepareStatement("INSERT INTO sql_challenge_users VALUES (?, ?, ?)");
preparedStatement.setString(1, username_reg);
preparedStatement.setString(2, email_reg);
preparedStatement.setString(3, password_reg);
preparedStatement.setString(1, username);
preparedStatement.setString(2, email);
preparedStatement.setString(3, password);
preparedStatement.execute();
attackResult = success(this).feedback("user.created").feedbackArgs(username_reg).build();
attackResult =
informationMessage(this).feedback("user.created").feedbackArgs(username).build();
}
} catch (SQLException e) {
attackResult = failed(this).output("Something went wrong").build();
@ -75,13 +75,13 @@ public class SqlInjectionChallenge implements AssignmentEndpoint {
return attackResult;
}
private AttackResult checkArguments(String username_reg, String email_reg, String password_reg) {
if (StringUtils.isEmpty(username_reg)
|| StringUtils.isEmpty(email_reg)
|| StringUtils.isEmpty(password_reg)) {
private AttackResult checkArguments(String username, String email, String password) {
if (StringUtils.isEmpty(username)
|| StringUtils.isEmpty(email)
|| StringUtils.isEmpty(password)) {
return failed(this).feedback("input.invalid").build();
}
if (username_reg.length() > 250 || email_reg.length() > 30 || password_reg.length() > 30) {
if (username.length() > 250 || email.length() > 30 || password.length() > 30) {
return failed(this).feedback("input.invalid").build();
}
return null;

View File

@ -9,7 +9,6 @@ import static org.owasp.webgoat.container.assignments.AttackResultBuilder.succes
import org.owasp.webgoat.container.LessonDataSource;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -17,13 +16,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AssignmentHints(
value = {
"SqlInjectionChallengeHint1",
"SqlInjectionChallengeHint2",
"SqlInjectionChallengeHint3",
"SqlInjectionChallengeHint4"
})
public class SqlInjectionChallengeLogin implements AssignmentEndpoint {
private final LessonDataSource dataSource;
@ -31,20 +23,22 @@ public class SqlInjectionChallengeLogin implements AssignmentEndpoint {
this.dataSource = dataSource;
}
@PostMapping("/SqlInjectionAdvanced/challenge_Login")
@PostMapping("/SqlInjectionAdvanced/login")
@ResponseBody
public AttackResult login(
@RequestParam String username_login, @RequestParam String password_login) throws Exception {
@RequestParam("username_login") String username,
@RequestParam("password_login") String password)
throws Exception {
try (var connection = dataSource.getConnection()) {
var statement =
connection.prepareStatement(
"select password from sql_challenge_users where userid = ? and password = ?");
statement.setString(1, username_login);
statement.setString(2, password_login);
statement.setString(1, username);
statement.setString(2, password);
var resultSet = statement.executeQuery();
if (resultSet.next()) {
return ("tom".equals(username_login))
return ("tom".equals(username))
? success(this).build()
: failed(this).feedback("ResultsButNotTom").build();
} else {