Compare commits
92 Commits
v2023.7
...
nbaars/bui
Author | SHA1 | Date | |
---|---|---|---|
d94d99a942 | |||
52c20738f9 | |||
e5d5a370f9 | |||
cf5101a633 | |||
3f049ba53a | |||
7e294fbdb5 | |||
2177eb663a | |||
50692300eb | |||
e2c2d425cb | |||
6bbd3cb66b | |||
d08a56d351 | |||
ec97568ec2 | |||
9b68368b23 | |||
ab068901f1 | |||
cb7c508046 | |||
f4c86be6c7 | |||
cf2c115093 | |||
bb6e84ddcf | |||
5fc2666b43 | |||
6e946f21a2 | |||
d38ba2a626 | |||
4c7e6ae4f4 | |||
58b762eade | |||
2b0c22ac68 | |||
85103bbcad | |||
b98e1a98e1 | |||
73de259809 | |||
4a804fabb6 | |||
7f652dadec | |||
f66dff1aeb | |||
777cec5a57 | |||
98fd280459 | |||
8e0addff0b | |||
a30fbc223a | |||
a0b6decf34 | |||
1531987da5 | |||
bec6580c84 | |||
900702481b | |||
f35b23970e | |||
074fd8f5ff | |||
8c890b090c | |||
630c1e1afd | |||
061c75a05f | |||
5e5a1363b0 | |||
47df923c8c | |||
5dbc642264 | |||
96bc6fa608 | |||
1d5a2bb6a2 | |||
8b37bd58d7 | |||
6545e2a066 | |||
9573b30667 | |||
30e40f2e59 | |||
8f11fb6729 | |||
10e36c203f | |||
edcce09b5f | |||
3134f18066 | |||
e219887f14 | |||
508703ffce | |||
e308d7cde7 | |||
4ab820e1d1 | |||
1a6a7e0be1 | |||
2e9140ab64 | |||
b79c83a52e | |||
297c6f49b5 | |||
d2049a8fcc | |||
24db39eae2 | |||
98443184e9 | |||
62931a1836 | |||
c18430752a | |||
57d5b313b9 | |||
dd0f135088 | |||
ad0286d5ba | |||
b67eb44142 | |||
7e75e9b8fc | |||
40c679ec5a | |||
4ebb869f5d | |||
6bb7a182dc | |||
cb2c99d38d | |||
84029345b4 | |||
a0ca199cdc | |||
2058298e2d | |||
17acef57b4 | |||
d913967ec5 | |||
87edc7d1db | |||
ac7a9c7863 | |||
2803ef45e4 | |||
5357a65e05 | |||
d343c60781 | |||
98acc1f55a | |||
f99888e61b | |||
29dda49190 | |||
369be6f688 |
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@ -8,10 +8,7 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
ignore:
|
||||
- dependency-name: "org.webjars:bootstrap" # First the WebWolf UI needs to be refactored due to breaking changes
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
|
@ -11,9 +11,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
java-version: [ 17, 21 ]
|
||||
java-version: [ 21 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
82
.github/workflows/build.yml
vendored
82
.github/workflows/build.yml
vendored
@ -1,60 +1,50 @@
|
||||
name: "Pull requests build"
|
||||
name: "Main / Pull requests build"
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.txt'
|
||||
- 'LICENSE'
|
||||
- 'docs/**'
|
||||
branches: [ main ]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
pr-build:
|
||||
if: >
|
||||
github.event_name == 'pull_request' && !github.event.pull_request.draft && (
|
||||
github.event.action == 'opened' ||
|
||||
github.event.action == 'reopened' ||
|
||||
github.event.action == 'synchronize'
|
||||
)
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
pre-commit:
|
||||
name: Pre-commit check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
- 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
|
||||
architecture: x64
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3.3.1
|
||||
java-version: '21'
|
||||
- name: Pre-commit checks
|
||||
uses: pre-commit/action@v3.0.0
|
||||
- name: pre-commit-ci-lite
|
||||
uses: pre-commit-ci/lite-action@v1.1.0
|
||||
if: always()
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ windows-latest, ubuntu-latest, macos-13 ]
|
||||
max-parallel: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4.2.1
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2-
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
architecture: x64
|
||||
cache: 'maven'
|
||||
- name: Build with Maven
|
||||
run: mvn --no-transfer-progress verify
|
||||
- name: "Set up QEMU"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/setup-qemu-action@v2.2.0
|
||||
- name: "Set up Docker Buildx"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: "Verify Docker WebGoat build"
|
||||
if: runner.os == 'Linux'
|
||||
uses: docker/build-push-action@v5.1.0
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: false
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
- name: "Verify Docker WebGoat desktop build"
|
||||
uses: docker/build-push-action@v5.1.0
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile_desktop
|
||||
push: false
|
||||
build-args: |
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
|
33
.github/workflows/release.yml
vendored
33
.github/workflows/release.yml
vendored
@ -8,24 +8,20 @@ jobs:
|
||||
if: github.repository == 'WebGoat/WebGoat'
|
||||
name: Release WebGoat
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
environment:
|
||||
name: release
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
architecture: x64
|
||||
|
||||
- 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
|
||||
cache: 'maven'
|
||||
|
||||
- name: "Set labels for ${{ github.ref }}"
|
||||
run: |
|
||||
@ -72,7 +68,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: "Set up QEMU"
|
||||
uses: docker/setup-qemu-action@v2.2.0
|
||||
uses: docker/setup-qemu-action@v3.1.0
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
@ -80,18 +76,18 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: "Login to dockerhub"
|
||||
uses: docker/login-action@v3.0.0
|
||||
uses: docker/login-action@v3.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: "Build and push WebGoat"
|
||||
uses: docker/build-push-action@v5.1.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64, linux/arm64, linux/arm/v7
|
||||
platforms: linux/amd64, linux/arm64
|
||||
tags: |
|
||||
webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }}
|
||||
webgoat/webgoat:latest
|
||||
@ -99,7 +95,7 @@ jobs:
|
||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||
|
||||
- name: "Build and push WebGoat desktop"
|
||||
uses: docker/build-push-action@v5.1.0
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
context: ./
|
||||
file: ./Dockerfile_desktop
|
||||
@ -116,15 +112,15 @@ jobs:
|
||||
needs: [ release ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
architecture: x64
|
||||
|
||||
- name: Set version to next snapshot
|
||||
@ -145,4 +141,3 @@ jobs:
|
||||
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
title: ${{ github.event.commits[0].message }}
|
||||
target_branch: main
|
||||
|
||||
|
22
.github/workflows/test.yml
vendored
22
.github/workflows/test.yml
vendored
@ -21,27 +21,21 @@ jobs:
|
||||
name: "Robot framework test"
|
||||
steps:
|
||||
# Uses an default action to checkout the code
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4.1.6
|
||||
# Uses an action to add Python to the VM
|
||||
- name: Setup Pyton
|
||||
uses: actions/setup-python@v4
|
||||
- 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
|
||||
# Uses an action to add JDK 21 to the VM (and mvn?)
|
||||
- name: set up JDK 21
|
||||
uses: actions/setup-java@v4.2.1
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
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-
|
||||
cache: 'maven'
|
||||
- uses: BSFishy/pip-action@v1
|
||||
with:
|
||||
packages: |
|
||||
|
28
.pre-commit-config.yaml
Normal file
28
.pre-commit-config.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
ci:
|
||||
autofix_commit_msg: |
|
||||
[pre-commit.ci] auto fixes from pre-commit.com hooks
|
||||
autofix_prs: false # managed in the action step
|
||||
autoupdate_branch: ""
|
||||
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
|
||||
autoupdate_schedule: weekly
|
||||
skip: []
|
||||
submodules: false
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^(README.md|CREATE_RELEASE.md)
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||
rev: v9.5.0
|
||||
hooks:
|
||||
- id: commitlint
|
||||
stages: [commit-msg]
|
||||
- repo: https://github.com/ejba/pre-commit-maven
|
||||
rev: v0.3.4
|
||||
hooks:
|
||||
- id: maven
|
||||
args: [ 'clean compile' ]
|
||||
- id: maven-spotless-apply
|
11
Dockerfile
11
Dockerfile
@ -1,6 +1,8 @@
|
||||
FROM docker.io/eclipse-temurin:21.0.1_12-jre
|
||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||
MAINTAINER "WebGoat team"
|
||||
# We need JDK as some of the lessons needs to be able to compile Java code
|
||||
FROM docker.io/eclipse-temurin:21-jdk-jammy
|
||||
|
||||
LABEL name="WebGoat: A deliberately insecure Web Application"
|
||||
LABEL maintainer="WebGoat team"
|
||||
|
||||
RUN \
|
||||
useradd -ms /bin/bash webgoat && \
|
||||
@ -33,3 +35,6 @@ ENTRYPOINT [ "java", \
|
||||
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
|
||||
"-Drunning.in.docker=true", \
|
||||
"-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
|
||||
|
@ -1,6 +1,6 @@
|
||||
FROM lscr.io/linuxserver/webtop:ubuntu-xfce
|
||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||
MAINTAINER "WebGoat team"
|
||||
LABEL maintainer = "WebGoat team"
|
||||
|
||||
WORKDIR /config
|
||||
|
||||
@ -9,26 +9,38 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
|
||||
COPY config/desktop/start_zap.sh /config/start_zap.sh
|
||||
COPY config/desktop/WebGoat.txt /config/Desktop/
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get --yes install vim nano gzip
|
||||
|
||||
RUN \
|
||||
case $(uname -m) in \
|
||||
x86_64) ARCH=x64;; \
|
||||
aarch64) ARCH=aarch64;; \
|
||||
*) ARCH=unknown;; \
|
||||
esac && \
|
||||
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 && \
|
||||
rm -rf ZAP_2.12.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 OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
||||
rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
||||
echo ${ARCH}
|
||||
|
||||
RUN \
|
||||
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \
|
||||
tar zfxv ZAP_2.15.0_Linux.tar.gz && \
|
||||
rm -rf ZAP_2.15.0_Linux.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/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_"${ARCH}"_linux_hotspot_21.0.3_9.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_zap.sh && \
|
||||
apt-get update && \
|
||||
apt-get --yes install vim nano && \
|
||||
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
|
||||
echo "JAVA_HOME=/config/jdk-21.0.3+9-jre/" >> .bash_aliases && \
|
||||
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
||||
|
||||
|
||||
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
|
||||
ENV JAVA_HOME=/config/jdk-21.0.3+9-jre
|
||||
|
||||
WORKDIR /config/Desktop
|
||||
|
1
FAQ.md
1
FAQ.md
@ -5,4 +5,3 @@
|
||||
### Integration tests fail
|
||||
|
||||
Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line.
|
||||
|
||||
|
16
README.md
16
README.md
@ -1,7 +1,7 @@
|
||||
# WebGoat: A deliberately insecure Web Application
|
||||
|
||||
[](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
|
||||
[](https://jdk.java.net/)
|
||||
[](https://jdk.java.net/)
|
||||
[](https://owasp.org/projects/)
|
||||
[](https://github.com/WebGoat/WebGoat/releases/latest)
|
||||
[](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
@ -80,11 +80,21 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel
|
||||
|
||||
```shell
|
||||
export TZ=Europe/Amsterdam # or your timezone
|
||||
java -Dfile.encoding=UTF-8 -jar webgoat-2023.5.jar
|
||||
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
### Prerequisites:
|
||||
@ -144,7 +154,7 @@ For instance running as a jar on a Linux/macOS it will look like this:
|
||||
export TZ=Europe/Amsterdam # or your timezone
|
||||
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
|
||||
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
|
||||
java -jar target/webgoat-2023.6-SNAPSHOT.jar
|
||||
java -jar target/webgoat-2023.8-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
||||
|
@ -17,18 +17,18 @@ The following steps are required when you want to add a new language
|
||||
1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html)
|
||||
1. Add the parts for showing the flag and providing the correct value for the flag= parameter
|
||||
2.
|
||||
3. Add a flag image to src/main/resources/webgoat/static/css/img
|
||||
2. Add a flag image to src/main/resources/webgoat/static/css/img
|
||||
1. See the main_new.html for a link to download flag resources
|
||||
4. Add a welcome page to the introduction lesson
|
||||
3. Add a welcome page to the introduction lesson
|
||||
1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish)
|
||||
2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable
|
||||
5. Translate the main labels
|
||||
4. Translate the main labels
|
||||
1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish)
|
||||
2. Translate the label values
|
||||
6. Optionally translate lessons by
|
||||
5. Optionally translate lessons by
|
||||
1. Adding lang specifc adoc files in documentation folder of the lesson
|
||||
2. Adding WebGoatLabels.properties of a specific language if you want to
|
||||
7. Run mvn clean to see if the LabelAndHintIntegration test passes
|
||||
8. Run WebGoat and verify that your own language and the other languages work as expected
|
||||
6. Run mvn clean to see if the LabelAndHintIntegration test passes
|
||||
7. Run WebGoat and verify that your own language and the other languages work as expected
|
||||
|
||||
If you only want to translate more for a certain language, you only need to do step 4-8
|
||||
|
@ -1,6 +1,6 @@
|
||||
# WebGoat release notes
|
||||
|
||||
## Version 2023.7
|
||||
## Version 2023.8
|
||||
|
||||
### 🚀 New functionality
|
||||
|
||||
@ -215,4 +215,3 @@ Special thanks to the following contributors providing us with a pull request:
|
||||
And everyone who provided feedback through Github.
|
||||
|
||||
Team WebGoat
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
/config/jdk-17.0.6+10-jre/bin/java \
|
||||
/config/jdk-21.0.3+9-jre/bin/java \
|
||||
-Duser.home=/config \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-DTZ=Europe/Amsterdam \
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
|
||||
/config/jdk-21.0.3+9-jre/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar
|
||||
|
@ -1,4 +1,3 @@
|
||||
# WebGoat landing page
|
||||
|
||||
Old GitHub page which now redirects to OWASP website.
|
||||
|
||||
|
163
pom.xml
163
pom.xml
@ -5,12 +5,12 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.5</version>
|
||||
<version>3.3.5</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.owasp.webgoat</groupId>
|
||||
<artifactId>webgoat</artifactId>
|
||||
<version>2023.7</version>
|
||||
<version>2024.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>WebGoat</name>
|
||||
@ -29,13 +29,6 @@
|
||||
</licenses>
|
||||
|
||||
<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>
|
||||
<id>nbaars</id>
|
||||
<name>Nanne Baars</name>
|
||||
@ -43,11 +36,6 @@
|
||||
<organizationUrl>https://github.com/nbaars</organizationUrl>
|
||||
<timezone>Europe/Amsterdam</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>misfir3</id>
|
||||
<name>Jason White</name>
|
||||
<email>jason.white@owasp.org</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>zubcevic</id>
|
||||
<name>René Zubcevic</name>
|
||||
@ -58,43 +46,8 @@
|
||||
<name>Àngel Ollé Blázquez</name>
|
||||
<email>angel@olleb.com</email>
|
||||
</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&s=150</picUrl>
|
||||
</properties>
|
||||
</developer>
|
||||
</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>
|
||||
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
|
||||
@ -109,61 +62,56 @@
|
||||
|
||||
<properties>
|
||||
<!-- Shared properties with plugins and version numbers across submodules-->
|
||||
<asciidoctorj.version>2.5.10</asciidoctorj.version>
|
||||
<bootstrap.version>5.3.1</bootstrap.version>
|
||||
<asciidoctorj.version>3.0.0</asciidoctorj.version>
|
||||
<bootstrap.version>5.3.3</bootstrap.version>
|
||||
<cglib.version>3.3.0</cglib.version>
|
||||
<!-- do not update necessary for lesson -->
|
||||
<checkstyle.version>3.3.1</checkstyle.version>
|
||||
<checkstyle.version>3.4.0</checkstyle.version>
|
||||
<commons-collections.version>3.2.1</commons-collections.version>
|
||||
<commons-io.version>2.15.1</commons-io.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<commons-text.version>1.10.0</commons-text.version>
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
<commons-compress.version>1.27.1</commons-compress.version>
|
||||
<commons-io.version>2.16.1</commons-io.version>
|
||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||
<commons-text.version>1.12.0</commons-text.version>
|
||||
<guava.version>33.3.0-jre</guava.version>
|
||||
<jacoco.version>0.8.11</jacoco.version>
|
||||
<java.version>17</java.version>
|
||||
<java.version>21</java.version>
|
||||
<jaxb.version>2.3.1</jaxb.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<jose4j.version>0.9.3</jose4j.version>
|
||||
<jquery.version>3.7.0</jquery.version>
|
||||
<jsoup.version>1.16.1</jsoup.version>
|
||||
<jquery.version>3.7.1</jquery.version>
|
||||
<jsoup.version>1.18.1</jsoup.version>
|
||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-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-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
|
||||
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
|
||||
<maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven-surefire-plugin.version>3.5.1</maven-surefire-plugin.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<pmd.version>3.15.0</pmd.version>
|
||||
<!-- Use UTF-8 Encoding -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<thymeleaf.version>3.1.1.RELEASE</thymeleaf.version>
|
||||
<webdriver.version>5.3.3</webdriver.version>
|
||||
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
|
||||
<waittimeForServerStart>30</waittimeForServerStart>
|
||||
<webdriver.version>5.9.2</webdriver.version>
|
||||
<webgoat.context>/</webgoat.context>
|
||||
<webgoat.sslenabled>false</webgoat.sslenabled>
|
||||
<webjars-locator-core.version>0.53</webjars-locator-core.version>
|
||||
<webjars-locator-core.version>0.59</webjars-locator-core.version>
|
||||
<webwolf.context>/</webwolf.context>
|
||||
<wiremock.version>2.27.2</wiremock.version>
|
||||
<wiremock.version>3.9.1</wiremock.version>
|
||||
<xml-resolver.version>1.2</xml-resolver.version>
|
||||
<xstream.version>1.4.5</xstream.version>
|
||||
<!-- do not update necessary for lesson -->
|
||||
<zxcvbn.version>1.8.0</zxcvbn.version>
|
||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-exec</artifactId>
|
||||
<version>1.3</version>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
@ -248,8 +196,8 @@
|
||||
<version>${webjars-locator-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock</artifactId>
|
||||
<groupId>org.wiremock</groupId>
|
||||
<artifactId>wiremock-standalone</artifactId>
|
||||
<version>${wiremock.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -260,12 +208,12 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.25.0</version>
|
||||
<version>${commons-compress.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jruby</groupId>
|
||||
<artifactId>jruby</artifactId>
|
||||
<version>9.4.3.0</version>
|
||||
<version>9.4.8.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@ -284,24 +232,26 @@
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.20.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>1.20.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
<version>${jaxb.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -311,6 +261,10 @@
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-database-hsqldb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctorj</artifactId>
|
||||
@ -417,6 +371,12 @@
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.terma</groupId>
|
||||
<artifactId>javaniotcpproxy</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -429,10 +389,8 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock</artifactId>
|
||||
<version>3.0.0-beta-2</version>
|
||||
<scope>test</scope>
|
||||
<groupId>org.wiremock</groupId>
|
||||
<artifactId>wiremock-standalone</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
@ -548,6 +506,7 @@
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<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
|
||||
--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
|
||||
@ -555,8 +514,6 @@
|
||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
|
||||
<excludes>
|
||||
<exclude>**/*IntegrationTest.java</exclude>
|
||||
<exclude>src/it/java</exclude>
|
||||
<exclude>org/owasp/webgoat/*Test</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@ -636,7 +593,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>restrict-log4j-versions</id>
|
||||
@ -660,10 +617,6 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
@ -735,24 +688,18 @@
|
||||
<argument>--add-opens</argument>
|
||||
<argument>java.base/java.lang.reflect=ALL-UNNAMED</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>--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>--add-opens</argument>
|
||||
<argument>java.base/java.io=ALL-UNNAMED</argument>
|
||||
<argument>--add-opens</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>
|
||||
</arguments>
|
||||
<waitForInterrupt>false</waitForInterrupt>
|
||||
<waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
|
||||
<healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}login</healthCheckUrl>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
@ -15,7 +15,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
||||
assignment2();
|
||||
assignment3();
|
||||
|
||||
checkResults("/access-control");
|
||||
checkResults("MissingFunctionAC");
|
||||
}
|
||||
|
||||
private void assignment3() {
|
||||
|
@ -86,7 +86,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
// logout();
|
||||
login(); // because old cookie got replaced and invalidated
|
||||
startLesson("CSRF", false);
|
||||
checkResults("/csrf");
|
||||
checkResults("CSRF");
|
||||
}
|
||||
|
||||
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
||||
@ -103,7 +103,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.multiPart("file", htmlName, htmlContent.getBytes())
|
||||
.post(webWolfUrl("fileupload"))
|
||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -118,7 +118,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("files/" + this.getUser() + "/" + htmlName))
|
||||
.get(new WebWolfUrlBuilder().path("files/%s/%s", this.getUser(), htmlName).build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -136,7 +136,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("files/fake.html"))
|
||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||
.post(goatURL)
|
||||
.then()
|
||||
.extract()
|
||||
@ -163,7 +163,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("files/fake.html"))
|
||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||
.formParams(params)
|
||||
.post(goatURL)
|
||||
.then()
|
||||
@ -184,7 +184,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("files/fake.html"))
|
||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||
.contentType(ContentType.TEXT)
|
||||
.body(
|
||||
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
|
||||
@ -217,7 +217,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.header("Referer", webWolfUrl("files/fake.html"))
|
||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||
.params(params)
|
||||
.post(goatURL)
|
||||
.then()
|
||||
@ -254,15 +254,15 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
||||
RestAssured.given()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.relaxedHTTPSValidation()
|
||||
.get(url("service/lessonoverview.mvc"))
|
||||
.get(url("service/lessonoverview.mvc/CSRF"))
|
||||
.then()
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.getObject("$", Overview[].class);
|
||||
// assertThat(assignments)
|
||||
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||
// .extracting(o -> o.solved)
|
||||
// .containsExactly(true);
|
||||
assertThat(assignments)
|
||||
.filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||
.extracting(o -> o.solved)
|
||||
.containsExactly(true);
|
||||
}
|
||||
|
||||
@Data
|
||||
|
@ -50,9 +50,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||
params.clear();
|
||||
params.put("flag", flag);
|
||||
checkAssignment(url("challenge/flag"), params, true);
|
||||
checkAssignment(url("challenge/flag/1"), params, true);
|
||||
|
||||
checkResults("/challenge/1");
|
||||
checkResults("Challenge1");
|
||||
|
||||
List<String> capturefFlags =
|
||||
RestAssured.given()
|
||||
@ -92,9 +92,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||
params.clear();
|
||||
params.put("flag", flag);
|
||||
checkAssignment(url("challenge/flag"), params, true);
|
||||
checkAssignment(url("challenge/flag/5"), params, true);
|
||||
|
||||
checkResults("/challenge/5");
|
||||
checkResults("Challenge5");
|
||||
|
||||
List<String> capturefFlags =
|
||||
RestAssured.given()
|
||||
@ -126,7 +126,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.extract()
|
||||
.asString();
|
||||
|
||||
// Should send an email to WebWolf inbox this should give a hint to the link being static
|
||||
// Should email WebWolf inbox this should give a hint to the link being static
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
@ -144,7 +144,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("mail"))
|
||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -165,6 +165,6 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
||||
.asString();
|
||||
|
||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||
checkAssignment(url("challenge/flag"), Map.of("flag", flag), true);
|
||||
checkAssignment(url("challenge/flag/7"), Map.of("flag", flag), true);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
||||
|
||||
checkAssignmentDefaults();
|
||||
|
||||
checkResults("/crypto");
|
||||
checkResults("Cryptography");
|
||||
}
|
||||
|
||||
private void checkAssignment2() {
|
||||
|
@ -28,6 +28,6 @@ public class DeserializationIntegrationTest extends IntegrationTest {
|
||||
}
|
||||
checkAssignment(url("InsecureDeserialization/task"), params, true);
|
||||
|
||||
checkResults("/InsecureDeserialization/");
|
||||
checkResults("InsecureDeserialization");
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,17 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("magic_num", "33");
|
||||
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(url("HttpBasics/attack1"), params, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -51,7 +61,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
|
||||
checkResults("/HttpProxies/");
|
||||
checkResults("HttpProxies");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -73,7 +83,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
"question_3_solution",
|
||||
"Solution 2: The systems security is compromised even if only one goal is harmed.");
|
||||
checkAssignment(url("cia/quiz"), params, true);
|
||||
checkResults("/cia/");
|
||||
checkResults("CIA");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -96,7 +106,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
params.put("payload", solution);
|
||||
checkAssignment(url("VulnerableComponents/attack1"), params, true);
|
||||
checkResults("/VulnerableComponents/");
|
||||
checkResults("VulnerableComponents");
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +118,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("username", "CaptainJack");
|
||||
params.put("password", "BlackPearl");
|
||||
checkAssignment(url("InsecureLogin/task"), params, true);
|
||||
checkResults("/InsecureLogin/");
|
||||
checkResults("InsecureLogin");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -118,7 +128,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.clear();
|
||||
params.put("password", "ajnaeliclm^&&@kjn.");
|
||||
checkAssignment(url("SecurePasswords/assignment"), params, true);
|
||||
checkResults("SecurePasswords/");
|
||||
checkResults("SecurePasswords");
|
||||
|
||||
startLesson("AuthBypass");
|
||||
params.clear();
|
||||
@ -128,7 +138,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("verifyMethod", "SEC_QUESTIONS");
|
||||
params.put("userId", "12309746");
|
||||
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
||||
checkResults("/auth-bypass/");
|
||||
checkResults("AuthBypass");
|
||||
|
||||
startLesson("HttpProxies");
|
||||
MatcherAssert.assertThat(
|
||||
@ -144,7 +154,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
.extract()
|
||||
.path("lessonCompleted"),
|
||||
CoreMatchers.is(true));
|
||||
checkResults("/HttpProxies/");
|
||||
checkResults("HttpProxies");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -180,7 +190,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("network_num", "24");
|
||||
checkAssignment(url("ChromeDevTools/network"), params, true);
|
||||
|
||||
checkResults("/ChromeDevTools/");
|
||||
checkResults("ChromeDevTools");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -194,7 +204,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("verifyMethod", "SEC_QUESTIONS");
|
||||
params.put("userId", "12309746");
|
||||
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
||||
checkResults("/auth-bypass/");
|
||||
checkResults("AuthBypass");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -205,6 +215,6 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("param1", "secr37Value");
|
||||
params.put("param2", "Main");
|
||||
checkAssignment(url("lesson-template/sample-attack"), params, true);
|
||||
checkResults("/lesson-template/");
|
||||
checkResults("LessonTemplate");
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
||||
|
||||
@AfterEach
|
||||
public void shutdown() {
|
||||
checkResults("/IDOR");
|
||||
checkResults("IDOR");
|
||||
}
|
||||
|
||||
private void loginIDOR() {
|
||||
|
@ -3,6 +3,7 @@ package org.owasp.webgoat;
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.filter.log.LogDetail;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
@ -15,43 +16,80 @@ import org.springframework.http.HttpStatus;
|
||||
public abstract class IntegrationTest {
|
||||
|
||||
private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
|
||||
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 webGoatContext =
|
||||
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
|
||||
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 webWolfCookie;
|
||||
@Getter private final String user = "webgoat";
|
||||
|
||||
protected String url(String url) {
|
||||
return webgoatUrl + url;
|
||||
return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url);
|
||||
}
|
||||
|
||||
protected String webWolfUrl(String url) {
|
||||
return webWolfUrl + url;
|
||||
protected class WebWolfUrlBuilder {
|
||||
|
||||
private boolean attackMode = false;
|
||||
private String path = null;
|
||||
|
||||
protected String build() {
|
||||
return "http://localhost:%s%s%s"
|
||||
.formatted(webWolfPort, webWolfContext, path != null ? path : "");
|
||||
}
|
||||
|
||||
protected String webWolfFileUrl(String fileName) {
|
||||
return webWolfUrl("files") + "/" + getUser() + "/" + fileName;
|
||||
/**
|
||||
* In attack mode it means WebGoat calls WebWolf to perform an attack. In this case we need to
|
||||
* use port 9090 in a Docker environment.
|
||||
*/
|
||||
protected WebWolfUrlBuilder attackMode() {
|
||||
attackMode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected WebWolfUrlBuilder path(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected WebWolfUrlBuilder path(String path, String... uriVariables) {
|
||||
this.path = path.formatted(uriVariables);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugging options: install TestContainers Desktop and map port 5005 to the host machine with
|
||||
* https://newsletter.testcontainers.com/announcements/set-fixed-ports-to-easily-debug-development-services
|
||||
*
|
||||
* <p>Start the test and connect a remote debugger in IntelliJ to localhost:5005 and attach it.
|
||||
*/
|
||||
// private static GenericContainer<?> webGoatContainer =
|
||||
// new GenericContainer(new ImageFromDockerfile("webgoat").withFileFromPath("/",
|
||||
// Paths.get(".")))
|
||||
// .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("webgoat")))
|
||||
// .withExposedPorts(8080, 9090, 5005)
|
||||
// .withEnv(
|
||||
// "_JAVA_OPTIONS",
|
||||
// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005")
|
||||
// .waitingFor(Wait.forHealthcheck());
|
||||
//
|
||||
// static {
|
||||
// webGoatContainer.start();
|
||||
// }
|
||||
|
||||
@BeforeEach
|
||||
public void login() {
|
||||
login("webgoat");
|
||||
}
|
||||
|
||||
protected void login(String user) {
|
||||
String location =
|
||||
given()
|
||||
.when()
|
||||
@ -60,6 +98,8 @@ public abstract class IntegrationTest {
|
||||
.formParam("password", "password")
|
||||
.post(url("login"))
|
||||
.then()
|
||||
.log()
|
||||
.ifValidationFails(LogDetail.ALL) // Log the response details if validation fails
|
||||
.cookie("JSESSIONID")
|
||||
.statusCode(302)
|
||||
.extract()
|
||||
@ -100,7 +140,7 @@ public abstract class IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.formParam("username", user)
|
||||
.formParam("password", "password")
|
||||
.post(webWolfUrl("login"))
|
||||
.post(new WebWolfUrlBuilder().path("login").build())
|
||||
.then()
|
||||
.statusCode(302)
|
||||
.cookie("WEBWOLFSESSION")
|
||||
@ -131,7 +171,7 @@ public abstract class IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/restartlesson.mvc"))
|
||||
.get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName)))
|
||||
.then()
|
||||
.statusCode(200);
|
||||
}
|
||||
@ -167,23 +207,18 @@ public abstract class IntegrationTest {
|
||||
CoreMatchers.is(expectedResult));
|
||||
}
|
||||
|
||||
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
|
||||
// only required to be in the same package)
|
||||
public void checkResults(String prefix) {
|
||||
checkResults();
|
||||
|
||||
MatcherAssert.assertThat(
|
||||
public void checkResults(String lesson) {
|
||||
var result =
|
||||
RestAssured.given()
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.get(url("service/lessonoverview.mvc"))
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.extract()
|
||||
.jsonPath()
|
||||
.getList("assignment.path"),
|
||||
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||
.get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson)))
|
||||
.andReturn();
|
||||
|
||||
MatcherAssert.assertThat(
|
||||
result.then().statusCode(200).extract().jsonPath().getList("solved"),
|
||||
CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||
}
|
||||
|
||||
public void checkResults() {
|
||||
@ -238,7 +273,7 @@ public abstract class IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("file-server-location"))
|
||||
.get(new WebWolfUrlBuilder().path("file-server-location").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -266,7 +301,7 @@ public abstract class IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.delete(webWolfUrl("mail"))
|
||||
.delete(new WebWolfUrlBuilder().path("mail").build())
|
||||
.then()
|
||||
.statusCode(HttpStatus.ACCEPTED.value());
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import io.jsonwebtoken.impl.TextCodec;
|
||||
import io.restassured.RestAssured;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@ -34,7 +33,7 @@ import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
|
||||
public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
||||
public void solveAssignment() throws IOException, NoSuchAlgorithmException {
|
||||
startLesson("JWT");
|
||||
|
||||
decodingToken();
|
||||
@ -51,11 +50,10 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
quiz();
|
||||
|
||||
checkResults("/JWT/");
|
||||
checkResults("JWT");
|
||||
}
|
||||
|
||||
private String generateToken(String key) {
|
||||
|
||||
return Jwts.builder()
|
||||
.setIssuer("WebGoat Token Builder")
|
||||
.setAudience("webgoat.org")
|
||||
@ -96,7 +94,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
CoreMatchers.is(true));
|
||||
}
|
||||
|
||||
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
private void findPassword() {
|
||||
|
||||
String accessToken =
|
||||
RestAssured.given()
|
||||
@ -256,7 +254,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.multiPart("file", "jwks.json", jwks.toJson().getBytes())
|
||||
.post(webWolfUrl("fileupload"))
|
||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -265,7 +263,10 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||
|
||||
Map<String, Object> header = new HashMap();
|
||||
header.put(Header.TYPE, Header.JWT_TYPE);
|
||||
header.put(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json"));
|
||||
header.put(
|
||||
JwsHeader.JWK_SET_URL,
|
||||
new WebWolfUrlBuilder().attackMode().path("files/%s/jwks.json", getUser()).build());
|
||||
|
||||
String token =
|
||||
Jwts.builder()
|
||||
.setHeader(header)
|
||||
|
@ -151,7 +151,6 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||
checkLang(propsDefault, "nl");
|
||||
checkLang(propsDefault, "de");
|
||||
checkLang(propsDefault, "fr");
|
||||
checkLang(propsDefault, "ru");
|
||||
}
|
||||
|
||||
private Properties getProperties(String lang) {
|
||||
|
@ -85,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("mail"))
|
||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -99,7 +99,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
public void shutdown() {
|
||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||
// lesson is marked complete
|
||||
checkResults("/PasswordReset");
|
||||
checkResults("PasswordReset");
|
||||
}
|
||||
|
||||
private void changePassword(String link) {
|
||||
@ -119,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("requests"))
|
||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
|
@ -147,6 +147,6 @@ class PathTraversalIT extends IntegrationTest {
|
||||
void shutdown() {
|
||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||
// lesson is marked complete
|
||||
checkResults("/PathTraversal");
|
||||
checkResults("PathTraversal");
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("JSESSIONID", getWebGoatCookie())
|
||||
.formParams(Map.of("flag", "test"))
|
||||
.post(url("challenge/flag"));
|
||||
.post(url("challenge/flag/1"));
|
||||
};
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS);
|
||||
List<? extends Callable<Response>> flagCalls =
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.owasp.webgoat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -8,7 +7,7 @@ import org.junit.jupiter.api.Test;
|
||||
public class SSRFIntegrationTest extends IntegrationTest {
|
||||
|
||||
@Test
|
||||
public void runTests() throws IOException {
|
||||
public void runTests() {
|
||||
startLesson("SSRF");
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
@ -21,6 +20,6 @@ public class SSRFIntegrationTest extends IntegrationTest {
|
||||
|
||||
checkAssignment(url("SSRF/task2"), params, true);
|
||||
|
||||
checkResults("/SSRF/");
|
||||
checkResults("SSRF");
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,6 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
||||
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
||||
checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true);
|
||||
|
||||
checkResults("/SqlInjectionAdvanced/");
|
||||
checkResults("SqlInjectionAdvanced");
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,6 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
||||
params.put("action_string", sql_13);
|
||||
checkAssignment(url("SqlInjection/attack10"), params, true);
|
||||
|
||||
checkResults("/SqlInjection/");
|
||||
checkResults("SqlInjection");
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,6 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
||||
params.put("ip", "104.130.219.202");
|
||||
checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true);
|
||||
|
||||
checkResults();
|
||||
checkResults("SqlInjectionMitigations");
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("mail"))
|
||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -53,7 +53,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.queryParams(params)
|
||||
.get(webWolfUrl("landing"))
|
||||
.get(new WebWolfUrlBuilder().path("landing").build())
|
||||
.then()
|
||||
.statusCode(200);
|
||||
responseBody =
|
||||
@ -61,7 +61,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("requests"))
|
||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -72,6 +72,6 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
||||
params.put("uniqueCode", uniqueCode);
|
||||
checkAssignment(url("WebWolf/landing"), params, true);
|
||||
|
||||
checkResults("/WebWolf");
|
||||
checkResults("WebWolfIntroduction");
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,6 @@ public class XSSIntegrationTest extends IntegrationTest {
|
||||
+ "MyCommentDAO.addComment(threadID, userID).getCleanHTML());");
|
||||
checkAssignment(url("CrossSiteScripting/attack4"), params, true);
|
||||
|
||||
checkResults("/CrossSiteScripting");
|
||||
checkResults("CrossSiteScripting");
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,6 @@ 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 {
|
||||
@ -28,47 +25,40 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
""";
|
||||
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
* @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")));
|
||||
}
|
||||
private String getSecret() {
|
||||
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
||||
String dtd7String =
|
||||
dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile);
|
||||
String webWolfCallback = new WebWolfUrlBuilder().path("landing").attackMode().build();
|
||||
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile);
|
||||
|
||||
// upload DTD
|
||||
RestAssured.given()
|
||||
@ -76,15 +66,17 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
||||
.post(webWolfUrl("fileupload"))
|
||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
.getBody()
|
||||
.asString();
|
||||
|
||||
// upload attack
|
||||
String xxe7String =
|
||||
xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser());
|
||||
xxe7.replace("WEBWOLFURL", new WebWolfUrlBuilder().attackMode().path("files").build())
|
||||
.replace("USERNAME", this.getUser());
|
||||
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
|
||||
|
||||
// read results from WebWolf
|
||||
@ -93,7 +85,7 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
.when()
|
||||
.relaxedHTTPSValidation()
|
||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||
.get(webWolfUrl("requests"))
|
||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||
.then()
|
||||
.extract()
|
||||
.response()
|
||||
@ -113,7 +105,6 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
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(
|
||||
@ -121,6 +112,6 @@ public class XXEIntegrationTest extends IntegrationTest {
|
||||
ContentType.XML,
|
||||
"<comment><text>" + getSecret() + "</text></comment>",
|
||||
true);
|
||||
checkResults("xxe/");
|
||||
checkResults("XXE");
|
||||
}
|
||||
}
|
||||
|
@ -32,22 +32,23 @@ package org.owasp.webgoat.container;
|
||||
|
||||
import static org.asciidoctor.Asciidoctor.Factory.create;
|
||||
|
||||
import io.undertow.util.Headers;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.asciidoctor.Asciidoctor;
|
||||
import org.asciidoctor.Attributes;
|
||||
import org.asciidoctor.Options;
|
||||
import org.asciidoctor.extension.JavaExtensionRegistry;
|
||||
import org.owasp.webgoat.container.asciidoc.*;
|
||||
import org.owasp.webgoat.container.i18n.Language;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||
@ -135,17 +136,17 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
||||
return computedResourceName;
|
||||
}
|
||||
|
||||
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);
|
||||
private Options createAttributes() {
|
||||
|
||||
Map<String, Object> options = new HashMap<>();
|
||||
options.put("attributes", attributes);
|
||||
|
||||
return options;
|
||||
return Options.builder()
|
||||
.attributes(
|
||||
Attributes.builder()
|
||||
.attribute("source-highlighter", "coderay")
|
||||
.attribute("backend", "xhtml")
|
||||
.attribute("lang", determineLanguage())
|
||||
.attribute("icons", org.asciidoctor.Attributes.FONT_ICONS)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private String determineLanguage() {
|
||||
@ -159,7 +160,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
||||
log.debug("browser locale {}", browserLocale);
|
||||
return browserLocale.getLanguage();
|
||||
} else {
|
||||
String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING);
|
||||
String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
|
||||
if (null != langHeader) {
|
||||
log.debug("browser locale {}", langHeader);
|
||||
return langHeader.substring(0, 2);
|
||||
|
14
src/main/java/org/owasp/webgoat/container/CurrentUser.java
Normal file
14
src/main/java/org/owasp/webgoat/container/CurrentUser.java
Normal file
@ -0,0 +1,14 @@
|
||||
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 {}
|
@ -0,0 +1,14 @@
|
||||
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 {}
|
@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.owasp.webgoat.container.service.RestartLessonService;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -34,8 +35,8 @@ public class DatabaseConfiguration {
|
||||
/**
|
||||
* 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
|
||||
* quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson
|
||||
* related tables.
|
||||
* quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we
|
||||
* clean the lesson related tables.
|
||||
*/
|
||||
@Bean(initMethod = "migrate")
|
||||
public Flyway flyWayContainer() {
|
||||
@ -60,7 +61,7 @@ public class DatabaseConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LessonDataSource lessonDataSource() {
|
||||
return new LessonDataSource(dataSource());
|
||||
public LessonDataSource lessonDataSource(DataSource dataSource) {
|
||||
return new LessonDataSource(dataSource);
|
||||
}
|
||||
}
|
||||
|
@ -32,30 +32,33 @@
|
||||
package org.owasp.webgoat.container;
|
||||
|
||||
import java.io.File;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
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.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
|
||||
@PropertySource("classpath:application-webgoat.properties")
|
||||
@EnableAutoConfiguration
|
||||
@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"})
|
||||
@EntityScan(basePackages = "org.owasp.webgoat.container")
|
||||
public class WebGoat {
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public WebGoat(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Bean(name = "pluginTargetDirectory")
|
||||
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
||||
@ -64,21 +67,8 @@ public class WebGoat {
|
||||
|
||||
@Bean
|
||||
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
public WebSession webSession() {
|
||||
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");
|
||||
public LessonSession userSessionData() {
|
||||
return new LessonSession();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -35,6 +35,7 @@ import org.owasp.webgoat.container.users.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
@ -57,7 +58,6 @@ public class WebSecurityConfig {
|
||||
return http.authorizeHttpRequests(
|
||||
auth ->
|
||||
auth.requestMatchers(
|
||||
"/",
|
||||
"/favicon.ico",
|
||||
"/css/**",
|
||||
"/images/**",
|
||||
@ -65,7 +65,8 @@ public class WebSecurityConfig {
|
||||
"fonts/**",
|
||||
"/plugins/**",
|
||||
"/registration",
|
||||
"/register.mvc")
|
||||
"/register.mvc",
|
||||
"/actuator/**")
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
@ -97,6 +98,7 @@ public class WebSecurityConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public UserDetailsService userDetailsServiceBean() {
|
||||
return userDetailsService;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public class EnvironmentExposure implements ApplicationContextAware {
|
||||
private static ApplicationContext context;
|
||||
|
||||
public static Environment getEnv() {
|
||||
return (null != context) ? context.getEnvironment() : null;
|
||||
return null != context ? context.getEnvironment() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.owasp.webgoat.container.asciidoc;
|
||||
|
||||
import java.util.Map;
|
||||
import org.asciidoctor.ast.ContentNode;
|
||||
import org.asciidoctor.ast.PhraseNode;
|
||||
import org.asciidoctor.ast.StructuralNode;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
|
||||
public class OperatingSystemMacro extends InlineMacroProcessor {
|
||||
@ -15,7 +16,8 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||
public PhraseNode process(
|
||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||
var osName = System.getProperty("os.name");
|
||||
|
||||
// see
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.owasp.webgoat.container.asciidoc;
|
||||
|
||||
import java.util.Map;
|
||||
import org.asciidoctor.ast.ContentNode;
|
||||
import org.asciidoctor.ast.PhraseNode;
|
||||
import org.asciidoctor.ast.StructuralNode;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@ -17,7 +18,8 @@ public class UsernameMacro extends InlineMacroProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||
public PhraseNode process(
|
||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||
var auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
var username = "unknown";
|
||||
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.owasp.webgoat.container.asciidoc;
|
||||
|
||||
import java.util.Map;
|
||||
import org.asciidoctor.ast.ContentNode;
|
||||
import org.asciidoctor.ast.PhraseNode;
|
||||
import org.asciidoctor.ast.StructuralNode;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
|
||||
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
||||
@ -15,11 +16,12 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||
public PhraseNode process(
|
||||
StructuralNode structuralNode, String target, Map<String, Object> attributes) {
|
||||
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
|
||||
|
||||
// see
|
||||
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
|
||||
return createPhraseNode(contentNode, "quoted", env);
|
||||
return createPhraseNode(structuralNode, "quoted", env);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.owasp.webgoat.container.asciidoc;
|
||||
|
||||
import java.util.Map;
|
||||
import org.asciidoctor.ast.ContentNode;
|
||||
import org.asciidoctor.ast.PhraseNode;
|
||||
import org.asciidoctor.ast.StructuralNode;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
|
||||
public class WebGoatVersionMacro extends InlineMacroProcessor {
|
||||
@ -15,7 +16,8 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||
public PhraseNode process(
|
||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
|
||||
|
||||
// see
|
||||
|
@ -2,7 +2,8 @@ package org.owasp.webgoat.container.asciidoc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.asciidoctor.ast.ContentNode;
|
||||
import org.asciidoctor.ast.PhraseNode;
|
||||
import org.asciidoctor.ast.StructuralNode;
|
||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||
|
||||
/**
|
||||
@ -21,7 +22,8 @@ public class WebWolfMacro extends InlineMacroProcessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) {
|
||||
public PhraseNode process(
|
||||
StructuralNode contentNode, String linkText, Map<String, Object> attributes) {
|
||||
var env = EnvironmentExposure.getEnv();
|
||||
var hostname = env.getProperty("webwolf.url");
|
||||
var target = (String) attributes.getOrDefault("target", "home");
|
||||
@ -36,7 +38,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
|
||||
options.put("type", ":link");
|
||||
options.put("target", href);
|
||||
attributes.put("window", "_blank");
|
||||
return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert();
|
||||
return createPhraseNode(contentNode, "anchor", linkText, attributes, options);
|
||||
}
|
||||
|
||||
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
|
||||
|
@ -25,27 +25,13 @@
|
||||
|
||||
package org.owasp.webgoat.container.assignments;
|
||||
|
||||
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 {
|
||||
public abstract class AssignmentEndpoint {
|
||||
|
||||
@Autowired private WebSession webSession;
|
||||
@Autowired private UserSessionData userSessionData;
|
||||
@Getter @Autowired private PluginMessages messages;
|
||||
|
||||
protected WebSession getWebSession() {
|
||||
return webSession;
|
||||
}
|
||||
|
||||
protected UserSessionData getUserSessionData() {
|
||||
return userSessionData;
|
||||
}
|
||||
// TODO: move this to different bean.
|
||||
@Autowired private PluginMessages messages;
|
||||
|
||||
/**
|
||||
* Convenience method for create a successful result:
|
||||
@ -86,7 +72,4 @@ public abstract class AssignmentEndpoint implements Initializeable {
|
||||
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
||||
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(WebGoatUser user) {}
|
||||
}
|
||||
|
@ -22,27 +22,30 @@
|
||||
|
||||
package org.owasp.webgoat.container.assignments;
|
||||
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.users.UserProgress;
|
||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
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.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
||||
|
||||
private UserTrackerRepository userTrackerRepository;
|
||||
private WebSession webSession;
|
||||
private final Course course;
|
||||
private final UserProgressRepository userProgressRepository;
|
||||
|
||||
public LessonTrackerInterceptor(
|
||||
UserTrackerRepository userTrackerRepository, WebSession webSession) {
|
||||
this.userTrackerRepository = userTrackerRepository;
|
||||
this.webSession = webSession;
|
||||
public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) {
|
||||
this.course = course;
|
||||
this.userProgressRepository = userProgressRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,18 +68,30 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
||||
return o;
|
||||
}
|
||||
|
||||
protected AttackResult trackProgress(AttackResult attackResult) {
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
if (userTracker == null) {
|
||||
userTracker = new UserTracker(webSession.getUserName());
|
||||
}
|
||||
if (attackResult.assignmentSolved()) {
|
||||
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
|
||||
} else {
|
||||
userTracker.assignmentFailed(webSession.getCurrentLesson());
|
||||
}
|
||||
userTrackerRepository.save(userTracker);
|
||||
private void trackProgress(AttackResult attackResult) {
|
||||
var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
Assert.notNull(user, "User not found in SecurityContext");
|
||||
var username = realUsername(user);
|
||||
|
||||
return attackResult;
|
||||
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()) {
|
||||
userProgress.assignmentSolved(lesson, attackResult.getAssignment());
|
||||
} else {
|
||||
userProgress.assignmentFailed(lesson);
|
||||
}
|
||||
userProgressRepository.save(userProgress);
|
||||
}
|
||||
|
||||
private String realUsername(WebGoatUser user) {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
@ -33,42 +33,20 @@ package org.owasp.webgoat.container.controller;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Controller
|
||||
public class StartLesson {
|
||||
|
||||
private final WebSession ws;
|
||||
private final Course course;
|
||||
|
||||
public StartLesson(WebSession ws, Course course) {
|
||||
this.ws = ws;
|
||||
public StartLesson(Course course) {
|
||||
this.course = course;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
@GetMapping(
|
||||
value = {"*.lesson"},
|
||||
produces = "text/html")
|
||||
public ModelAndView lessonPage(HttpServletRequest request) {
|
||||
@ -81,8 +59,7 @@ public class StartLesson {
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
lesson -> {
|
||||
ws.setCurrentLesson(lesson);
|
||||
model.addObject("lesson", lesson);
|
||||
request.setAttribute("lesson", lesson);
|
||||
});
|
||||
|
||||
return model;
|
||||
|
@ -51,10 +51,11 @@ public class Assignment {
|
||||
|
||||
private String name;
|
||||
private String path;
|
||||
private boolean solved = false;
|
||||
|
||||
@Transient private List<String> hints;
|
||||
|
||||
private Assignment() {
|
||||
protected Assignment() {
|
||||
// Hibernate
|
||||
}
|
||||
|
||||
@ -74,4 +75,8 @@ public class Assignment {
|
||||
this.path = path;
|
||||
this.hints = hints;
|
||||
}
|
||||
|
||||
public void solved() {
|
||||
this.solved = true;
|
||||
}
|
||||
}
|
||||
|
@ -34,30 +34,28 @@ import lombok.Getter;
|
||||
* @since October 28, 2003
|
||||
*/
|
||||
public enum Category {
|
||||
INTRODUCTION("Introduction", 5),
|
||||
GENERAL("General", 100),
|
||||
INTRODUCTION("Introduction"),
|
||||
GENERAL("General"),
|
||||
|
||||
A1("(A1) Broken Access Control", 301),
|
||||
A2("(A2) Cryptographic Failures", 302),
|
||||
A3("(A3) Injection", 303),
|
||||
A1("(A1) Broken Access Control"),
|
||||
A2("(A2) Cryptographic Failures"),
|
||||
A3("(A3) Injection"),
|
||||
|
||||
A5("(A5) Security Misconfiguration", 305),
|
||||
A6("(A6) Vuln & Outdated Components", 306),
|
||||
A7("(A7) Identity & Auth Failure", 307),
|
||||
A8("(A8) Software & Data Integrity", 308),
|
||||
A9("(A9) Security Logging Failures", 309),
|
||||
A10("(A10) Server-side Request Forgery", 310),
|
||||
A5("(A5) Security Misconfiguration"),
|
||||
A6("(A6) Vuln & Outdated Components"),
|
||||
A7("(A7) Identity & Auth Failure"),
|
||||
A8("(A8) Software & Data Integrity"),
|
||||
A9("(A9) Security Logging Failures"),
|
||||
A10("(A10) Server-side Request Forgery"),
|
||||
|
||||
CLIENT_SIDE("Client side", 1700),
|
||||
CLIENT_SIDE("Client side"),
|
||||
|
||||
CHALLENGE("Challenges", 3000);
|
||||
CHALLENGE("Challenges");
|
||||
|
||||
@Getter private String name;
|
||||
@Getter private Integer ranking;
|
||||
|
||||
Category(String name, Integer ranking) {
|
||||
Category(String name) {
|
||||
this.name = name;
|
||||
this.ranking = ranking;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,12 +22,9 @@
|
||||
|
||||
package org.owasp.webgoat.container.lessons;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
@ -35,45 +32,91 @@ import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class CourseConfiguration {
|
||||
|
||||
private final List<Lesson> lessons;
|
||||
private final List<AssignmentEndpoint> assignments;
|
||||
private final Map<String, List<AssignmentEndpoint>> assignmentsByPackage;
|
||||
|
||||
public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
|
||||
this.lessons = lessons;
|
||||
this.assignments = assignments;
|
||||
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
|
||||
public Course course() {
|
||||
lessons.stream().forEach(l -> l.setAssignments(createAssignment(l)));
|
||||
assignments.stream().forEach(this::attachToLesson);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
private List<Assignment> createAssignment(Lesson lesson) {
|
||||
var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName());
|
||||
if (CollectionUtils.isEmpty(endpoints)) {
|
||||
log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle());
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return endpoints.stream()
|
||||
.map(
|
||||
e ->
|
||||
new Assignment(
|
||||
e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass())))
|
||||
.toList();
|
||||
private List<String> findDiff() {
|
||||
var matchedToLessons =
|
||||
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList();
|
||||
var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList();
|
||||
|
||||
var diff = new ArrayList<>(allAssignments);
|
||||
diff.removeAll(matchedToLessons);
|
||||
return diff;
|
||||
}
|
||||
|
||||
private String getPath(Class<? extends AssignmentEndpoint> e) {
|
||||
|
@ -6,7 +6,7 @@ import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
* Interface for initialization of a lesson. It is called when a new user is added to WebGoat and
|
||||
* when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson.
|
||||
*/
|
||||
public interface Initializeable {
|
||||
public interface Initializable {
|
||||
|
||||
void initialize(WebGoatUser webGoatUser);
|
||||
default void initialize(WebGoatUser webGoatUser) {}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
|
||||
package org.owasp.webgoat.container.lessons;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@ -30,13 +31,10 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public abstract class Lesson {
|
||||
|
||||
private static int count = 1;
|
||||
private Integer id = null;
|
||||
private List<Assignment> assignments;
|
||||
private List<Assignment> assignments = new ArrayList<>();
|
||||
|
||||
/** Constructor for the Lesson object */
|
||||
protected Lesson() {
|
||||
id = ++count;
|
||||
public void addAssignment(Assignment assignment) {
|
||||
this.assignments.add(assignment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,9 +42,9 @@ public abstract class Lesson {
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String getName() {
|
||||
public LessonName getName() {
|
||||
String className = getClass().getName();
|
||||
return className.substring(className.lastIndexOf('.') + 1);
|
||||
return new LessonName(className.substring(className.lastIndexOf('.') + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,6 +114,10 @@ public abstract class Lesson {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used in Thymeleaf to construct the HTML to load the lesson content from. See
|
||||
* lesson_content.html
|
||||
*/
|
||||
public final String getPackage() {
|
||||
var packageName = this.getClass().getPackageName();
|
||||
// package name is the direct package name below lessons (any subpackage will be removed)
|
||||
|
@ -0,0 +1,21 @@
|
||||
package org.owasp.webgoat.container.lessons;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wrapper class for the name of a lesson. This class is used to ensure that the lesson name is not
|
||||
* null and does not contain the ".lesson" suffix. The front-end passes the lesson name as a string
|
||||
* to the back-end, which then creates a new LessonName object with the lesson name as a parameter.
|
||||
* The constructor of the LessonName class checks if the lesson name is null and removes the
|
||||
* ".lesson" suffix if it is present.
|
||||
*
|
||||
* @param lessonName
|
||||
*/
|
||||
public record LessonName(String lessonName) {
|
||||
public LessonName {
|
||||
Assert.notNull(lessonName, "Lesson name cannot be null");
|
||||
if (lessonName.contains(".lesson")) {
|
||||
lessonName = lessonName.substring(0, lessonName.indexOf(".lesson"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package org.owasp.webgoat.container.report;
|
||||
|
||||
record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
|
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* *************************************************************************************************
|
||||
*
|
||||
* <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.report;
|
||||
|
||||
import java.util.List;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ReportCardController {
|
||||
|
||||
private final UserProgressRepository userProgressRepository;
|
||||
private final Course course;
|
||||
private final PluginMessages pluginMessages;
|
||||
|
||||
public ReportCardController(
|
||||
UserProgressRepository userProgressRepository, Course course, PluginMessages pluginMessages) {
|
||||
this.userProgressRepository = userProgressRepository;
|
||||
this.course = course;
|
||||
this.pluginMessages = pluginMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint which generates the report card for the current use to show the stats on the solved
|
||||
* lessons
|
||||
*/
|
||||
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
|
||||
@ResponseBody
|
||||
public ReportCard reportCard(@CurrentUsername String username) {
|
||||
var userProgress = userProgressRepository.findByUser(username);
|
||||
var lessonStatistics =
|
||||
course.getLessons().stream()
|
||||
.map(
|
||||
lesson -> {
|
||||
var lessonTracker = userProgress.getLessonProgress(lesson);
|
||||
return new LessonStatistics(
|
||||
pluginMessages.getMessage(lesson.getTitle()),
|
||||
lessonTracker.isLessonSolved(),
|
||||
lessonTracker.getNumberOfAttempts());
|
||||
})
|
||||
.toList();
|
||||
return new ReportCard(
|
||||
course.getTotalOfLessons(),
|
||||
course.getTotalOfAssignments(),
|
||||
userProgress.numberOfAssignmentsSolved(),
|
||||
userProgress.numberOfLessonsSolved(),
|
||||
lessonStatistics);
|
||||
}
|
||||
|
||||
private record ReportCard(
|
||||
int totalNumberOfLessons,
|
||||
int totalNumberOfAssignments,
|
||||
long numberOfAssignmentsSolved,
|
||||
long numberOfLessonsSolved,
|
||||
List<LessonStatistics> lessonStatistics) {}
|
||||
|
||||
private record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
|
||||
}
|
@ -10,26 +10,24 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.owasp.webgoat.container.lessons.Assignment;
|
||||
import org.owasp.webgoat.container.lessons.Hint;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* HintService class.
|
||||
*
|
||||
* @author rlawson
|
||||
* @version $Id: $Id
|
||||
*/
|
||||
@RestController
|
||||
public class HintService {
|
||||
|
||||
public static final String URL_HINTS_MVC = "/service/hint.mvc";
|
||||
private final WebSession webSession;
|
||||
private final List<Hint> allHints;
|
||||
|
||||
public HintService(WebSession webSession) {
|
||||
this.webSession = webSession;
|
||||
public HintService(Course course) {
|
||||
this.allHints =
|
||||
course.getLessons().stream()
|
||||
.flatMap(lesson -> lesson.getAssignments().stream())
|
||||
.map(this::createHint)
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,15 +38,7 @@ public class HintService {
|
||||
@GetMapping(path = URL_HINTS_MVC, produces = "application/json")
|
||||
@ResponseBody
|
||||
public List<Hint> getHints() {
|
||||
Lesson l = webSession.getCurrentLesson();
|
||||
return createAssignmentHints(l);
|
||||
}
|
||||
|
||||
private List<Hint> createAssignmentHints(Lesson l) {
|
||||
if (l != null) {
|
||||
return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList();
|
||||
}
|
||||
return List.of();
|
||||
return allHints;
|
||||
}
|
||||
|
||||
private List<Hint> createHint(Assignment a) {
|
||||
|
@ -1,33 +1,24 @@
|
||||
package org.owasp.webgoat.container.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.owasp.webgoat.container.lessons.LessonInfoModel;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.owasp.webgoat.container.lessons.LessonName;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* LessonInfoService class.
|
||||
*
|
||||
* @author dm
|
||||
* @version $Id: $Id
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class LessonInfoService {
|
||||
|
||||
private final WebSession webSession;
|
||||
private final Course course;
|
||||
|
||||
/**
|
||||
* getLessonInfo.
|
||||
*
|
||||
* @return a {@link LessonInfoModel} object.
|
||||
*/
|
||||
@RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json")
|
||||
public @ResponseBody LessonInfoModel getLessonInfo() {
|
||||
Lesson lesson = webSession.getCurrentLesson();
|
||||
@GetMapping(path = "/service/lessoninfo.mvc/{lesson}")
|
||||
public @ResponseBody LessonInfoModel getLessonInfo(
|
||||
@PathVariable("lesson") LessonName lessonName) {
|
||||
var lesson = course.getLessonByName(lessonName);
|
||||
return new LessonInfoModel(lesson.getTitle(), false, false, false);
|
||||
}
|
||||
}
|
||||
|
@ -32,16 +32,16 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.lessons.Assignment;
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.lessons.LessonMenuItem;
|
||||
import org.owasp.webgoat.container.lessons.LessonMenuItemType;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.LessonTracker;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.owasp.webgoat.container.users.LessonProgress;
|
||||
import org.owasp.webgoat.container.users.UserProgress;
|
||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -59,8 +59,7 @@ public class LessonMenuService {
|
||||
|
||||
public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc";
|
||||
private final Course course;
|
||||
private final WebSession webSession;
|
||||
private UserTrackerRepository userTrackerRepository;
|
||||
private UserProgressRepository userTrackerRepository;
|
||||
|
||||
@Value("#{'${exclude.categories}'.split(',')}")
|
||||
private List<String> excludeCategories;
|
||||
@ -74,10 +73,13 @@ public class LessonMenuService {
|
||||
* @return a {@link java.util.List} object.
|
||||
*/
|
||||
@RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json")
|
||||
public @ResponseBody List<LessonMenuItem> showLeftNav() {
|
||||
public @ResponseBody List<LessonMenuItem> showLeftNav(@CurrentUsername String username) {
|
||||
// TODO: this looks way too complicated. Either we save it incorrectly or we miss something to
|
||||
// easily find out
|
||||
// if a lesson if solved or not.
|
||||
List<LessonMenuItem> menu = new ArrayList<>();
|
||||
List<Category> categories = course.getCategories();
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
UserProgress userTracker = userTrackerRepository.findByUser(username);
|
||||
|
||||
for (Category category : categories) {
|
||||
if (excludeCategories.contains(category.name())) {
|
||||
@ -97,12 +99,12 @@ public class LessonMenuService {
|
||||
lessonItem.setName(lesson.getTitle());
|
||||
lessonItem.setLink(lesson.getLink());
|
||||
lessonItem.setType(LessonMenuItemType.LESSON);
|
||||
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
|
||||
LessonProgress lessonTracker = userTracker.getLessonProgress(lesson);
|
||||
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
|
||||
lessonItem.setComplete(lessonSolved);
|
||||
categoryItem.addChild(lessonItem);
|
||||
}
|
||||
categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking());
|
||||
categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking));
|
||||
menu.add(categoryItem);
|
||||
}
|
||||
return menu;
|
||||
|
@ -4,11 +4,15 @@ import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.lessons.Assignment;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.owasp.webgoat.container.lessons.LessonName;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
@ -20,8 +24,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@RequiredArgsConstructor
|
||||
public class LessonProgressService {
|
||||
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
private final WebSession webSession;
|
||||
private final UserProgressRepository userProgressRepository;
|
||||
private final Course course;
|
||||
|
||||
/**
|
||||
* Endpoint for fetching the complete lesson overview which informs the user about whether all the
|
||||
@ -29,20 +33,20 @@ public class LessonProgressService {
|
||||
*
|
||||
* @return list of assignments
|
||||
*/
|
||||
@RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json")
|
||||
@GetMapping(value = "/service/lessonoverview.mvc/{lesson}")
|
||||
@ResponseBody
|
||||
public List<LessonOverview> lessonOverview() {
|
||||
var userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
var currentLesson = webSession.getCurrentLesson();
|
||||
public List<LessonOverview> lessonOverview(
|
||||
@PathVariable("lesson") LessonName lessonName, @CurrentUsername String username) {
|
||||
var userProgress = userProgressRepository.findByUser(username);
|
||||
var lesson = course.getLessonByName(lessonName);
|
||||
|
||||
if (currentLesson != null) {
|
||||
var lessonTracker = userTracker.getLessonTracker(currentLesson);
|
||||
return lessonTracker.getLessonOverview().entrySet().stream()
|
||||
Assert.isTrue(lesson != null, "Lesson not found: " + lessonName);
|
||||
|
||||
var lessonProgress = userProgress.getLessonProgress(lesson);
|
||||
return lessonProgress.getLessonOverview().entrySet().stream()
|
||||
.map(entry -> new LessonOverview(entry.getKey(), entry.getValue()))
|
||||
.toList();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
|
@ -1,34 +0,0 @@
|
||||
package org.owasp.webgoat.container.service;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* LessonTitleService class.
|
||||
*
|
||||
* @author dm
|
||||
* @version $Id: $Id
|
||||
*/
|
||||
@Controller
|
||||
public class LessonTitleService {
|
||||
|
||||
private final WebSession webSession;
|
||||
|
||||
public LessonTitleService(final WebSession webSession) {
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for the current attack
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
@RequestMapping(path = "/service/lessontitle.mvc", produces = "application/html")
|
||||
public @ResponseBody String showPlan() {
|
||||
Lesson lesson = webSession.getCurrentLesson();
|
||||
return lesson != null ? lesson.getTitle() : "";
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/**
|
||||
* *************************************************************************************************
|
||||
*
|
||||
* <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.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.LessonTracker;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* ReportCardService
|
||||
*
|
||||
* @author nbaars
|
||||
* @version $Id: $Id
|
||||
*/
|
||||
@Controller
|
||||
@AllArgsConstructor
|
||||
public class ReportCardService {
|
||||
|
||||
private final WebSession webSession;
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
private final Course course;
|
||||
private final PluginMessages pluginMessages;
|
||||
|
||||
/**
|
||||
* Endpoint which generates the report card for the current use to show the stats on the solved
|
||||
* lessons
|
||||
*/
|
||||
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
|
||||
@ResponseBody
|
||||
public ReportCard reportCard() {
|
||||
final ReportCard reportCard = new ReportCard();
|
||||
reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());
|
||||
reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments());
|
||||
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved());
|
||||
reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved());
|
||||
for (Lesson lesson : course.getLessons()) {
|
||||
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
|
||||
final LessonStatistics lessonStatistics = new LessonStatistics();
|
||||
lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));
|
||||
lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());
|
||||
lessonStatistics.setSolved(lessonTracker.isLessonSolved());
|
||||
reportCard.lessonStatistics.add(lessonStatistics);
|
||||
}
|
||||
return reportCard;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private final class ReportCard {
|
||||
|
||||
private int totalNumberOfLessons;
|
||||
private int totalNumberOfAssignments;
|
||||
private int solvedLessons;
|
||||
private int numberOfAssignmentsSolved;
|
||||
private int numberOfLessonsSolved;
|
||||
private List<LessonStatistics> lessonStatistics = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private final class LessonStatistics {
|
||||
private String name;
|
||||
private boolean solved;
|
||||
private int numberOfAttempts;
|
||||
}
|
||||
}
|
@ -29,14 +29,17 @@ import java.util.function.Function;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.owasp.webgoat.container.lessons.Initializeable;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.owasp.webgoat.container.CurrentUser;
|
||||
import org.owasp.webgoat.container.lessons.Initializable;
|
||||
import org.owasp.webgoat.container.lessons.LessonName;
|
||||
import org.owasp.webgoat.container.session.Course;
|
||||
import org.owasp.webgoat.container.users.UserProgress;
|
||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@Controller
|
||||
@ -44,25 +47,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
@Slf4j
|
||||
public class RestartLessonService {
|
||||
|
||||
private final WebSession webSession;
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
private final Course course;
|
||||
private final UserProgressRepository userTrackerRepository;
|
||||
private final Function<String, Flyway> flywayLessons;
|
||||
private final List<Initializeable> lessonsToInitialize;
|
||||
private final List<Initializable> lessonsToInitialize;
|
||||
|
||||
@RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text")
|
||||
@GetMapping(path = "/service/restartlesson.mvc/{lesson}")
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
public void restartLesson() {
|
||||
Lesson al = webSession.getCurrentLesson();
|
||||
log.debug("Restarting lesson: " + al);
|
||||
public void restartLesson(
|
||||
@PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) {
|
||||
var lesson = course.getLessonByName(lessonName);
|
||||
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||
userTracker.reset(al);
|
||||
UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername());
|
||||
userTracker.reset(lesson);
|
||||
userTrackerRepository.save(userTracker);
|
||||
|
||||
var flyway = flywayLessons.apply(webSession.getUserName());
|
||||
var flyway = flywayLessons.apply(user.getUsername());
|
||||
flyway.clean();
|
||||
flyway.migrate();
|
||||
|
||||
lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser()));
|
||||
lessonsToInitialize.forEach(i -> i.initialize(user));
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,9 @@
|
||||
package org.owasp.webgoat.container.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.owasp.webgoat.container.CurrentUser;
|
||||
import org.owasp.webgoat.container.i18n.Messages;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -17,17 +18,17 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@RequiredArgsConstructor
|
||||
public class SessionService {
|
||||
|
||||
private final WebSession webSession;
|
||||
private final RestartLessonService restartLessonService;
|
||||
private final Messages messages;
|
||||
|
||||
@RequestMapping(path = "/service/enable-security.mvc", produces = "application/json")
|
||||
@ResponseBody
|
||||
public String applySecurity() {
|
||||
webSession.toggleSecurity();
|
||||
restartLessonService.restartLesson();
|
||||
public String applySecurity(@CurrentUser WebGoatUser user) {
|
||||
// webSession.toggleSecurity();
|
||||
// restartLessonService.restartLesson(user);
|
||||
|
||||
var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
||||
return messages.getMessage(msg);
|
||||
// TODO disabled for now
|
||||
// var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
||||
return messages.getMessage("Not working...");
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.lessons.LessonName;
|
||||
|
||||
/**
|
||||
* ************************************************************************************************
|
||||
@ -96,4 +97,21 @@ public class Course {
|
||||
return this.lessons.stream()
|
||||
.reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum);
|
||||
}
|
||||
|
||||
public Lesson getLessonByName(LessonName lessonName) {
|
||||
return lessons.stream()
|
||||
.filter(lesson -> lesson.getName().equals(lessonName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public Lesson getLessonByAssignment(String assignmentName) {
|
||||
return lessons.stream()
|
||||
.filter(
|
||||
lesson ->
|
||||
lesson.getAssignments().stream()
|
||||
.anyMatch(assignment -> assignment.getName().equals(assignmentName)))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.owasp.webgoat.container.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is responsible for managing user session data within a lesson. It uses a HashMap to
|
||||
* store key-value pairs representing session data.
|
||||
*/
|
||||
public class LessonSession {
|
||||
|
||||
private Map<String, Object> userSessionData = new HashMap<>();
|
||||
|
||||
/** Default constructor initializing an empty session. */
|
||||
public LessonSession() {}
|
||||
|
||||
/**
|
||||
* Retrieves the value associated with the given key.
|
||||
*
|
||||
* @param key the key for the session data
|
||||
* @return the value associated with the key, or null if the key does not exist
|
||||
*/
|
||||
public Object getValue(String key) {
|
||||
if (!userSessionData.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
// else
|
||||
return userSessionData.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for the given key. If the key already exists, its value is updated.
|
||||
*
|
||||
* @param key the key for the session data
|
||||
* @param value the value to be associated with the key
|
||||
*/
|
||||
public void setValue(String key, Object value) {
|
||||
if (userSessionData.containsKey(key)) {
|
||||
userSessionData.replace(key, value);
|
||||
} else {
|
||||
userSessionData.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package org.owasp.webgoat.container.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/** Created by jason on 1/4/17. */
|
||||
public class UserSessionData {
|
||||
|
||||
private HashMap<String, Object> userSessionData = new HashMap<>();
|
||||
|
||||
public UserSessionData() {}
|
||||
|
||||
public UserSessionData(String key, String value) {
|
||||
setValue(key, value);
|
||||
}
|
||||
|
||||
// GETTERS & SETTERS
|
||||
public Object getValue(String key) {
|
||||
if (!userSessionData.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
// else
|
||||
return userSessionData.get(key);
|
||||
}
|
||||
|
||||
public void setValue(String key, Object value) {
|
||||
if (userSessionData.containsKey(key)) {
|
||||
userSessionData.replace(key, value);
|
||||
} else {
|
||||
userSessionData.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package org.owasp.webgoat.container.session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||
|
||||
/**
|
||||
* *************************************************************************************************
|
||||
*
|
||||
* <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 <a href="http://www.aspectsecurity.com">Aspect Security</a>
|
||||
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
|
||||
* @version $Id: $Id
|
||||
* @since October 28, 2003
|
||||
*/
|
||||
public class WebSession implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4270066103101711560L;
|
||||
private WebGoatUser currentUser;
|
||||
private transient Lesson currentLesson;
|
||||
private boolean securityEnabled;
|
||||
|
||||
public WebSession(WebGoatUser webGoatUser) {
|
||||
this.currentUser = webGoatUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the field <code>currentScreen</code>.
|
||||
*
|
||||
* @param lesson current lesson
|
||||
*/
|
||||
public void setCurrentLesson(Lesson lesson) {
|
||||
this.currentLesson = lesson;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCurrentLesson.
|
||||
*
|
||||
* @return a {@link Lesson} object.
|
||||
*/
|
||||
public Lesson getCurrentLesson() {
|
||||
return this.currentLesson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the userName attribute of the WebSession object
|
||||
*
|
||||
* @return The userName value
|
||||
*/
|
||||
public String getUserName() {
|
||||
return currentUser.getUsername();
|
||||
}
|
||||
|
||||
public WebGoatUser getUser() {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
public void toggleSecurity() {
|
||||
this.securityEnabled = !this.securityEnabled;
|
||||
}
|
||||
|
||||
public boolean isSecurityEnabled() {
|
||||
return securityEnabled;
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
|
||||
*/
|
||||
@Entity
|
||||
@EqualsAndHashCode
|
||||
public class LessonTracker {
|
||||
public class LessonProgress {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@ -61,25 +61,22 @@ public class LessonTracker {
|
||||
@Getter private String lessonName;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private final Set<Assignment> solvedAssignments = new HashSet<>();
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private final Set<Assignment> allAssignments = new HashSet<>();
|
||||
private final Set<Assignment> assignments = new HashSet<>();
|
||||
|
||||
@Getter private int numberOfAttempts = 0;
|
||||
@Version private Integer version;
|
||||
|
||||
private LessonTracker() {
|
||||
protected LessonProgress() {
|
||||
// JPA
|
||||
}
|
||||
|
||||
public LessonTracker(Lesson lesson) {
|
||||
public LessonProgress(Lesson lesson) {
|
||||
lessonName = lesson.getId();
|
||||
allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
||||
assignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
||||
}
|
||||
|
||||
public Optional<Assignment> getAssignment(String name) {
|
||||
return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
||||
return assignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,14 +85,14 @@ public class LessonTracker {
|
||||
* @param solvedAssignment the assignment which the user solved
|
||||
*/
|
||||
public void assignmentSolved(String solvedAssignment) {
|
||||
getAssignment(solvedAssignment).ifPresent(solvedAssignments::add);
|
||||
getAssignment(solvedAssignment).ifPresent(Assignment::solved);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return did they user solved all solvedAssignments for the lesson?
|
||||
*/
|
||||
public boolean isLessonSolved() {
|
||||
return allAssignments.size() == solvedAssignments.size();
|
||||
return assignments.stream().allMatch(Assignment::isSolved);
|
||||
}
|
||||
|
||||
/** Increase the number attempts to solve the lesson */
|
||||
@ -105,18 +102,17 @@ public class LessonTracker {
|
||||
|
||||
/** Reset the tracker. We do not reset the number of attempts here! */
|
||||
void reset() {
|
||||
solvedAssignments.clear();
|
||||
assignments.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list containing all the assignments solved or not
|
||||
*/
|
||||
public Map<Assignment, Boolean> getLessonOverview() {
|
||||
List<Assignment> notSolved =
|
||||
allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList();
|
||||
Map<Assignment, Boolean> overview =
|
||||
notSolved.stream().collect(Collectors.toMap(a -> a, b -> false));
|
||||
overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true)));
|
||||
return overview;
|
||||
return assignments.stream().collect(Collectors.toMap(a -> a, Assignment::isSolved));
|
||||
}
|
||||
|
||||
long numberOfSolvedAssignments() {
|
||||
return assignments.size();
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@AllArgsConstructor
|
||||
public class Scoreboard {
|
||||
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
private final UserProgressRepository userTrackerRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final Course course;
|
||||
private final PluginMessages pluginMessages;
|
||||
@ -46,7 +46,7 @@ public class Scoreboard {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> challengesSolved(UserTracker userTracker) {
|
||||
private List<String> challengesSolved(UserProgress userTracker) {
|
||||
List<String> challenges =
|
||||
List.of(
|
||||
"Challenge1",
|
||||
@ -59,10 +59,10 @@ public class Scoreboard {
|
||||
"Challenge8",
|
||||
"Challenge9");
|
||||
return challenges.stream()
|
||||
.map(userTracker::getLessonTracker)
|
||||
.map(userTracker::getLessonProgress)
|
||||
.flatMap(Optional::stream)
|
||||
.filter(LessonTracker::isLessonSolved)
|
||||
.map(LessonTracker::getLessonName)
|
||||
.filter(LessonProgress::isLessonSolved)
|
||||
.map(LessonProgress::getLessonName)
|
||||
.map(this::toLessonTitle)
|
||||
.toList();
|
||||
}
|
||||
|
@ -9,13 +9,10 @@ import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.lessons.Assignment;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
|
||||
/**
|
||||
@ -52,7 +49,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
|
||||
@Slf4j
|
||||
@Entity
|
||||
@EqualsAndHashCode
|
||||
public class UserTracker {
|
||||
public class UserProgress {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@ -62,11 +59,11 @@ public class UserTracker {
|
||||
private String user;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private Set<LessonTracker> lessonTrackers = new HashSet<>();
|
||||
private Set<LessonProgress> lessonProgress = new HashSet<>();
|
||||
|
||||
private UserTracker() {}
|
||||
protected UserProgress() {}
|
||||
|
||||
public UserTracker(final String user) {
|
||||
public UserProgress(final String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@ -76,15 +73,15 @@ public class UserTracker {
|
||||
* @param lesson the lesson
|
||||
* @return a lesson tracker created if not already present
|
||||
*/
|
||||
public LessonTracker getLessonTracker(Lesson lesson) {
|
||||
Optional<LessonTracker> lessonTracker =
|
||||
lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
||||
if (!lessonTracker.isPresent()) {
|
||||
LessonTracker newLessonTracker = new LessonTracker(lesson);
|
||||
lessonTrackers.add(newLessonTracker);
|
||||
public LessonProgress getLessonProgress(Lesson lesson) {
|
||||
Optional<LessonProgress> progress =
|
||||
lessonProgress.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
||||
if (!progress.isPresent()) {
|
||||
LessonProgress newLessonTracker = new LessonProgress(lesson);
|
||||
lessonProgress.add(newLessonTracker);
|
||||
return newLessonTracker;
|
||||
} else {
|
||||
return lessonTracker.get();
|
||||
return progress.get();
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,43 +91,34 @@ public class UserTracker {
|
||||
* @param id the id of the lesson
|
||||
* @return optional due to the fact we can only create a lesson tracker based on a lesson
|
||||
*/
|
||||
public Optional<LessonTracker> getLessonTracker(String id) {
|
||||
return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
||||
public Optional<LessonProgress> getLessonProgress(String id) {
|
||||
return lessonProgress.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
||||
}
|
||||
|
||||
public void assignmentSolved(Lesson lesson, String assignmentName) {
|
||||
LessonTracker lessonTracker = getLessonTracker(lesson);
|
||||
lessonTracker.incrementAttempts();
|
||||
lessonTracker.assignmentSolved(assignmentName);
|
||||
LessonProgress progress = getLessonProgress(lesson);
|
||||
progress.incrementAttempts();
|
||||
progress.assignmentSolved(assignmentName);
|
||||
}
|
||||
|
||||
public void assignmentFailed(Lesson lesson) {
|
||||
LessonTracker lessonTracker = getLessonTracker(lesson);
|
||||
lessonTracker.incrementAttempts();
|
||||
LessonProgress progress = getLessonProgress(lesson);
|
||||
progress.incrementAttempts();
|
||||
}
|
||||
|
||||
public void reset(Lesson al) {
|
||||
LessonTracker lessonTracker = getLessonTracker(al);
|
||||
lessonTracker.reset();
|
||||
LessonProgress progress = getLessonProgress(al);
|
||||
progress.reset();
|
||||
}
|
||||
|
||||
public int numberOfLessonsSolved() {
|
||||
int numberOfLessonsSolved = 0;
|
||||
for (LessonTracker lessonTracker : lessonTrackers) {
|
||||
if (lessonTracker.isLessonSolved()) {
|
||||
numberOfLessonsSolved = numberOfLessonsSolved + 1;
|
||||
}
|
||||
}
|
||||
return numberOfLessonsSolved;
|
||||
public long numberOfLessonsSolved() {
|
||||
return lessonProgress.stream().filter(LessonProgress::isLessonSolved).count();
|
||||
}
|
||||
|
||||
public int numberOfAssignmentsSolved() {
|
||||
int numberOfAssignmentsSolved = 0;
|
||||
for (LessonTracker lessonTracker : lessonTrackers) {
|
||||
Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview();
|
||||
numberOfAssignmentsSolved =
|
||||
lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();
|
||||
}
|
||||
return numberOfAssignmentsSolved;
|
||||
public long numberOfAssignmentsSolved() {
|
||||
return lessonProgress.stream()
|
||||
.map(LessonProgress::numberOfSolvedAssignments)
|
||||
.mapToLong(Long::valueOf)
|
||||
.sum();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.owasp.webgoat.container.users;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface UserProgressRepository extends JpaRepository<UserProgress, String> {
|
||||
|
||||
// TODO: make optional
|
||||
UserProgress findByUser(String user);
|
||||
}
|
@ -4,7 +4,7 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.owasp.webgoat.container.lessons.Initializeable;
|
||||
import org.owasp.webgoat.container.lessons.Initializable;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
@ -19,10 +19,10 @@ import org.springframework.stereotype.Service;
|
||||
public class UserService implements UserDetailsService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
private final UserProgressRepository userTrackerRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final Function<String, Flyway> flywayLessons;
|
||||
private final List<Initializeable> lessonInitializables;
|
||||
private final List<Initializable> lessonInitializables;
|
||||
|
||||
@Override
|
||||
public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
@ -43,7 +43,7 @@ public class UserService implements UserDetailsService {
|
||||
|
||||
if (!userAlreadyExists) {
|
||||
userTrackerRepository.save(
|
||||
new UserTracker(username)); // if user previously existed it will not get another tracker
|
||||
new UserProgress(username)); // if user previously existed it will not get another tracker
|
||||
createLessonsForUser(webGoatUser);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
package org.owasp.webgoat.container.users;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 4/30/17.
|
||||
*/
|
||||
public interface UserTrackerRepository extends JpaRepository<UserTracker, String> {
|
||||
|
||||
UserTracker findByUser(String user);
|
||||
}
|
@ -32,9 +32,7 @@ import java.util.Map;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -50,9 +48,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
})
|
||||
public class VerifyAccount extends AssignmentEndpoint {
|
||||
|
||||
@Autowired private WebSession webSession;
|
||||
private final LessonSession userSessionData;
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
public VerifyAccount(LessonSession userSessionData) {
|
||||
this.userSessionData = userSessionData;
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
path = "/auth-bypass/verify-account",
|
||||
|
@ -2,11 +2,13 @@ package org.owasp.webgoat.lessons.challenges;
|
||||
|
||||
import org.owasp.webgoat.container.lessons.Category;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 3/21/17.
|
||||
*/
|
||||
@Component
|
||||
public class ChallengeIntro extends Lesson {
|
||||
|
||||
@Override
|
||||
|
@ -25,8 +25,7 @@ package org.owasp.webgoat.lessons.challenges;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -36,13 +35,12 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@AllArgsConstructor
|
||||
public class FlagController extends AssignmentEndpoint {
|
||||
|
||||
private final WebSession webSession;
|
||||
private final Flags flags;
|
||||
|
||||
@PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@PostMapping(path = "/challenge/flag/{flagNumber}")
|
||||
@ResponseBody
|
||||
public AttackResult postFlag(@RequestParam String flag) {
|
||||
Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson());
|
||||
public AttackResult postFlag(@PathVariable int flagNumber, @RequestParam String flag) {
|
||||
var expectedFlag = flags.getFlag(flagNumber);
|
||||
if (expectedFlag.isCorrect(flag)) {
|
||||
return success(this).feedback("challenge.flag.correct").build();
|
||||
} else {
|
||||
|
@ -4,7 +4,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.IntStream;
|
||||
import org.owasp.webgoat.container.lessons.Lesson;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ -15,12 +14,6 @@ public class Flags {
|
||||
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString())));
|
||||
}
|
||||
|
||||
public Flag getFlag(Lesson forLesson) {
|
||||
String lessonName = forLesson.getName();
|
||||
int challengeNumber = Integer.valueOf(lessonName.substring(lessonName.length() - 1));
|
||||
return FLAGS.get(challengeNumber);
|
||||
}
|
||||
|
||||
public Flag getFlag(int flagNumber) {
|
||||
return FLAGS.get(flagNumber);
|
||||
}
|
||||
|
@ -24,14 +24,14 @@ package org.owasp.webgoat.lessons.chromedevtools;
|
||||
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* This is just a class used to make the the HTTP request.
|
||||
* This is just a class used to make the HTTP request.
|
||||
*
|
||||
* @author TMelzer
|
||||
* @since 30.11.18
|
||||
@ -39,11 +39,16 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
public class NetworkDummy extends AssignmentEndpoint {
|
||||
|
||||
private final LessonSession lessonSession;
|
||||
|
||||
public NetworkDummy(LessonSession lessonSession) {
|
||||
this.lessonSession = lessonSession;
|
||||
}
|
||||
|
||||
@PostMapping("/ChromeDevTools/dummy")
|
||||
@ResponseBody
|
||||
public AttackResult completed(@RequestParam String successMessage) {
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
String answer = (String) userSessionData.getValue("randValue");
|
||||
String answer = (String) lessonSession.getValue("randValue");
|
||||
|
||||
if (successMessage != null && successMessage.equals(answer)) {
|
||||
return success(this).feedback("xss-dom-message-success").build();
|
||||
|
@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
|
||||
public class CSRF extends Lesson {
|
||||
@Override
|
||||
public Category getDefaultCategory() {
|
||||
return Category.A10;
|
||||
return Category.A5;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,7 +25,7 @@ package org.owasp.webgoat.lessons.csrf;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"})
|
||||
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
|
||||
@PostMapping(
|
||||
path = "/csrf/confirm-flag-1",
|
||||
|
@ -33,7 +33,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -42,15 +42,11 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 11/17/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
|
||||
public class CSRFFeedback extends AssignmentEndpoint {
|
||||
|
||||
@Autowired private UserSessionData userSessionData;
|
||||
@Autowired private LessonSession userSessionData;
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
|
||||
@PostMapping(
|
||||
|
@ -27,10 +27,9 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@ -38,13 +37,12 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
public class CSRFGetFlag {
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
@Autowired private PluginMessages pluginMessages;
|
||||
|
||||
@RequestMapping(
|
||||
@PostMapping(
|
||||
path = "/csrf/basic-get-flag",
|
||||
produces = {"application/json"},
|
||||
method = RequestMethod.POST)
|
||||
produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public Map<String, Object> invoke(HttpServletRequest req) {
|
||||
|
||||
|
@ -22,47 +22,26 @@
|
||||
|
||||
package org.owasp.webgoat.lessons.csrf;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.users.UserTracker;
|
||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author nbaars
|
||||
* @since 11/17/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
|
||||
public class CSRFLogin extends AssignmentEndpoint {
|
||||
|
||||
private final UserTrackerRepository userTrackerRepository;
|
||||
|
||||
public CSRFLogin(UserTrackerRepository userTrackerRepository) {
|
||||
this.userTrackerRepository = userTrackerRepository;
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
path = "/csrf/login",
|
||||
produces = {"application/json"})
|
||||
@ResponseBody
|
||||
public AttackResult completed(HttpServletRequest request) {
|
||||
String userName = request.getUserPrincipal().getName();
|
||||
if (userName.startsWith("csrf")) {
|
||||
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
|
||||
public AttackResult completed(@CurrentUsername String username) {
|
||||
if (username.startsWith("csrf")) {
|
||||
return success(this).feedback("csrf-login-success").build();
|
||||
}
|
||||
return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build();
|
||||
}
|
||||
|
||||
private void markAssignmentSolvedWithRealUser(String username) {
|
||||
UserTracker userTracker = userTrackerRepository.findByUser(username);
|
||||
userTracker.assignmentSolved(
|
||||
getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
|
||||
userTrackerRepository.save(userTracker);
|
||||
return failed(this).feedback("csrf-login-failed").feedbackArgs(username).build();
|
||||
}
|
||||
}
|
||||
|
@ -33,11 +33,10 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -48,7 +47,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"})
|
||||
public class ForgedReviews extends AssignmentEndpoint {
|
||||
|
||||
@Autowired private WebSession webSession;
|
||||
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
|
||||
|
||||
private static final Map<String, List<Review>> userReviews = new HashMap<>();
|
||||
@ -73,9 +71,9 @@ public class ForgedReviews extends AssignmentEndpoint {
|
||||
produces = MediaType.APPLICATION_JSON_VALUE,
|
||||
consumes = ALL_VALUE)
|
||||
@ResponseBody
|
||||
public Collection<Review> retrieveReviews() {
|
||||
public Collection<Review> retrieveReviews(@CurrentUsername String username) {
|
||||
Collection<Review> allReviews = Lists.newArrayList();
|
||||
Collection<Review> newReviews = userReviews.get(webSession.getUserName());
|
||||
Collection<Review> newReviews = userReviews.get(username);
|
||||
if (newReviews != null) {
|
||||
allReviews.addAll(newReviews);
|
||||
}
|
||||
@ -88,7 +86,11 @@ public class ForgedReviews extends AssignmentEndpoint {
|
||||
@PostMapping("/csrf/review")
|
||||
@ResponseBody
|
||||
public AttackResult createNewReview(
|
||||
String reviewText, Integer stars, String validateReq, HttpServletRequest request) {
|
||||
String reviewText,
|
||||
Integer stars,
|
||||
String validateReq,
|
||||
HttpServletRequest request,
|
||||
@CurrentUsername String username) {
|
||||
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
|
||||
final String referer =
|
||||
(request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
|
||||
@ -97,11 +99,11 @@ public class ForgedReviews extends AssignmentEndpoint {
|
||||
Review review = new Review();
|
||||
review.setText(reviewText);
|
||||
review.setDateTime(LocalDateTime.now().format(fmt));
|
||||
review.setUser(webSession.getUserName());
|
||||
review.setUser(username);
|
||||
review.setStars(stars);
|
||||
var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>());
|
||||
var reviews = userReviews.getOrDefault(username, new ArrayList<>());
|
||||
reviews.add(review);
|
||||
userReviews.put(webSession.getUserName(), reviews);
|
||||
userReviews.put(username, reviews);
|
||||
// short-circuit
|
||||
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
|
||||
return failed(this).feedback("csrf-you-forgot-something").build();
|
||||
|
@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
})
|
||||
public class IDOREditOtherProfile extends AssignmentEndpoint {
|
||||
|
||||
@Autowired private UserSessionData userSessionData;
|
||||
@Autowired private LessonSession userSessionData;
|
||||
|
||||
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
|
||||
@ResponseBody
|
||||
|
@ -28,7 +28,7 @@ import java.util.Map;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -38,6 +38,12 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@AssignmentHints({"idor.hints.idor_login"})
|
||||
public class IDORLogin extends AssignmentEndpoint {
|
||||
|
||||
private final LessonSession lessonSession;
|
||||
|
||||
public IDORLogin(LessonSession lessonSession) {
|
||||
this.lessonSession = lessonSession;
|
||||
}
|
||||
|
||||
private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();
|
||||
|
||||
public void initIDORInfo() {
|
||||
@ -59,13 +65,11 @@ public class IDORLogin extends AssignmentEndpoint {
|
||||
@ResponseBody
|
||||
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
|
||||
initIDORInfo();
|
||||
UserSessionData userSessionData = getUserSessionData();
|
||||
|
||||
if (idorUserInfo.containsKey(username)) {
|
||||
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
|
||||
userSessionData.setValue("idor-authenticated-as", username);
|
||||
userSessionData.setValue(
|
||||
"idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
|
||||
lessonSession.setValue("idor-authenticated-as", username);
|
||||
lessonSession.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
|
||||
return success(this).feedback("idor.login.success").feedbackArgs(username).build();
|
||||
} else {
|
||||
return failed(this).feedback("idor.login.failure").build();
|
||||
|
@ -27,7 +27,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
})
|
||||
public class IDORViewOtherProfile extends AssignmentEndpoint {
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
|
||||
@GetMapping(
|
||||
path = "/IDOR/profile/{userId}",
|
||||
|
@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@Slf4j
|
||||
public class IDORViewOwnProfile {
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
|
||||
@GetMapping(
|
||||
path = {"/IDOR/own", "/IDOR/profile"},
|
||||
|
@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@ -41,7 +41,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
})
|
||||
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {
|
||||
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
|
||||
@PostMapping("/IDOR/profile/alt-path")
|
||||
@ResponseBody
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.owasp.webgoat.lessons.jwt.claimmisuse;
|
||||
|
||||
import com.auth0.jwk.JwkException;
|
||||
import com.auth0.jwk.JwkProvider;
|
||||
import com.auth0.jwk.JwkProviderBuilder;
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
@ -48,12 +47,12 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint {
|
||||
try {
|
||||
var decodedJWT = JWT.decode(token);
|
||||
var jku = decodedJWT.getHeaderClaim("jku");
|
||||
JwkProvider jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build();
|
||||
var jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build();
|
||||
var jwk = jwkProvider.get(decodedJWT.getKeyId());
|
||||
var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey());
|
||||
JWT.require(algorithm).build().verify(decodedJWT);
|
||||
|
||||
String username = decodedJWT.getClaims().get("username").asString();
|
||||
var username = decodedJWT.getClaims().get("username").asString();
|
||||
if ("Jerry".equals(username)) {
|
||||
return failed(this).feedback("jwt-final-jerry-account").build();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import lombok.AllArgsConstructor;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.UserSessionData;
|
||||
import org.owasp.webgoat.container.session.LessonSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -44,7 +44,7 @@ public class SampleAttack extends AssignmentEndpoint {
|
||||
String secretValue = "secr37Value";
|
||||
|
||||
// UserSessionData is bound to session and can be used to persist data across multiple assignments
|
||||
@Autowired UserSessionData userSessionData;
|
||||
@Autowired LessonSession userSessionData;
|
||||
|
||||
@PostMapping("/lesson-template/sample-attack")
|
||||
@ResponseBody
|
||||
|
@ -30,7 +30,7 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@ -47,7 +47,6 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
public class MissingFunctionACUsers {
|
||||
|
||||
private final MissingAccessControlUserRepository userRepository;
|
||||
private final WebSession webSession;
|
||||
|
||||
@GetMapping(path = {"access-control/users"})
|
||||
public ModelAndView listUsers() {
|
||||
@ -81,8 +80,8 @@ public class MissingFunctionACUsers {
|
||||
path = {"access-control/users-admin-fix"},
|
||||
consumes = "application/json")
|
||||
@ResponseBody
|
||||
public ResponseEntity<List<DisplayUser>> usersFixed() {
|
||||
var currentUser = userRepository.findByUsername(webSession.getUserName());
|
||||
public ResponseEntity<List<DisplayUser>> usersFixed(@CurrentUsername String username) {
|
||||
var currentUser = userRepository.findByUsername(username);
|
||||
if (currentUser != null && currentUser.isAdmin()) {
|
||||
return ResponseEntity.ok(
|
||||
userRepository.findAllUsers().stream()
|
||||
|
@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
@ -81,10 +82,10 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
|
||||
@PostMapping("/PasswordReset/reset/login")
|
||||
@ResponseBody
|
||||
public AttackResult login(@RequestParam String password, @RequestParam String email) {
|
||||
public AttackResult login(
|
||||
@RequestParam String password, @RequestParam String email, @CurrentUsername String username) {
|
||||
if (TOM_EMAIL.equals(email)) {
|
||||
String passwordTom =
|
||||
usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
|
||||
String passwordTom = usersToTomPassword.getOrDefault(username, PASSWORD_TOM_9);
|
||||
if (passwordTom.equals(PASSWORD_TOM_9)) {
|
||||
return failed(this).feedback("login_failed").build();
|
||||
} else if (passwordTom.equals(password)) {
|
||||
@ -112,7 +113,9 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
|
||||
@PostMapping("/PasswordReset/reset/change-password")
|
||||
public ModelAndView changePassword(
|
||||
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||
@ModelAttribute("form") PasswordChangeForm form,
|
||||
BindingResult bindingResult,
|
||||
@CurrentUsername String username) {
|
||||
ModelAndView modelAndView = new ModelAndView();
|
||||
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {
|
||||
bindingResult.rejectValue("password", "not.empty");
|
||||
@ -125,15 +128,15 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||
return modelAndView;
|
||||
}
|
||||
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||
if (checkIfLinkIsFromTom(form.getResetLink(), username)) {
|
||||
usersToTomPassword.put(username, form.getPassword());
|
||||
}
|
||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
|
||||
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
|
||||
private boolean checkIfLinkIsFromTom(String resetLinkFromForm, String username) {
|
||||
String resetLink = userToTomResetLink.getOrDefault(username, "unknown");
|
||||
return resetLink.equals(resetLinkFromForm);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ package org.owasp.webgoat.lessons.passwordreset;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.UUID;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -67,14 +68,14 @@ public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint {
|
||||
@PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link")
|
||||
@ResponseBody
|
||||
public AttackResult sendPasswordResetLink(
|
||||
@RequestParam String email, HttpServletRequest request) {
|
||||
@RequestParam String email, HttpServletRequest request, @CurrentUsername String username) {
|
||||
String resetLink = UUID.randomUUID().toString();
|
||||
ResetLinkAssignment.resetLinks.add(resetLink);
|
||||
String host = request.getHeader(HttpHeaders.HOST);
|
||||
if (ResetLinkAssignment.TOM_EMAIL.equals(email)
|
||||
&& (host.contains(webWolfPort)
|
||||
&& host.contains(webWolfHost))) { // User indeed changed the host header.
|
||||
ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
||||
ResetLinkAssignment.userToTomResetLink.put(username, resetLink);
|
||||
fakeClickingLinkEmail(webWolfURL, resetLink);
|
||||
} else {
|
||||
try {
|
||||
|
@ -26,6 +26,7 @@ import static java.util.Optional.ofNullable;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@ -57,12 +58,14 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
||||
path = "/PasswordReset/simple-mail",
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
@ResponseBody
|
||||
public AttackResult login(@RequestParam String email, @RequestParam String password) {
|
||||
public AttackResult login(
|
||||
@RequestParam String email,
|
||||
@RequestParam String password,
|
||||
@CurrentUsername String webGoatUsername) {
|
||||
String emailAddress = ofNullable(email).orElse("unknown@webgoat.org");
|
||||
String username = extractUsername(emailAddress);
|
||||
|
||||
if (username.equals(getWebSession().getUserName())
|
||||
&& StringUtils.reverse(username).equals(password)) {
|
||||
if (username.equals(webGoatUsername) && StringUtils.reverse(username).equals(password)) {
|
||||
return success(this).build();
|
||||
} else {
|
||||
return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build();
|
||||
@ -73,9 +76,10 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
value = "/PasswordReset/simple-mail/reset")
|
||||
@ResponseBody
|
||||
public AttackResult resetPassword(@RequestParam String emailReset) {
|
||||
public AttackResult resetPassword(
|
||||
@RequestParam String emailReset, @CurrentUsername String username) {
|
||||
String email = ofNullable(emailReset).orElse("unknown@webgoat.org");
|
||||
return sendEmail(extractUsername(email), email);
|
||||
return sendEmail(extractUsername(email), email, username);
|
||||
}
|
||||
|
||||
private String extractUsername(String email) {
|
||||
@ -83,8 +87,8 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
||||
return email.substring(0, index == -1 ? email.length() : index);
|
||||
}
|
||||
|
||||
private AttackResult sendEmail(String username, String email) {
|
||||
if (username.equals(getWebSession().getUserName())) {
|
||||
private AttackResult sendEmail(String username, String email, String webGoatUsername) {
|
||||
if (username.equals(webGoatUsername)) {
|
||||
PasswordResetEmail mailEvent =
|
||||
PasswordResetEmail.builder()
|
||||
.recipient(username)
|
||||
|
@ -3,9 +3,9 @@ package org.owasp.webgoat.lessons.pathtraversal;
|
||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
import org.owasp.webgoat.container.CurrentUsername;
|
||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||
import org.owasp.webgoat.container.session.WebSession;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -23,9 +23,8 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
})
|
||||
public class ProfileUpload extends ProfileUploadBase {
|
||||
|
||||
public ProfileUpload(
|
||||
@Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) {
|
||||
super(webGoatHomeDirectory, webSession);
|
||||
public ProfileUpload(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) {
|
||||
super(webGoatHomeDirectory);
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
@ -35,13 +34,14 @@ public class ProfileUpload extends ProfileUploadBase {
|
||||
@ResponseBody
|
||||
public AttackResult uploadFileHandler(
|
||||
@RequestParam("uploadedFile") MultipartFile file,
|
||||
@RequestParam(value = "fullName", required = false) String fullName) {
|
||||
return super.execute(file, fullName);
|
||||
@RequestParam(value = "fullName", required = false) String fullName,
|
||||
@CurrentUsername String username) {
|
||||
return super.execute(file, fullName, username);
|
||||
}
|
||||
|
||||
@GetMapping("/PathTraversal/profile-picture")
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> getProfilePicture() {
|
||||
return super.getProfilePicture();
|
||||
public ResponseEntity<?> getProfilePicture(@CurrentUsername String username) {
|
||||
return super.getProfilePicture(username);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user