Compare commits
6 Commits
nbaars/bui
...
lang-es
Author | SHA1 | Date | |
---|---|---|---|
27f23fea19 | |||
9bd7a81435 | |||
08ccb069df | |||
2357651570 | |||
b25caa5c2f | |||
9c354d5699 |
4
.github/workflows/branchbuild.txt
vendored
4
.github/workflows/branchbuild.txt
vendored
@ -11,9 +11,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
java-version: [ 21 ]
|
java-version: [ 17, 21 ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Set up JDK ${{ matrix.java-version }}
|
- name: Set up JDK ${{ matrix.java-version }}
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
|
40
.github/workflows/build.yml
vendored
40
.github/workflows/build.yml
vendored
@ -5,46 +5,30 @@ on:
|
|||||||
- '.txt'
|
- '.txt'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-commit:
|
|
||||||
name: Pre-commit check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.9"
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
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:
|
build:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
matrix:
|
||||||
os: [ windows-latest, ubuntu-latest, macos-13 ]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
max-parallel: 1
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v3
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4.2.1
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
cache: 'maven'
|
- name: Cache Maven packages
|
||||||
|
uses: actions/cache@v3.3.1
|
||||||
|
with:
|
||||||
|
path: ~/.m2
|
||||||
|
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: ${{ runner.os }}-m2-
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn --no-transfer-progress verify
|
run: mvn --no-transfer-progress verify
|
||||||
|
29
.github/workflows/pre-commit.yaml
vendored
Normal file
29
.github/workflows/pre-commit.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
name: Pre-commit check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
jobs:
|
||||||
|
pre-commit:
|
||||||
|
name: Pre-commit check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.9"
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
- name: Pre-commit checks
|
||||||
|
uses: pre-commit/action@v3.0.0
|
||||||
|
- name: pre-commit-ci-lite
|
||||||
|
uses: pre-commit-ci/lite-action@v1.0.1
|
||||||
|
if: always()
|
28
.github/workflows/release.yml
vendored
28
.github/workflows/release.yml
vendored
@ -13,15 +13,21 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
name: release
|
name: release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
cache: 'maven'
|
|
||||||
|
- name: Cache Maven packages
|
||||||
|
uses: actions/cache@v3.3.1
|
||||||
|
with:
|
||||||
|
path: ~/.m2
|
||||||
|
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: ${{ runner.os }}-m2
|
||||||
|
|
||||||
- name: "Set labels for ${{ github.ref }}"
|
- name: "Set labels for ${{ github.ref }}"
|
||||||
run: |
|
run: |
|
||||||
@ -68,7 +74,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: "Set up QEMU"
|
- name: "Set up QEMU"
|
||||||
uses: docker/setup-qemu-action@v3.1.0
|
uses: docker/setup-qemu-action@v2.2.0
|
||||||
with:
|
with:
|
||||||
platforms: all
|
platforms: all
|
||||||
|
|
||||||
@ -76,13 +82,13 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: "Login to dockerhub"
|
- name: "Login to dockerhub"
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v3.0.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: "Build and push WebGoat"
|
- name: "Build and push WebGoat"
|
||||||
uses: docker/build-push-action@v6.9.0
|
uses: docker/build-push-action@v5.1.0
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
@ -95,7 +101,7 @@ jobs:
|
|||||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||||
|
|
||||||
- name: "Build and push WebGoat desktop"
|
- name: "Build and push WebGoat desktop"
|
||||||
uses: docker/build-push-action@v6.9.0
|
uses: docker/build-push-action@v5.1.0
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./Dockerfile_desktop
|
file: ./Dockerfile_desktop
|
||||||
@ -112,15 +118,15 @@ jobs:
|
|||||||
needs: [ release ]
|
needs: [ release ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up JDK 21
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|
||||||
- name: Set version to next snapshot
|
- name: Set version to next snapshot
|
||||||
|
18
.github/workflows/test.yml
vendored
18
.github/workflows/test.yml
vendored
@ -21,21 +21,27 @@ jobs:
|
|||||||
name: "Robot framework test"
|
name: "Robot framework test"
|
||||||
steps:
|
steps:
|
||||||
# Uses an default action to checkout the code
|
# Uses an default action to checkout the code
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v3
|
||||||
# Uses an action to add Python to the VM
|
# Uses an action to add Python to the VM
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
architecture: x64
|
architecture: x64
|
||||||
# Uses an action to add JDK 21 to the VM (and mvn?)
|
# Uses an action to add JDK 17 to the VM (and mvn?)
|
||||||
- name: set up JDK 21
|
- name: set up JDK 17
|
||||||
uses: actions/setup-java@v4.2.1
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
cache: 'maven'
|
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
|
||||||
|
- name: Cache Maven packages
|
||||||
|
uses: actions/cache@v3.3.1
|
||||||
|
with:
|
||||||
|
path: ~/.m2
|
||||||
|
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: ubuntu-latest-m2-
|
||||||
- uses: BSFishy/pip-action@v1
|
- uses: BSFishy/pip-action@v1
|
||||||
with:
|
with:
|
||||||
packages: |
|
packages: |
|
||||||
|
11
Dockerfile
11
Dockerfile
@ -1,8 +1,6 @@
|
|||||||
# We need JDK as some of the lessons needs to be able to compile Java code
|
FROM docker.io/eclipse-temurin:21.0.1_12-jre
|
||||||
FROM docker.io/eclipse-temurin:21-jdk-jammy
|
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||||
|
MAINTAINER "WebGoat team"
|
||||||
LABEL name="WebGoat: A deliberately insecure Web Application"
|
|
||||||
LABEL maintainer="WebGoat team"
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
useradd -ms /bin/bash webgoat && \
|
useradd -ms /bin/bash webgoat && \
|
||||||
@ -35,6 +33,3 @@ ENTRYPOINT [ "java", \
|
|||||||
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
|
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
|
||||||
"-Drunning.in.docker=true", \
|
"-Drunning.in.docker=true", \
|
||||||
"-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
|
"-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
|
||||||
|
|
||||||
HEALTHCHECK --interval=5s --timeout=3s \
|
|
||||||
CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
FROM lscr.io/linuxserver/webtop:ubuntu-xfce
|
FROM lscr.io/linuxserver/webtop:ubuntu-xfce
|
||||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||||
LABEL maintainer = "WebGoat team"
|
MAINTAINER "WebGoat team"
|
||||||
|
|
||||||
WORKDIR /config
|
WORKDIR /config
|
||||||
|
|
||||||
@ -9,38 +9,26 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
|
|||||||
COPY config/desktop/start_zap.sh /config/start_zap.sh
|
COPY config/desktop/start_zap.sh /config/start_zap.sh
|
||||||
COPY config/desktop/WebGoat.txt /config/Desktop/
|
COPY config/desktop/WebGoat.txt /config/Desktop/
|
||||||
|
|
||||||
RUN \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get --yes install vim nano gzip
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
case $(uname -m) in \
|
case $(uname -m) in \
|
||||||
x86_64) ARCH=x64;; \
|
x86_64) ARCH=x64;; \
|
||||||
aarch64) ARCH=aarch64;; \
|
aarch64) ARCH=aarch64;; \
|
||||||
*) ARCH=unknown;; \
|
*) ARCH=unknown;; \
|
||||||
esac && \
|
esac && \
|
||||||
echo ${ARCH}
|
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \
|
||||||
|
tar zfxv ZAP_2.12.0_Linux.tar.gz && \
|
||||||
RUN \
|
rm -rf ZAP_2.12.0_Linux.tar.gz && \
|
||||||
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \
|
curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
tar zfxv ZAP_2.15.0_Linux.tar.gz && \
|
tar zfxv OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
rm -rf ZAP_2.15.0_Linux.tar.gz
|
rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
|
|
||||||
RUN \
|
|
||||||
case $(uname -m) in \
|
|
||||||
x86_64) ARCH=x64;; \
|
|
||||||
aarch64) ARCH=aarch64;; \
|
|
||||||
*) ARCH=unknown;; \
|
|
||||||
esac && \
|
|
||||||
echo "oeps == ${ARCH}==" && \
|
|
||||||
curl -L https://github.com/adoptium/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_webgoat.sh && \
|
||||||
chmod +x /config/start_zap.sh && \
|
chmod +x /config/start_zap.sh && \
|
||||||
echo "JAVA_HOME=/config/jdk-21.0.3+9-jre/" >> .bash_aliases && \
|
apt-get update && \
|
||||||
|
apt-get --yes install vim nano && \
|
||||||
|
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
|
||||||
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
||||||
|
|
||||||
ENV JAVA_HOME=/config/jdk-21.0.3+9-jre
|
|
||||||
|
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
|
||||||
|
|
||||||
WORKDIR /config/Desktop
|
WORKDIR /config/Desktop
|
||||||
|
16
README.md
16
README.md
@ -1,7 +1,7 @@
|
|||||||
# WebGoat: A deliberately insecure Web Application
|
# WebGoat: A deliberately insecure Web Application
|
||||||
|
|
||||||
[](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
|
[](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
|
||||||
[](https://jdk.java.net/)
|
[](https://jdk.java.net/)
|
||||||
[](https://owasp.org/projects/)
|
[](https://owasp.org/projects/)
|
||||||
[](https://github.com/WebGoat/WebGoat/releases/latest)
|
[](https://github.com/WebGoat/WebGoat/releases/latest)
|
||||||
[](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
@ -80,21 +80,11 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
export TZ=Europe/Amsterdam # or your timezone
|
export TZ=Europe/Amsterdam # or your timezone
|
||||||
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
|
java -Dfile.encoding=UTF-8 -jar webgoat-2023.5.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Click the link in the log to start WebGoat.
|
Click the link in the log to start WebGoat.
|
||||||
|
|
||||||
### 3.1 Running on a different port
|
|
||||||
|
|
||||||
If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002
|
|
||||||
```
|
|
||||||
|
|
||||||
For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties).
|
|
||||||
|
|
||||||
## 4. Run from the sources
|
## 4. Run from the sources
|
||||||
|
|
||||||
### Prerequisites:
|
### Prerequisites:
|
||||||
@ -154,7 +144,7 @@ For instance running as a jar on a Linux/macOS it will look like this:
|
|||||||
export TZ=Europe/Amsterdam # or your timezone
|
export TZ=Europe/Amsterdam # or your timezone
|
||||||
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
|
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
|
||||||
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
|
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
|
||||||
java -jar target/webgoat-2023.8-SNAPSHOT.jar
|
java -jar target/webgoat-2023.6-SNAPSHOT.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
/config/jdk-21.0.3+9-jre/bin/java \
|
/config/jdk-17.0.6+10-jre/bin/java \
|
||||||
-Duser.home=/config \
|
-Duser.home=/config \
|
||||||
-Dfile.encoding=UTF-8 \
|
-Dfile.encoding=UTF-8 \
|
||||||
-DTZ=Europe/Amsterdam \
|
-DTZ=Europe/Amsterdam \
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
/config/jdk-21.0.3+9-jre/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar
|
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
|
||||||
|
163
pom.xml
163
pom.xml
@ -5,12 +5,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.3.5</version>
|
<version>3.1.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat</artifactId>
|
<artifactId>webgoat</artifactId>
|
||||||
<version>2024.2-SNAPSHOT</version>
|
<version>2023.9-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>WebGoat</name>
|
<name>WebGoat</name>
|
||||||
@ -29,6 +29,13 @@
|
|||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>mayhew64</id>
|
||||||
|
<name>Bruce Mayhew</name>
|
||||||
|
<email>webgoat@owasp.org</email>
|
||||||
|
<organization>OWASP</organization>
|
||||||
|
<organizationUrl>https://github.com/WebGoat/WebGoat</organizationUrl>
|
||||||
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>nbaars</id>
|
<id>nbaars</id>
|
||||||
<name>Nanne Baars</name>
|
<name>Nanne Baars</name>
|
||||||
@ -36,6 +43,11 @@
|
|||||||
<organizationUrl>https://github.com/nbaars</organizationUrl>
|
<organizationUrl>https://github.com/nbaars</organizationUrl>
|
||||||
<timezone>Europe/Amsterdam</timezone>
|
<timezone>Europe/Amsterdam</timezone>
|
||||||
</developer>
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>misfir3</id>
|
||||||
|
<name>Jason White</name>
|
||||||
|
<email>jason.white@owasp.org</email>
|
||||||
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>zubcevic</id>
|
<id>zubcevic</id>
|
||||||
<name>René Zubcevic</name>
|
<name>René Zubcevic</name>
|
||||||
@ -46,8 +58,43 @@
|
|||||||
<name>Àngel Ollé Blázquez</name>
|
<name>Àngel Ollé Blázquez</name>
|
||||||
<email>angel@olleb.com</email>
|
<email>angel@olleb.com</email>
|
||||||
</developer>
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>jwayman</id>
|
||||||
|
<name>Jeff Wayman</name>
|
||||||
|
<email></email>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>dcowden</id>
|
||||||
|
<name>Dave Cowden</name>
|
||||||
|
<email></email>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>lawson89</id>
|
||||||
|
<name>Richard Lawson</name>
|
||||||
|
<email></email>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>dougmorato</id>
|
||||||
|
<name>Doug Morato</name>
|
||||||
|
<email>doug.morato@owasp.org</email>
|
||||||
|
<organization>OWASP</organization>
|
||||||
|
<organizationUrl>https://github.com/dougmorato</organizationUrl>
|
||||||
|
<timezone>America/New_York</timezone>
|
||||||
|
<properties>
|
||||||
|
<picUrl>https://avatars2.githubusercontent.com/u/9654?v=3&s=150</picUrl>
|
||||||
|
</properties>
|
||||||
|
</developer>
|
||||||
</developers>
|
</developers>
|
||||||
|
|
||||||
|
<mailingLists>
|
||||||
|
<mailingList>
|
||||||
|
<name>OWASP WebGoat Mailing List</name>
|
||||||
|
<subscribe>https://lists.owasp.org/mailman/listinfo/owasp-webgoat</subscribe>
|
||||||
|
<unsubscribe>Owasp-webgoat-request@lists.owasp.org</unsubscribe>
|
||||||
|
<post>owasp-webgoat@lists.owasp.org</post>
|
||||||
|
<archive>http://lists.owasp.org/pipermail/owasp-webgoat/</archive>
|
||||||
|
</mailingList>
|
||||||
|
</mailingLists>
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
|
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
|
||||||
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
|
||||||
@ -62,56 +109,61 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Shared properties with plugins and version numbers across submodules-->
|
<!-- Shared properties with plugins and version numbers across submodules-->
|
||||||
<asciidoctorj.version>3.0.0</asciidoctorj.version>
|
<asciidoctorj.version>2.5.10</asciidoctorj.version>
|
||||||
<bootstrap.version>5.3.3</bootstrap.version>
|
<bootstrap.version>5.3.2</bootstrap.version>
|
||||||
<cglib.version>3.3.0</cglib.version>
|
<cglib.version>3.3.0</cglib.version>
|
||||||
<!-- do not update necessary for lesson -->
|
<!-- do not update necessary for lesson -->
|
||||||
<checkstyle.version>3.4.0</checkstyle.version>
|
<checkstyle.version>3.3.1</checkstyle.version>
|
||||||
<commons-collections.version>3.2.1</commons-collections.version>
|
<commons-collections.version>3.2.1</commons-collections.version>
|
||||||
<commons-compress.version>1.27.1</commons-compress.version>
|
<commons-io.version>2.15.1</commons-io.version>
|
||||||
<commons-io.version>2.16.1</commons-io.version>
|
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
<commons-text.version>1.10.0</commons-text.version>
|
||||||
<commons-text.version>1.12.0</commons-text.version>
|
<guava.version>32.1.3-jre</guava.version>
|
||||||
<guava.version>33.3.0-jre</guava.version>
|
|
||||||
<jacoco.version>0.8.11</jacoco.version>
|
<jacoco.version>0.8.11</jacoco.version>
|
||||||
<java.version>21</java.version>
|
<java.version>17</java.version>
|
||||||
<jaxb.version>2.3.1</jaxb.version>
|
<jaxb.version>2.3.1</jaxb.version>
|
||||||
<jjwt.version>0.9.1</jjwt.version>
|
<jjwt.version>0.9.1</jjwt.version>
|
||||||
<jose4j.version>0.9.3</jose4j.version>
|
<jose4j.version>0.9.3</jose4j.version>
|
||||||
<jquery.version>3.7.1</jquery.version>
|
<jquery.version>3.7.0</jquery.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.16.1</jsoup.version>
|
||||||
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
|
||||||
<maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version>
|
<maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version>
|
||||||
<maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>
|
||||||
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
|
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
|
||||||
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
|
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
|
||||||
<maven-surefire-plugin.version>3.5.1</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
<pmd.version>3.15.0</pmd.version>
|
<pmd.version>3.15.0</pmd.version>
|
||||||
<!-- Use UTF-8 Encoding -->
|
<!-- Use UTF-8 Encoding -->
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
|
<thymeleaf.version>3.1.1.RELEASE</thymeleaf.version>
|
||||||
<waittimeForServerStart>30</waittimeForServerStart>
|
<webdriver.version>5.3.3</webdriver.version>
|
||||||
<webdriver.version>5.9.2</webdriver.version>
|
|
||||||
<webgoat.context>/</webgoat.context>
|
<webgoat.context>/</webgoat.context>
|
||||||
<webgoat.sslenabled>false</webgoat.sslenabled>
|
<webgoat.sslenabled>false</webgoat.sslenabled>
|
||||||
<webjars-locator-core.version>0.59</webjars-locator-core.version>
|
<webjars-locator-core.version>0.53</webjars-locator-core.version>
|
||||||
<webwolf.context>/</webwolf.context>
|
<webwolf.context>/</webwolf.context>
|
||||||
<wiremock.version>3.9.1</wiremock.version>
|
<wiremock.version>2.27.2</wiremock.version>
|
||||||
<xml-resolver.version>1.2</xml-resolver.version>
|
<xml-resolver.version>1.2</xml-resolver.version>
|
||||||
<xstream.version>1.4.5</xstream.version>
|
<xstream.version>1.4.5</xstream.version>
|
||||||
<!-- do not update necessary for lesson -->
|
<!-- do not update necessary for lesson -->
|
||||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
<zxcvbn.version>1.8.0</zxcvbn.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ow2.asm</groupId>
|
||||||
|
<artifactId>asm</artifactId>
|
||||||
|
<version>9.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-exec</artifactId>
|
<artifactId>commons-exec</artifactId>
|
||||||
<version>1.4.0</version>
|
<version>1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.asciidoctor</groupId>
|
<groupId>org.asciidoctor</groupId>
|
||||||
@ -196,8 +248,8 @@
|
|||||||
<version>${webjars-locator-core.version}</version>
|
<version>${webjars-locator-core.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.wiremock</groupId>
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
<artifactId>wiremock-standalone</artifactId>
|
<artifactId>wiremock</artifactId>
|
||||||
<version>${wiremock.version}</version>
|
<version>${wiremock.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -208,12 +260,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>${commons-compress.version}</version>
|
<version>1.25.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jruby</groupId>
|
<groupId>org.jruby</groupId>
|
||||||
<artifactId>jruby</artifactId>
|
<artifactId>jruby</artifactId>
|
||||||
<version>9.4.8.0</version>
|
<version>9.4.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
@ -232,26 +284,24 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>javax.xml.bind</groupId>
|
||||||
<artifactId>jaxb-api</artifactId>
|
<artifactId>jaxb-api</artifactId>
|
||||||
<version>${jaxb.version}</version>
|
<version>${jaxb.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -261,10 +311,6 @@
|
|||||||
<groupId>org.flywaydb</groupId>
|
<groupId>org.flywaydb</groupId>
|
||||||
<artifactId>flyway-core</artifactId>
|
<artifactId>flyway-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.flywaydb</groupId>
|
|
||||||
<artifactId>flyway-database-hsqldb</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.asciidoctor</groupId>
|
<groupId>org.asciidoctor</groupId>
|
||||||
<artifactId>asciidoctorj</artifactId>
|
<artifactId>asciidoctorj</artifactId>
|
||||||
@ -371,12 +417,6 @@
|
|||||||
<artifactId>jaxb-impl</artifactId>
|
<artifactId>jaxb-impl</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.terma</groupId>
|
|
||||||
<artifactId>javaniotcpproxy</artifactId>
|
|
||||||
<version>1.5</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -389,8 +429,10 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.wiremock</groupId>
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
<artifactId>wiremock-standalone</artifactId>
|
<artifactId>wiremock</artifactId>
|
||||||
|
<version>3.0.0-beta-2</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.rest-assured</groupId>
|
<groupId>io.rest-assured</groupId>
|
||||||
@ -506,7 +548,6 @@
|
|||||||
<version>${maven-surefire-plugin.version}</version>
|
<version>${maven-surefire-plugin.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds>
|
<forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds>
|
||||||
<!-- Necessary for vulnerable components lesson -->
|
|
||||||
<argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
|
<argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
|
||||||
--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
|
--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
|
||||||
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
|
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
|
||||||
@ -514,6 +555,8 @@
|
|||||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
|
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>**/*IntegrationTest.java</exclude>
|
<exclude>**/*IntegrationTest.java</exclude>
|
||||||
|
<exclude>src/it/java</exclude>
|
||||||
|
<exclude>org/owasp/webgoat/*Test</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@ -593,7 +636,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-enforcer-plugin</artifactId>
|
<artifactId>maven-enforcer-plugin</artifactId>
|
||||||
<version>3.5.0</version>
|
<version>3.3.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>restrict-log4j-versions</id>
|
<id>restrict-log4j-versions</id>
|
||||||
@ -617,6 +660,10 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>17</source>
|
||||||
|
<target>17</target>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
@ -688,18 +735,24 @@
|
|||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
<argument>java.base/java.lang.reflect=ALL-UNNAMED</argument>
|
<argument>java.base/java.lang.reflect=ALL-UNNAMED</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
|
<argument>java.base/java.text=ALL-UNNAMED</argument>
|
||||||
|
<argument>--add-opens</argument>
|
||||||
<argument>java.desktop/java.beans=ALL-UNNAMED</argument>
|
<argument>java.desktop/java.beans=ALL-UNNAMED</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
|
<argument>java.desktop/java.awt.font=ALL-UNNAMED</argument>
|
||||||
|
<argument>--add-opens</argument>
|
||||||
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
|
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
<argument>java.base/java.io=ALL-UNNAMED</argument>
|
<argument>java.base/java.io=ALL-UNNAMED</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
<argument>java.base/java.util=ALL-UNNAMED</argument>
|
<argument>java.base/java.util=ALL-UNNAMED</argument>
|
||||||
|
<argument>--add-opens</argument>
|
||||||
|
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
|
||||||
|
<argument>--add-opens</argument>
|
||||||
|
<argument>java.base/java.io=ALL-UNNAMED</argument>
|
||||||
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
|
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
|
||||||
</arguments>
|
</arguments>
|
||||||
<waitForInterrupt>false</waitForInterrupt>
|
<waitForInterrupt>false</waitForInterrupt>
|
||||||
<waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
|
|
||||||
<healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}login</healthCheckUrl>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
|
@ -15,7 +15,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
assignment2();
|
assignment2();
|
||||||
assignment3();
|
assignment3();
|
||||||
|
|
||||||
checkResults("MissingFunctionAC");
|
checkResults("/access-control");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment3() {
|
private void assignment3() {
|
||||||
|
@ -86,7 +86,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
// logout();
|
// logout();
|
||||||
login(); // because old cookie got replaced and invalidated
|
login(); // because old cookie got replaced and invalidated
|
||||||
startLesson("CSRF", false);
|
startLesson("CSRF", false);
|
||||||
checkResults("CSRF");
|
checkResults("/csrf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
||||||
@ -103,7 +103,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.multiPart("file", htmlName, htmlContent.getBytes())
|
.multiPart("file", htmlName, htmlContent.getBytes())
|
||||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
.post(webWolfUrl("fileupload"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -118,7 +118,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("files/%s/%s", this.getUser(), htmlName).build())
|
.get(webWolfUrl("files/" + this.getUser() + "/" + htmlName))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -136,7 +136,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
.header("Referer", webWolfUrl("files/fake.html"))
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
@ -163,7 +163,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
.header("Referer", webWolfUrl("files/fake.html"))
|
||||||
.formParams(params)
|
.formParams(params)
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
@ -184,7 +184,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
.header("Referer", webWolfUrl("files/fake.html"))
|
||||||
.contentType(ContentType.TEXT)
|
.contentType(ContentType.TEXT)
|
||||||
.body(
|
.body(
|
||||||
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
|
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
|
||||||
@ -217,7 +217,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
.header("Referer", webWolfUrl("files/fake.html"))
|
||||||
.params(params)
|
.params(params)
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
@ -254,15 +254,15 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.get(url("service/lessonoverview.mvc/CSRF"))
|
.get(url("service/lessonoverview.mvc"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.jsonPath()
|
.jsonPath()
|
||||||
.getObject("$", Overview[].class);
|
.getObject("$", Overview[].class);
|
||||||
assertThat(assignments)
|
// assertThat(assignments)
|
||||||
.filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||||
.extracting(o -> o.solved)
|
// .extracting(o -> o.solved)
|
||||||
.containsExactly(true);
|
// .containsExactly(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@ -50,9 +50,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("flag", flag);
|
params.put("flag", flag);
|
||||||
checkAssignment(url("challenge/flag/1"), params, true);
|
checkAssignment(url("challenge/flag"), params, true);
|
||||||
|
|
||||||
checkResults("Challenge1");
|
checkResults("/challenge/1");
|
||||||
|
|
||||||
List<String> capturefFlags =
|
List<String> capturefFlags =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -92,9 +92,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("flag", flag);
|
params.put("flag", flag);
|
||||||
checkAssignment(url("challenge/flag/5"), params, true);
|
checkAssignment(url("challenge/flag"), params, true);
|
||||||
|
|
||||||
checkResults("Challenge5");
|
checkResults("/challenge/5");
|
||||||
|
|
||||||
List<String> capturefFlags =
|
List<String> capturefFlags =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -126,7 +126,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.extract()
|
.extract()
|
||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
// Should email WebWolf inbox this should give a hint to the link being static
|
// Should send an email to WebWolf inbox this should give a hint to the link being static
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -144,7 +144,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
.get(webWolfUrl("mail"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -165,6 +165,6 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||||
checkAssignment(url("challenge/flag/7"), Map.of("flag", flag), true);
|
checkAssignment(url("challenge/flag"), Map.of("flag", flag), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
checkAssignmentDefaults();
|
checkAssignmentDefaults();
|
||||||
|
|
||||||
checkResults("Cryptography");
|
checkResults("/crypto");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAssignment2() {
|
private void checkAssignment2() {
|
||||||
|
@ -28,6 +28,6 @@ public class DeserializationIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
checkAssignment(url("InsecureDeserialization/task"), params, true);
|
checkAssignment(url("InsecureDeserialization/task"), params, true);
|
||||||
|
|
||||||
checkResults("InsecureDeserialization");
|
checkResults("/InsecureDeserialization/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,17 +31,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("magic_num", "33");
|
params.put("magic_num", "33");
|
||||||
checkAssignment(url("HttpBasics/attack2"), params, true);
|
checkAssignment(url("HttpBasics/attack2"), params, true);
|
||||||
|
|
||||||
checkResults("HttpBasics");
|
checkResults("/HttpBasics/");
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void solveAsOtherUserHttpBasics() {
|
|
||||||
login("steven");
|
|
||||||
startLesson("HttpBasics");
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
|
||||||
params.clear();
|
|
||||||
params.put("person", "goatuser");
|
|
||||||
checkAssignment(url("HttpBasics/attack1"), params, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -61,7 +51,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
.path("lessonCompleted"),
|
.path("lessonCompleted"),
|
||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
|
|
||||||
checkResults("HttpProxies");
|
checkResults("/HttpProxies/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -83,7 +73,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
"question_3_solution",
|
"question_3_solution",
|
||||||
"Solution 2: The systems security is compromised even if only one goal is harmed.");
|
"Solution 2: The systems security is compromised even if only one goal is harmed.");
|
||||||
checkAssignment(url("cia/quiz"), params, true);
|
checkAssignment(url("cia/quiz"), params, true);
|
||||||
checkResults("CIA");
|
checkResults("/cia/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -106,7 +96,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
params.put("payload", solution);
|
params.put("payload", solution);
|
||||||
checkAssignment(url("VulnerableComponents/attack1"), params, true);
|
checkAssignment(url("VulnerableComponents/attack1"), params, true);
|
||||||
checkResults("VulnerableComponents");
|
checkResults("/VulnerableComponents/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +108,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("username", "CaptainJack");
|
params.put("username", "CaptainJack");
|
||||||
params.put("password", "BlackPearl");
|
params.put("password", "BlackPearl");
|
||||||
checkAssignment(url("InsecureLogin/task"), params, true);
|
checkAssignment(url("InsecureLogin/task"), params, true);
|
||||||
checkResults("InsecureLogin");
|
checkResults("/InsecureLogin/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -128,7 +118,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
params.put("password", "ajnaeliclm^&&@kjn.");
|
params.put("password", "ajnaeliclm^&&@kjn.");
|
||||||
checkAssignment(url("SecurePasswords/assignment"), params, true);
|
checkAssignment(url("SecurePasswords/assignment"), params, true);
|
||||||
checkResults("SecurePasswords");
|
checkResults("SecurePasswords/");
|
||||||
|
|
||||||
startLesson("AuthBypass");
|
startLesson("AuthBypass");
|
||||||
params.clear();
|
params.clear();
|
||||||
@ -138,7 +128,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("verifyMethod", "SEC_QUESTIONS");
|
params.put("verifyMethod", "SEC_QUESTIONS");
|
||||||
params.put("userId", "12309746");
|
params.put("userId", "12309746");
|
||||||
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
||||||
checkResults("AuthBypass");
|
checkResults("/auth-bypass/");
|
||||||
|
|
||||||
startLesson("HttpProxies");
|
startLesson("HttpProxies");
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
@ -154,7 +144,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
.extract()
|
.extract()
|
||||||
.path("lessonCompleted"),
|
.path("lessonCompleted"),
|
||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
checkResults("HttpProxies");
|
checkResults("/HttpProxies/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -190,7 +180,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("network_num", "24");
|
params.put("network_num", "24");
|
||||||
checkAssignment(url("ChromeDevTools/network"), params, true);
|
checkAssignment(url("ChromeDevTools/network"), params, true);
|
||||||
|
|
||||||
checkResults("ChromeDevTools");
|
checkResults("/ChromeDevTools/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -204,7 +194,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("verifyMethod", "SEC_QUESTIONS");
|
params.put("verifyMethod", "SEC_QUESTIONS");
|
||||||
params.put("userId", "12309746");
|
params.put("userId", "12309746");
|
||||||
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
checkAssignment(url("auth-bypass/verify-account"), params, true);
|
||||||
checkResults("AuthBypass");
|
checkResults("/auth-bypass/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -215,6 +205,6 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.put("param1", "secr37Value");
|
params.put("param1", "secr37Value");
|
||||||
params.put("param2", "Main");
|
params.put("param2", "Main");
|
||||||
checkAssignment(url("lesson-template/sample-attack"), params, true);
|
checkAssignment(url("lesson-template/sample-attack"), params, true);
|
||||||
checkResults("LessonTemplate");
|
checkResults("/lesson-template/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
checkResults("IDOR");
|
checkResults("/IDOR");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loginIDOR() {
|
private void loginIDOR() {
|
||||||
|
@ -3,7 +3,6 @@ package org.owasp.webgoat;
|
|||||||
import static io.restassured.RestAssured.given;
|
import static io.restassured.RestAssured.given;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.filter.log.LogDetail;
|
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -16,80 +15,43 @@ import org.springframework.http.HttpStatus;
|
|||||||
public abstract class IntegrationTest {
|
public abstract class IntegrationTest {
|
||||||
|
|
||||||
private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
|
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 webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
|
private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
|
||||||
|
|
||||||
private static String webGoatContext =
|
@Getter
|
||||||
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
|
private static String webGoatHost = System.getenv().getOrDefault("WEBGOAT_HOST", "127.0.0.1");
|
||||||
|
|
||||||
private static String webWolfContext =
|
private static String webWolfContext =
|
||||||
System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
|
System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
|
||||||
|
|
||||||
|
private static boolean useSSL =
|
||||||
|
Boolean.valueOf(System.getenv().getOrDefault("WEBGOAT_SSLENABLED", "false"));
|
||||||
|
private static String webgoatUrl =
|
||||||
|
(useSSL ? "https://" : "http://") + webGoatHost + ":" + webGoatPort + webGoatContext;
|
||||||
|
private static String webWolfUrl = "http://" + webWolfHost + ":" + webWolfPort + webWolfContext;
|
||||||
@Getter private String webGoatCookie;
|
@Getter private String webGoatCookie;
|
||||||
@Getter private String webWolfCookie;
|
@Getter private String webWolfCookie;
|
||||||
@Getter private final String user = "webgoat";
|
@Getter private final String user = "webgoat";
|
||||||
|
|
||||||
protected String url(String url) {
|
protected String url(String url) {
|
||||||
return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url);
|
return webgoatUrl + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class WebWolfUrlBuilder {
|
protected String webWolfUrl(String url) {
|
||||||
|
return webWolfUrl + url;
|
||||||
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) {
|
||||||
* In attack mode it means WebGoat calls WebWolf to perform an attack. In this case we need to
|
return webWolfUrl("files") + "/" + getUser() + "/" + fileName;
|
||||||
* 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
|
@BeforeEach
|
||||||
public void login() {
|
public void login() {
|
||||||
login("webgoat");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void login(String user) {
|
|
||||||
String location =
|
String location =
|
||||||
given()
|
given()
|
||||||
.when()
|
.when()
|
||||||
@ -98,8 +60,6 @@ public abstract class IntegrationTest {
|
|||||||
.formParam("password", "password")
|
.formParam("password", "password")
|
||||||
.post(url("login"))
|
.post(url("login"))
|
||||||
.then()
|
.then()
|
||||||
.log()
|
|
||||||
.ifValidationFails(LogDetail.ALL) // Log the response details if validation fails
|
|
||||||
.cookie("JSESSIONID")
|
.cookie("JSESSIONID")
|
||||||
.statusCode(302)
|
.statusCode(302)
|
||||||
.extract()
|
.extract()
|
||||||
@ -140,7 +100,7 @@ public abstract class IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
.formParam("password", "password")
|
.formParam("password", "password")
|
||||||
.post(new WebWolfUrlBuilder().path("login").build())
|
.post(webWolfUrl("login"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(302)
|
.statusCode(302)
|
||||||
.cookie("WEBWOLFSESSION")
|
.cookie("WEBWOLFSESSION")
|
||||||
@ -171,7 +131,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName)))
|
.get(url("service/restartlesson.mvc"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
}
|
}
|
||||||
@ -207,18 +167,23 @@ public abstract class IntegrationTest {
|
|||||||
CoreMatchers.is(expectedResult));
|
CoreMatchers.is(expectedResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkResults(String lesson) {
|
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
|
||||||
var result =
|
// only required to be in the same package)
|
||||||
|
public void checkResults(String prefix) {
|
||||||
|
checkResults();
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson)))
|
.get(url("service/lessonoverview.mvc"))
|
||||||
.andReturn();
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
MatcherAssert.assertThat(
|
.extract()
|
||||||
result.then().statusCode(200).extract().jsonPath().getList("solved"),
|
.jsonPath()
|
||||||
CoreMatchers.everyItem(CoreMatchers.is(true)));
|
.getList("assignment.path"),
|
||||||
|
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkResults() {
|
public void checkResults() {
|
||||||
@ -273,7 +238,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("file-server-location").build())
|
.get(webWolfUrl("file-server-location"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -301,7 +266,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.delete(new WebWolfUrlBuilder().path("mail").build())
|
.delete(webWolfUrl("mail"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.ACCEPTED.value());
|
.statusCode(HttpStatus.ACCEPTED.value());
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.jsonwebtoken.impl.TextCodec;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@ -33,7 +34,7 @@ import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
|
|||||||
public class JWTLessonIntegrationTest extends IntegrationTest {
|
public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void solveAssignment() throws IOException, NoSuchAlgorithmException {
|
public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
||||||
startLesson("JWT");
|
startLesson("JWT");
|
||||||
|
|
||||||
decodingToken();
|
decodingToken();
|
||||||
@ -50,10 +51,11 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
quiz();
|
quiz();
|
||||||
|
|
||||||
checkResults("JWT");
|
checkResults("/JWT/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateToken(String key) {
|
private String generateToken(String key) {
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setIssuer("WebGoat Token Builder")
|
.setIssuer("WebGoat Token Builder")
|
||||||
.setAudience("webgoat.org")
|
.setAudience("webgoat.org")
|
||||||
@ -94,7 +96,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPassword() {
|
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
|
||||||
String accessToken =
|
String accessToken =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -254,7 +256,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.multiPart("file", "jwks.json", jwks.toJson().getBytes())
|
.multiPart("file", "jwks.json", jwks.toJson().getBytes())
|
||||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
.post(webWolfUrl("fileupload"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -263,10 +265,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
Map<String, Object> header = new HashMap();
|
Map<String, Object> header = new HashMap();
|
||||||
header.put(Header.TYPE, Header.JWT_TYPE);
|
header.put(Header.TYPE, Header.JWT_TYPE);
|
||||||
header.put(
|
header.put(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json"));
|
||||||
JwsHeader.JWK_SET_URL,
|
|
||||||
new WebWolfUrlBuilder().attackMode().path("files/%s/jwks.json", getUser()).build());
|
|
||||||
|
|
||||||
String token =
|
String token =
|
||||||
Jwts.builder()
|
Jwts.builder()
|
||||||
.setHeader(header)
|
.setHeader(header)
|
||||||
|
@ -151,6 +151,7 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
|||||||
checkLang(propsDefault, "nl");
|
checkLang(propsDefault, "nl");
|
||||||
checkLang(propsDefault, "de");
|
checkLang(propsDefault, "de");
|
||||||
checkLang(propsDefault, "fr");
|
checkLang(propsDefault, "fr");
|
||||||
|
checkLang(propsDefault, "ru");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Properties getProperties(String lang) {
|
private Properties getProperties(String lang) {
|
||||||
|
@ -85,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
.get(webWolfUrl("mail"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -99,7 +99,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||||
// lesson is marked complete
|
// lesson is marked complete
|
||||||
checkResults("PasswordReset");
|
checkResults("/PasswordReset");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changePassword(String link) {
|
private void changePassword(String link) {
|
||||||
@ -119,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
.get(webWolfUrl("requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
|
@ -147,6 +147,6 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
void shutdown() {
|
void shutdown() {
|
||||||
// this will run only once after the list of dynamic tests has run, this is to test if the
|
// this will run only once after the list of dynamic tests has run, this is to test if the
|
||||||
// lesson is marked complete
|
// lesson is marked complete
|
||||||
checkResults("PathTraversal");
|
checkResults("/PathTraversal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.formParams(Map.of("flag", "test"))
|
.formParams(Map.of("flag", "test"))
|
||||||
.post(url("challenge/flag/1"));
|
.post(url("challenge/flag"));
|
||||||
};
|
};
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS);
|
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS);
|
||||||
List<? extends Callable<Response>> flagCalls =
|
List<? extends Callable<Response>> flagCalls =
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -7,7 +8,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
public class SSRFIntegrationTest extends IntegrationTest {
|
public class SSRFIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void runTests() {
|
public void runTests() throws IOException {
|
||||||
startLesson("SSRF");
|
startLesson("SSRF");
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
@ -20,6 +21,6 @@ public class SSRFIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
checkAssignment(url("SSRF/task2"), params, true);
|
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;--'.");
|
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
||||||
checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true);
|
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);
|
params.put("action_string", sql_13);
|
||||||
checkAssignment(url("SqlInjection/attack10"), params, true);
|
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");
|
params.put("ip", "104.130.219.202");
|
||||||
checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true);
|
checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true);
|
||||||
|
|
||||||
checkResults("SqlInjectionMitigations");
|
checkResults();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("mail").build())
|
.get(webWolfUrl("mail"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -53,7 +53,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.queryParams(params)
|
.queryParams(params)
|
||||||
.get(new WebWolfUrlBuilder().path("landing").build())
|
.get(webWolfUrl("landing"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
responseBody =
|
responseBody =
|
||||||
@ -61,7 +61,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
.get(webWolfUrl("requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -72,6 +72,6 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
params.put("uniqueCode", uniqueCode);
|
params.put("uniqueCode", uniqueCode);
|
||||||
checkAssignment(url("WebWolf/landing"), params, true);
|
checkAssignment(url("WebWolf/landing"), params, true);
|
||||||
|
|
||||||
checkResults("WebWolfIntroduction");
|
checkResults("/WebWolf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,6 @@ public class XSSIntegrationTest extends IntegrationTest {
|
|||||||
+ "MyCommentDAO.addComment(threadID, userID).getCleanHTML());");
|
+ "MyCommentDAO.addComment(threadID, userID).getCleanHTML());");
|
||||||
checkAssignment(url("CrossSiteScripting/attack4"), params, true);
|
checkAssignment(url("CrossSiteScripting/attack4"), params, true);
|
||||||
|
|
||||||
checkResults("CrossSiteScripting");
|
checkResults("/CrossSiteScripting");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ package org.owasp.webgoat;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import java.io.IOException;
|
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;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class XXEIntegrationTest extends IntegrationTest {
|
public class XXEIntegrationTest extends IntegrationTest {
|
||||||
@ -25,40 +28,47 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
private String webGoatHomeDirectory;
|
private String webGoatHomeDirectory;
|
||||||
|
private String webWolfFileServerLocation;
|
||||||
|
|
||||||
// TODO fix me
|
/*
|
||||||
// /*
|
* This test is to verify that all is secure when XXE security patch is applied.
|
||||||
// * This test is to verify that all is secure when XXE security patch is applied.
|
*/
|
||||||
// */
|
@Test
|
||||||
// @Test
|
public void xxeSecure() throws IOException {
|
||||||
// public void xxeSecure() throws IOException {
|
startLesson("XXE");
|
||||||
// startLesson("XXE");
|
webGoatHomeDirectory = webGoatServerDirectory();
|
||||||
// webGoatHomeDirectory = webGoatServerDirectory();
|
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
||||||
// RestAssured.given()
|
RestAssured.given()
|
||||||
// .when()
|
.when()
|
||||||
// .relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
// .cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
// .get(url("service/enable-security.mvc"))
|
.get(url("service/enable-security.mvc"))
|
||||||
// .then()
|
.then()
|
||||||
// .statusCode(200);
|
.statusCode(200);
|
||||||
// checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false);
|
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false);
|
||||||
// checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false);
|
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false);
|
||||||
// checkAssignment(
|
checkAssignment(
|
||||||
// url("xxe/blind"),
|
url("xxe/blind"),
|
||||||
// ContentType.XML,
|
ContentType.XML,
|
||||||
// "<comment><text>" + getSecret() + "</text></comment>",
|
"<comment><text>" + getSecret() + "</text></comment>",
|
||||||
// false);
|
false);
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This performs the steps of the exercise before the secret can be committed in the final step.
|
* This performs the steps of the exercise before the secret can be committed in the final step.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private String getSecret() {
|
private String getSecret() throws IOException {
|
||||||
|
// remove any left over DTD
|
||||||
|
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
|
||||||
|
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
|
||||||
|
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
|
||||||
|
}
|
||||||
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
||||||
String webWolfCallback = new WebWolfUrlBuilder().path("landing").attackMode().build();
|
String dtd7String =
|
||||||
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile);
|
dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile);
|
||||||
|
|
||||||
// upload DTD
|
// upload DTD
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -66,17 +76,15 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
||||||
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
.post(webWolfUrl("fileupload"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
.getBody()
|
.getBody()
|
||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
// upload attack
|
// upload attack
|
||||||
String xxe7String =
|
String xxe7String =
|
||||||
xxe7.replace("WEBWOLFURL", new WebWolfUrlBuilder().attackMode().path("files").build())
|
xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser());
|
||||||
.replace("USERNAME", this.getUser());
|
|
||||||
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
|
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
|
||||||
|
|
||||||
// read results from WebWolf
|
// read results from WebWolf
|
||||||
@ -85,7 +93,7 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(new WebWolfUrlBuilder().path("requests").build())
|
.get(webWolfUrl("requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -105,6 +113,7 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
public void runTests() throws IOException {
|
public void runTests() throws IOException {
|
||||||
startLesson("XXE", true);
|
startLesson("XXE", true);
|
||||||
webGoatHomeDirectory = webGoatServerDirectory();
|
webGoatHomeDirectory = webGoatServerDirectory();
|
||||||
|
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
||||||
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true);
|
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true);
|
||||||
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true);
|
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true);
|
||||||
checkAssignment(
|
checkAssignment(
|
||||||
@ -112,6 +121,6 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
ContentType.XML,
|
ContentType.XML,
|
||||||
"<comment><text>" + getSecret() + "</text></comment>",
|
"<comment><text>" + getSecret() + "</text></comment>",
|
||||||
true);
|
true);
|
||||||
checkResults("XXE");
|
checkResults("xxe/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,23 +32,22 @@ package org.owasp.webgoat.container;
|
|||||||
|
|
||||||
import static org.asciidoctor.Asciidoctor.Factory.create;
|
import static org.asciidoctor.Asciidoctor.Factory.create;
|
||||||
|
|
||||||
|
import io.undertow.util.Headers;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.asciidoctor.Asciidoctor;
|
import org.asciidoctor.Asciidoctor;
|
||||||
import org.asciidoctor.Attributes;
|
|
||||||
import org.asciidoctor.Options;
|
|
||||||
import org.asciidoctor.extension.JavaExtensionRegistry;
|
import org.asciidoctor.extension.JavaExtensionRegistry;
|
||||||
import org.owasp.webgoat.container.asciidoc.*;
|
import org.owasp.webgoat.container.asciidoc.*;
|
||||||
import org.owasp.webgoat.container.i18n.Language;
|
import org.owasp.webgoat.container.i18n.Language;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
@ -136,17 +135,17 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
|||||||
return computedResourceName;
|
return computedResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Options createAttributes() {
|
private Map<String, Object> createAttributes() {
|
||||||
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
|
attributes.put("source-highlighter", "coderay");
|
||||||
|
attributes.put("backend", "xhtml");
|
||||||
|
attributes.put("lang", determineLanguage());
|
||||||
|
attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS);
|
||||||
|
|
||||||
return Options.builder()
|
Map<String, Object> options = new HashMap<>();
|
||||||
.attributes(
|
options.put("attributes", attributes);
|
||||||
Attributes.builder()
|
|
||||||
.attribute("source-highlighter", "coderay")
|
return options;
|
||||||
.attribute("backend", "xhtml")
|
|
||||||
.attribute("lang", determineLanguage())
|
|
||||||
.attribute("icons", org.asciidoctor.Attributes.FONT_ICONS)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String determineLanguage() {
|
private String determineLanguage() {
|
||||||
@ -160,7 +159,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
|||||||
log.debug("browser locale {}", browserLocale);
|
log.debug("browser locale {}", browserLocale);
|
||||||
return browserLocale.getLanguage();
|
return browserLocale.getLanguage();
|
||||||
} else {
|
} else {
|
||||||
String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
|
String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING);
|
||||||
if (null != langHeader) {
|
if (null != langHeader) {
|
||||||
log.debug("browser locale {}", langHeader);
|
log.debug("browser locale {}", langHeader);
|
||||||
return langHeader.substring(0, 2);
|
return langHeader.substring(0, 2);
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
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 {}
|
|
@ -1,14 +0,0 @@
|
|||||||
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,7 +7,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
import org.owasp.webgoat.container.service.RestartLessonService;
|
import org.owasp.webgoat.container.service.RestartLessonService;
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -35,8 +34,8 @@ public class DatabaseConfiguration {
|
|||||||
/**
|
/**
|
||||||
* Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users
|
* Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users
|
||||||
* and 1 for lesson specific tables we use. This way we clean the data in the lesson database
|
* and 1 for lesson specific tables we use. This way we clean the data in the lesson database
|
||||||
* quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we
|
* quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson
|
||||||
* clean the lesson related tables.
|
* related tables.
|
||||||
*/
|
*/
|
||||||
@Bean(initMethod = "migrate")
|
@Bean(initMethod = "migrate")
|
||||||
public Flyway flyWayContainer() {
|
public Flyway flyWayContainer() {
|
||||||
@ -61,7 +60,7 @@ public class DatabaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LessonDataSource lessonDataSource(DataSource dataSource) {
|
public LessonDataSource lessonDataSource() {
|
||||||
return new LessonDataSource(dataSource);
|
return new LessonDataSource(dataSource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,33 +32,30 @@
|
|||||||
package org.owasp.webgoat.container;
|
package org.owasp.webgoat.container;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.users.UserRepository;
|
import org.owasp.webgoat.container.users.UserRepository;
|
||||||
|
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
|
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
|
||||||
@PropertySource("classpath:application-webgoat.properties")
|
@PropertySource("classpath:application-webgoat.properties")
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"})
|
|
||||||
@EntityScan(basePackages = "org.owasp.webgoat.container")
|
|
||||||
public class WebGoat {
|
public class WebGoat {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
@Autowired private UserRepository userRepository;
|
||||||
|
|
||||||
public WebGoat(UserRepository userRepository) {
|
|
||||||
this.userRepository = userRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "pluginTargetDirectory")
|
@Bean(name = "pluginTargetDirectory")
|
||||||
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
||||||
@ -67,8 +64,21 @@ public class WebGoat {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
public LessonSession userSessionData() {
|
public WebSession webSession() {
|
||||||
return new LessonSession();
|
WebGoatUser webGoatUser = null;
|
||||||
|
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
if (principal instanceof WebGoatUser) {
|
||||||
|
webGoatUser = (WebGoatUser) principal;
|
||||||
|
} else if (principal instanceof DefaultOAuth2User) {
|
||||||
|
webGoatUser = userRepository.findByUsername(((DefaultOAuth2User) principal).getName());
|
||||||
|
}
|
||||||
|
return new WebSession(webGoatUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
|
public UserSessionData userSessionData() {
|
||||||
|
return new UserSessionData("test", "data");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -35,7 +35,6 @@ import org.owasp.webgoat.container.users.UserService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||||
@ -58,6 +57,7 @@ public class WebSecurityConfig {
|
|||||||
return http.authorizeHttpRequests(
|
return http.authorizeHttpRequests(
|
||||||
auth ->
|
auth ->
|
||||||
auth.requestMatchers(
|
auth.requestMatchers(
|
||||||
|
"/",
|
||||||
"/favicon.ico",
|
"/favicon.ico",
|
||||||
"/css/**",
|
"/css/**",
|
||||||
"/images/**",
|
"/images/**",
|
||||||
@ -65,8 +65,7 @@ public class WebSecurityConfig {
|
|||||||
"fonts/**",
|
"fonts/**",
|
||||||
"/plugins/**",
|
"/plugins/**",
|
||||||
"/registration",
|
"/registration",
|
||||||
"/register.mvc",
|
"/register.mvc")
|
||||||
"/actuator/**")
|
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated())
|
.authenticated())
|
||||||
@ -98,7 +97,6 @@ public class WebSecurityConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
|
||||||
public UserDetailsService userDetailsServiceBean() {
|
public UserDetailsService userDetailsServiceBean() {
|
||||||
return userDetailsService;
|
return userDetailsService;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public class EnvironmentExposure implements ApplicationContextAware {
|
|||||||
private static ApplicationContext context;
|
private static ApplicationContext context;
|
||||||
|
|
||||||
public static Environment getEnv() {
|
public static Environment getEnv() {
|
||||||
return null != context ? context.getEnvironment() : null;
|
return (null != context) ? context.getEnvironment() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.PhraseNode;
|
import org.asciidoctor.ast.ContentNode;
|
||||||
import org.asciidoctor.ast.StructuralNode;
|
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class OperatingSystemMacro extends InlineMacroProcessor {
|
public class OperatingSystemMacro extends InlineMacroProcessor {
|
||||||
@ -16,8 +15,7 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PhraseNode process(
|
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
|
||||||
var osName = System.getProperty("os.name");
|
var osName = System.getProperty("os.name");
|
||||||
|
|
||||||
// see
|
// see
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.PhraseNode;
|
import org.asciidoctor.ast.ContentNode;
|
||||||
import org.asciidoctor.ast.StructuralNode;
|
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -18,8 +17,7 @@ public class UsernameMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PhraseNode process(
|
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
|
||||||
var auth = SecurityContextHolder.getContext().getAuthentication();
|
var auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
var username = "unknown";
|
var username = "unknown";
|
||||||
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {
|
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.PhraseNode;
|
import org.asciidoctor.ast.ContentNode;
|
||||||
import org.asciidoctor.ast.StructuralNode;
|
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
||||||
@ -16,12 +15,11 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PhraseNode process(
|
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
StructuralNode structuralNode, String target, Map<String, Object> attributes) {
|
|
||||||
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
|
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
|
||||||
|
|
||||||
// see
|
// see
|
||||||
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
|
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
|
||||||
return createPhraseNode(structuralNode, "quoted", env);
|
return createPhraseNode(contentNode, "quoted", env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.PhraseNode;
|
import org.asciidoctor.ast.ContentNode;
|
||||||
import org.asciidoctor.ast.StructuralNode;
|
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class WebGoatVersionMacro extends InlineMacroProcessor {
|
public class WebGoatVersionMacro extends InlineMacroProcessor {
|
||||||
@ -16,8 +15,7 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PhraseNode process(
|
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
|
||||||
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
|
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
|
||||||
|
|
||||||
// see
|
// see
|
||||||
|
@ -2,8 +2,7 @@ package org.owasp.webgoat.container.asciidoc;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.PhraseNode;
|
import org.asciidoctor.ast.ContentNode;
|
||||||
import org.asciidoctor.ast.StructuralNode;
|
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,8 +21,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PhraseNode process(
|
public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) {
|
||||||
StructuralNode contentNode, String linkText, Map<String, Object> attributes) {
|
|
||||||
var env = EnvironmentExposure.getEnv();
|
var env = EnvironmentExposure.getEnv();
|
||||||
var hostname = env.getProperty("webwolf.url");
|
var hostname = env.getProperty("webwolf.url");
|
||||||
var target = (String) attributes.getOrDefault("target", "home");
|
var target = (String) attributes.getOrDefault("target", "home");
|
||||||
@ -38,7 +36,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
|
|||||||
options.put("type", ":link");
|
options.put("type", ":link");
|
||||||
options.put("target", href);
|
options.put("target", href);
|
||||||
attributes.put("window", "_blank");
|
attributes.put("window", "_blank");
|
||||||
return createPhraseNode(contentNode, "anchor", linkText, attributes, options);
|
return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
|
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
|
||||||
|
@ -25,13 +25,27 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.assignments;
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
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;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
public abstract class AssignmentEndpoint {
|
public abstract class AssignmentEndpoint implements Initializeable {
|
||||||
|
|
||||||
// TODO: move this to different bean.
|
@Autowired private WebSession webSession;
|
||||||
@Autowired private PluginMessages messages;
|
@Autowired private UserSessionData userSessionData;
|
||||||
|
@Getter @Autowired private PluginMessages messages;
|
||||||
|
|
||||||
|
protected WebSession getWebSession() {
|
||||||
|
return webSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UserSessionData getUserSessionData() {
|
||||||
|
return userSessionData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method for create a successful result:
|
* Convenience method for create a successful result:
|
||||||
@ -72,4 +86,7 @@ public abstract class AssignmentEndpoint {
|
|||||||
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
||||||
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
|
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(WebGoatUser user) {}
|
||||||
}
|
}
|
||||||
|
@ -22,30 +22,27 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.assignments;
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.users.UserTracker;
|
||||||
import org.owasp.webgoat.container.users.UserProgress;
|
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.server.ServerHttpRequest;
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
import org.springframework.http.server.ServerHttpResponse;
|
import org.springframework.http.server.ServerHttpResponse;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||||
|
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
||||||
|
|
||||||
private final Course course;
|
private UserTrackerRepository userTrackerRepository;
|
||||||
private final UserProgressRepository userProgressRepository;
|
private WebSession webSession;
|
||||||
|
|
||||||
public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) {
|
public LessonTrackerInterceptor(
|
||||||
this.course = course;
|
UserTrackerRepository userTrackerRepository, WebSession webSession) {
|
||||||
this.userProgressRepository = userProgressRepository;
|
this.userTrackerRepository = userTrackerRepository;
|
||||||
|
this.webSession = webSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -68,30 +65,18 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void trackProgress(AttackResult attackResult) {
|
protected AttackResult trackProgress(AttackResult attackResult) {
|
||||||
var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||||
Assert.notNull(user, "User not found in SecurityContext");
|
if (userTracker == null) {
|
||||||
var username = realUsername(user);
|
userTracker = new UserTracker(webSession.getUserName());
|
||||||
|
|
||||||
var userProgress = userProgressRepository.findByUser(username);
|
|
||||||
if (userProgress == null) {
|
|
||||||
userProgress = new UserProgress(username);
|
|
||||||
}
|
}
|
||||||
Lesson lesson = course.getLessonByAssignment(attackResult.getAssignment());
|
|
||||||
Assert.notNull(lesson, "Lesson not found for assignment " + attackResult.getAssignment());
|
|
||||||
|
|
||||||
if (attackResult.assignmentSolved()) {
|
if (attackResult.assignmentSolved()) {
|
||||||
userProgress.assignmentSolved(lesson, attackResult.getAssignment());
|
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
|
||||||
} else {
|
} else {
|
||||||
userProgress.assignmentFailed(lesson);
|
userTracker.assignmentFailed(webSession.getCurrentLesson());
|
||||||
}
|
|
||||||
userProgressRepository.save(userProgress);
|
|
||||||
}
|
}
|
||||||
|
userTrackerRepository.save(userTracker);
|
||||||
|
|
||||||
private String realUsername(WebGoatUser user) {
|
return attackResult;
|
||||||
// maybe we shouldn't hard code this with just csrf- prefix for now it works
|
|
||||||
return user.getUsername().startsWith("csrf-")
|
|
||||||
? user.getUsername().substring("csrf-".length())
|
|
||||||
: user.getUsername();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,20 +33,42 @@ package org.owasp.webgoat.container.controller;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class StartLesson {
|
public class StartLesson {
|
||||||
|
|
||||||
|
private final WebSession ws;
|
||||||
private final Course course;
|
private final Course course;
|
||||||
|
|
||||||
public StartLesson(Course course) {
|
public StartLesson(WebSession ws, Course course) {
|
||||||
|
this.ws = ws;
|
||||||
this.course = course;
|
this.course = course;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(
|
/**
|
||||||
|
* start.
|
||||||
|
*
|
||||||
|
* @return a {@link ModelAndView} object.
|
||||||
|
*/
|
||||||
|
@RequestMapping(
|
||||||
|
path = "startlesson.mvc",
|
||||||
|
method = {RequestMethod.GET, RequestMethod.POST})
|
||||||
|
public ModelAndView start() {
|
||||||
|
var model = new ModelAndView();
|
||||||
|
|
||||||
|
model.addObject("course", course);
|
||||||
|
model.addObject("lesson", ws.getCurrentLesson());
|
||||||
|
model.setViewName("lesson_content");
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
value = {"*.lesson"},
|
value = {"*.lesson"},
|
||||||
produces = "text/html")
|
produces = "text/html")
|
||||||
public ModelAndView lessonPage(HttpServletRequest request) {
|
public ModelAndView lessonPage(HttpServletRequest request) {
|
||||||
@ -59,7 +81,8 @@ public class StartLesson {
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(
|
.ifPresent(
|
||||||
lesson -> {
|
lesson -> {
|
||||||
request.setAttribute("lesson", lesson);
|
ws.setCurrentLesson(lesson);
|
||||||
|
model.addObject("lesson", lesson);
|
||||||
});
|
});
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
@ -51,11 +51,10 @@ public class Assignment {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private String path;
|
private String path;
|
||||||
private boolean solved = false;
|
|
||||||
|
|
||||||
@Transient private List<String> hints;
|
@Transient private List<String> hints;
|
||||||
|
|
||||||
protected Assignment() {
|
private Assignment() {
|
||||||
// Hibernate
|
// Hibernate
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +74,4 @@ public class Assignment {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.hints = hints;
|
this.hints = hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void solved() {
|
|
||||||
this.solved = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -34,28 +34,30 @@ import lombok.Getter;
|
|||||||
* @since October 28, 2003
|
* @since October 28, 2003
|
||||||
*/
|
*/
|
||||||
public enum Category {
|
public enum Category {
|
||||||
INTRODUCTION("Introduction"),
|
INTRODUCTION("Introduction", 5),
|
||||||
GENERAL("General"),
|
GENERAL("General", 100),
|
||||||
|
|
||||||
A1("(A1) Broken Access Control"),
|
A1("(A1) Broken Access Control", 301),
|
||||||
A2("(A2) Cryptographic Failures"),
|
A2("(A2) Cryptographic Failures", 302),
|
||||||
A3("(A3) Injection"),
|
A3("(A3) Injection", 303),
|
||||||
|
|
||||||
A5("(A5) Security Misconfiguration"),
|
A5("(A5) Security Misconfiguration", 305),
|
||||||
A6("(A6) Vuln & Outdated Components"),
|
A6("(A6) Vuln & Outdated Components", 306),
|
||||||
A7("(A7) Identity & Auth Failure"),
|
A7("(A7) Identity & Auth Failure", 307),
|
||||||
A8("(A8) Software & Data Integrity"),
|
A8("(A8) Software & Data Integrity", 308),
|
||||||
A9("(A9) Security Logging Failures"),
|
A9("(A9) Security Logging Failures", 309),
|
||||||
A10("(A10) Server-side Request Forgery"),
|
A10("(A10) Server-side Request Forgery", 310),
|
||||||
|
|
||||||
CLIENT_SIDE("Client side"),
|
CLIENT_SIDE("Client side", 1700),
|
||||||
|
|
||||||
CHALLENGE("Challenges");
|
CHALLENGE("Challenges", 3000);
|
||||||
|
|
||||||
@Getter private String name;
|
@Getter private String name;
|
||||||
|
@Getter private Integer ranking;
|
||||||
|
|
||||||
Category(String name) {
|
Category(String name, Integer ranking) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.ranking = ranking;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,9 +22,12 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.lessons;
|
package org.owasp.webgoat.container.lessons;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
@ -32,91 +35,45 @@ import org.owasp.webgoat.container.assignments.AttackResult;
|
|||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
public class CourseConfiguration {
|
public class CourseConfiguration {
|
||||||
|
|
||||||
private final List<Lesson> lessons;
|
private final List<Lesson> lessons;
|
||||||
private final List<AssignmentEndpoint> assignments;
|
private final List<AssignmentEndpoint> assignments;
|
||||||
|
private final Map<String, List<AssignmentEndpoint>> assignmentsByPackage;
|
||||||
|
|
||||||
public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
|
public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
|
||||||
this.lessons = lessons;
|
this.lessons = lessons;
|
||||||
this.assignments = assignments;
|
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
|
@Bean
|
||||||
public Course course() {
|
public Course course() {
|
||||||
assignments.stream().forEach(this::attachToLesson);
|
lessons.stream().forEach(l -> l.setAssignments(createAssignment(l)));
|
||||||
|
|
||||||
// Check if all assignments are attached to a lesson
|
|
||||||
var assignmentsAttachedToLessons =
|
|
||||||
lessons.stream().mapToInt(l -> l.getAssignments().size()).sum();
|
|
||||||
Assert.isTrue(
|
|
||||||
assignmentsAttachedToLessons == assignments.size(),
|
|
||||||
"Not all assignments are attached to a lesson, please check the configuration. The"
|
|
||||||
+ " following assignments are not attached to any lesson: "
|
|
||||||
+ findDiff());
|
|
||||||
return new Course(lessons);
|
return new Course(lessons);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> findDiff() {
|
private List<Assignment> createAssignment(Lesson lesson) {
|
||||||
var matchedToLessons =
|
var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName());
|
||||||
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList();
|
if (CollectionUtils.isEmpty(endpoints)) {
|
||||||
var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList();
|
log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle());
|
||||||
|
return new ArrayList<>();
|
||||||
var diff = new ArrayList<>(allAssignments);
|
}
|
||||||
diff.removeAll(matchedToLessons);
|
return endpoints.stream()
|
||||||
return diff;
|
.map(
|
||||||
|
e ->
|
||||||
|
new Assignment(
|
||||||
|
e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass())))
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPath(Class<? extends AssignmentEndpoint> e) {
|
private String getPath(Class<? extends AssignmentEndpoint> e) {
|
||||||
|
@ -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
|
* 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.
|
* when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson.
|
||||||
*/
|
*/
|
||||||
public interface Initializable {
|
public interface Initializeable {
|
||||||
|
|
||||||
default void initialize(WebGoatUser webGoatUser) {}
|
void initialize(WebGoatUser webGoatUser);
|
||||||
}
|
}
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.lessons;
|
package org.owasp.webgoat.container.lessons;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@ -31,10 +30,13 @@ import lombok.Setter;
|
|||||||
@Setter
|
@Setter
|
||||||
public abstract class Lesson {
|
public abstract class Lesson {
|
||||||
|
|
||||||
private List<Assignment> assignments = new ArrayList<>();
|
private static int count = 1;
|
||||||
|
private Integer id = null;
|
||||||
|
private List<Assignment> assignments;
|
||||||
|
|
||||||
public void addAssignment(Assignment assignment) {
|
/** Constructor for the Lesson object */
|
||||||
this.assignments.add(assignment);
|
protected Lesson() {
|
||||||
|
id = ++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,9 +44,9 @@ public abstract class Lesson {
|
|||||||
*
|
*
|
||||||
* @return a {@link java.lang.String} object.
|
* @return a {@link java.lang.String} object.
|
||||||
*/
|
*/
|
||||||
public LessonName getName() {
|
public String getName() {
|
||||||
String className = getClass().getName();
|
String className = getClass().getName();
|
||||||
return new LessonName(className.substring(className.lastIndexOf('.') + 1));
|
return className.substring(className.lastIndexOf('.') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,10 +116,6 @@ public abstract class Lesson {
|
|||||||
return this.getClass().getSimpleName();
|
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() {
|
public final String getPackage() {
|
||||||
var packageName = this.getClass().getPackageName();
|
var packageName = this.getClass().getPackageName();
|
||||||
// package name is the direct package name below lessons (any subpackage will be removed)
|
// package name is the direct package name below lessons (any subpackage will be removed)
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.report;
|
|
||||||
|
|
||||||
record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
|
|
@ -1,88 +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.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,24 +10,26 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.owasp.webgoat.container.lessons.Assignment;
|
import org.owasp.webgoat.container.lessons.Assignment;
|
||||||
import org.owasp.webgoat.container.lessons.Hint;
|
import org.owasp.webgoat.container.lessons.Hint;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HintService class.
|
||||||
|
*
|
||||||
|
* @author rlawson
|
||||||
|
* @version $Id: $Id
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class HintService {
|
public class HintService {
|
||||||
|
|
||||||
public static final String URL_HINTS_MVC = "/service/hint.mvc";
|
public static final String URL_HINTS_MVC = "/service/hint.mvc";
|
||||||
private final List<Hint> allHints;
|
private final WebSession webSession;
|
||||||
|
|
||||||
public HintService(Course course) {
|
public HintService(WebSession webSession) {
|
||||||
this.allHints =
|
this.webSession = webSession;
|
||||||
course.getLessons().stream()
|
|
||||||
.flatMap(lesson -> lesson.getAssignments().stream())
|
|
||||||
.map(this::createHint)
|
|
||||||
.flatMap(Collection::stream)
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +40,15 @@ public class HintService {
|
|||||||
@GetMapping(path = URL_HINTS_MVC, produces = "application/json")
|
@GetMapping(path = URL_HINTS_MVC, produces = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<Hint> getHints() {
|
public List<Hint> getHints() {
|
||||||
return allHints;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Hint> createHint(Assignment a) {
|
private List<Hint> createHint(Assignment a) {
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
package org.owasp.webgoat.container.service;
|
package org.owasp.webgoat.container.service;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.owasp.webgoat.container.lessons.LessonInfoModel;
|
import org.owasp.webgoat.container.lessons.LessonInfoModel;
|
||||||
import org.owasp.webgoat.container.lessons.LessonName;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
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.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LessonInfoService class.
|
||||||
|
*
|
||||||
|
* @author dm
|
||||||
|
* @version $Id: $Id
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
@AllArgsConstructor
|
||||||
public class LessonInfoService {
|
public class LessonInfoService {
|
||||||
|
|
||||||
private final Course course;
|
private final WebSession webSession;
|
||||||
|
|
||||||
@GetMapping(path = "/service/lessoninfo.mvc/{lesson}")
|
/**
|
||||||
public @ResponseBody LessonInfoModel getLessonInfo(
|
* getLessonInfo.
|
||||||
@PathVariable("lesson") LessonName lessonName) {
|
*
|
||||||
var lesson = course.getLessonByName(lessonName);
|
* @return a {@link LessonInfoModel} object.
|
||||||
|
*/
|
||||||
|
@RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json")
|
||||||
|
public @ResponseBody LessonInfoModel getLessonInfo() {
|
||||||
|
Lesson lesson = webSession.getCurrentLesson();
|
||||||
return new LessonInfoModel(lesson.getTitle(), false, false, false);
|
return new LessonInfoModel(lesson.getTitle(), false, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,16 +32,16 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.lessons.Assignment;
|
import org.owasp.webgoat.container.lessons.Assignment;
|
||||||
import org.owasp.webgoat.container.lessons.Category;
|
import org.owasp.webgoat.container.lessons.Category;
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.owasp.webgoat.container.lessons.LessonMenuItem;
|
import org.owasp.webgoat.container.lessons.LessonMenuItem;
|
||||||
import org.owasp.webgoat.container.lessons.LessonMenuItemType;
|
import org.owasp.webgoat.container.lessons.LessonMenuItemType;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
import org.owasp.webgoat.container.users.LessonProgress;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.users.UserProgress;
|
import org.owasp.webgoat.container.users.LessonTracker;
|
||||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
import org.owasp.webgoat.container.users.UserTracker;
|
||||||
|
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@ -59,7 +59,8 @@ public class LessonMenuService {
|
|||||||
|
|
||||||
public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc";
|
public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc";
|
||||||
private final Course course;
|
private final Course course;
|
||||||
private UserProgressRepository userTrackerRepository;
|
private final WebSession webSession;
|
||||||
|
private UserTrackerRepository userTrackerRepository;
|
||||||
|
|
||||||
@Value("#{'${exclude.categories}'.split(',')}")
|
@Value("#{'${exclude.categories}'.split(',')}")
|
||||||
private List<String> excludeCategories;
|
private List<String> excludeCategories;
|
||||||
@ -73,13 +74,10 @@ public class LessonMenuService {
|
|||||||
* @return a {@link java.util.List} object.
|
* @return a {@link java.util.List} object.
|
||||||
*/
|
*/
|
||||||
@RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json")
|
@RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json")
|
||||||
public @ResponseBody List<LessonMenuItem> showLeftNav(@CurrentUsername String username) {
|
public @ResponseBody List<LessonMenuItem> showLeftNav() {
|
||||||
// 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<LessonMenuItem> menu = new ArrayList<>();
|
||||||
List<Category> categories = course.getCategories();
|
List<Category> categories = course.getCategories();
|
||||||
UserProgress userTracker = userTrackerRepository.findByUser(username);
|
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||||
|
|
||||||
for (Category category : categories) {
|
for (Category category : categories) {
|
||||||
if (excludeCategories.contains(category.name())) {
|
if (excludeCategories.contains(category.name())) {
|
||||||
@ -99,12 +97,12 @@ public class LessonMenuService {
|
|||||||
lessonItem.setName(lesson.getTitle());
|
lessonItem.setName(lesson.getTitle());
|
||||||
lessonItem.setLink(lesson.getLink());
|
lessonItem.setLink(lesson.getLink());
|
||||||
lessonItem.setType(LessonMenuItemType.LESSON);
|
lessonItem.setType(LessonMenuItemType.LESSON);
|
||||||
LessonProgress lessonTracker = userTracker.getLessonProgress(lesson);
|
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
|
||||||
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
|
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
|
||||||
lessonItem.setComplete(lessonSolved);
|
lessonItem.setComplete(lessonSolved);
|
||||||
categoryItem.addChild(lessonItem);
|
categoryItem.addChild(lessonItem);
|
||||||
}
|
}
|
||||||
categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking));
|
categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking());
|
||||||
menu.add(categoryItem);
|
menu.add(categoryItem);
|
||||||
}
|
}
|
||||||
return menu;
|
return menu;
|
||||||
|
@ -4,15 +4,11 @@ import java.util.List;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.lessons.Assignment;
|
import org.owasp.webgoat.container.lessons.Assignment;
|
||||||
import org.owasp.webgoat.container.lessons.LessonName;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.util.Assert;
|
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.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,8 +20,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LessonProgressService {
|
public class LessonProgressService {
|
||||||
|
|
||||||
private final UserProgressRepository userProgressRepository;
|
private final UserTrackerRepository userTrackerRepository;
|
||||||
private final Course course;
|
private final WebSession webSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endpoint for fetching the complete lesson overview which informs the user about whether all the
|
* Endpoint for fetching the complete lesson overview which informs the user about whether all the
|
||||||
@ -33,20 +29,20 @@ public class LessonProgressService {
|
|||||||
*
|
*
|
||||||
* @return list of assignments
|
* @return list of assignments
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/service/lessonoverview.mvc/{lesson}")
|
@RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<LessonOverview> lessonOverview(
|
public List<LessonOverview> lessonOverview() {
|
||||||
@PathVariable("lesson") LessonName lessonName, @CurrentUsername String username) {
|
var userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||||
var userProgress = userProgressRepository.findByUser(username);
|
var currentLesson = webSession.getCurrentLesson();
|
||||||
var lesson = course.getLessonByName(lessonName);
|
|
||||||
|
|
||||||
Assert.isTrue(lesson != null, "Lesson not found: " + lessonName);
|
if (currentLesson != null) {
|
||||||
|
var lessonTracker = userTracker.getLessonTracker(currentLesson);
|
||||||
var lessonProgress = userProgress.getLessonProgress(lesson);
|
return lessonTracker.getLessonOverview().entrySet().stream()
|
||||||
return lessonProgress.getLessonOverview().entrySet().stream()
|
|
||||||
.map(entry -> new LessonOverview(entry.getKey(), entry.getValue()))
|
.map(entry -> new LessonOverview(entry.getKey(), entry.getValue()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
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() : "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* *************************************************************************************************
|
||||||
|
*
|
||||||
|
* <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,17 +29,14 @@ import java.util.function.Function;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
import org.owasp.webgoat.container.CurrentUser;
|
import org.owasp.webgoat.container.lessons.Initializeable;
|
||||||
import org.owasp.webgoat.container.lessons.Initializable;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.owasp.webgoat.container.lessons.LessonName;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.users.UserTracker;
|
||||||
import org.owasp.webgoat.container.users.UserProgress;
|
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||||
import org.owasp.webgoat.container.users.UserProgressRepository;
|
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@ -47,25 +44,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class RestartLessonService {
|
public class RestartLessonService {
|
||||||
|
|
||||||
private final Course course;
|
private final WebSession webSession;
|
||||||
private final UserProgressRepository userTrackerRepository;
|
private final UserTrackerRepository userTrackerRepository;
|
||||||
private final Function<String, Flyway> flywayLessons;
|
private final Function<String, Flyway> flywayLessons;
|
||||||
private final List<Initializable> lessonsToInitialize;
|
private final List<Initializeable> lessonsToInitialize;
|
||||||
|
|
||||||
@GetMapping(path = "/service/restartlesson.mvc/{lesson}")
|
@RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text")
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
public void restartLesson(
|
public void restartLesson() {
|
||||||
@PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) {
|
Lesson al = webSession.getCurrentLesson();
|
||||||
var lesson = course.getLessonByName(lessonName);
|
log.debug("Restarting lesson: " + al);
|
||||||
|
|
||||||
UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername());
|
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
||||||
userTracker.reset(lesson);
|
userTracker.reset(al);
|
||||||
userTrackerRepository.save(userTracker);
|
userTrackerRepository.save(userTracker);
|
||||||
|
|
||||||
var flyway = flywayLessons.apply(user.getUsername());
|
var flyway = flywayLessons.apply(webSession.getUserName());
|
||||||
flyway.clean();
|
flyway.clean();
|
||||||
flyway.migrate();
|
flyway.migrate();
|
||||||
|
|
||||||
lessonsToInitialize.forEach(i -> i.initialize(user));
|
lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
package org.owasp.webgoat.container.service;
|
package org.owasp.webgoat.container.service;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.owasp.webgoat.container.CurrentUser;
|
|
||||||
import org.owasp.webgoat.container.i18n.Messages;
|
import org.owasp.webgoat.container.i18n.Messages;
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -18,17 +17,17 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class SessionService {
|
public class SessionService {
|
||||||
|
|
||||||
|
private final WebSession webSession;
|
||||||
private final RestartLessonService restartLessonService;
|
private final RestartLessonService restartLessonService;
|
||||||
private final Messages messages;
|
private final Messages messages;
|
||||||
|
|
||||||
@RequestMapping(path = "/service/enable-security.mvc", produces = "application/json")
|
@RequestMapping(path = "/service/enable-security.mvc", produces = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public String applySecurity(@CurrentUser WebGoatUser user) {
|
public String applySecurity() {
|
||||||
// webSession.toggleSecurity();
|
webSession.toggleSecurity();
|
||||||
// restartLessonService.restartLesson(user);
|
restartLessonService.restartLesson();
|
||||||
|
|
||||||
// TODO disabled for now
|
var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
||||||
// var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
return messages.getMessage(msg);
|
||||||
return messages.getMessage("Not working...");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import java.util.List;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.owasp.webgoat.container.lessons.Category;
|
import org.owasp.webgoat.container.lessons.Category;
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.owasp.webgoat.container.lessons.LessonName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ************************************************************************************************
|
* ************************************************************************************************
|
||||||
@ -97,21 +96,4 @@ public class Course {
|
|||||||
return this.lessons.stream()
|
return this.lessons.stream()
|
||||||
.reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum);
|
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
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
|
@Entity
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class LessonProgress {
|
public class LessonTracker {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -61,22 +61,25 @@ public class LessonProgress {
|
|||||||
@Getter private String lessonName;
|
@Getter private String lessonName;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
private final Set<Assignment> assignments = new HashSet<>();
|
private final Set<Assignment> solvedAssignments = new HashSet<>();
|
||||||
|
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
|
private final Set<Assignment> allAssignments = new HashSet<>();
|
||||||
|
|
||||||
@Getter private int numberOfAttempts = 0;
|
@Getter private int numberOfAttempts = 0;
|
||||||
@Version private Integer version;
|
@Version private Integer version;
|
||||||
|
|
||||||
protected LessonProgress() {
|
private LessonTracker() {
|
||||||
// JPA
|
// JPA
|
||||||
}
|
}
|
||||||
|
|
||||||
public LessonProgress(Lesson lesson) {
|
public LessonTracker(Lesson lesson) {
|
||||||
lessonName = lesson.getId();
|
lessonName = lesson.getId();
|
||||||
assignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Assignment> getAssignment(String name) {
|
public Optional<Assignment> getAssignment(String name) {
|
||||||
return assignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,14 +88,14 @@ public class LessonProgress {
|
|||||||
* @param solvedAssignment the assignment which the user solved
|
* @param solvedAssignment the assignment which the user solved
|
||||||
*/
|
*/
|
||||||
public void assignmentSolved(String solvedAssignment) {
|
public void assignmentSolved(String solvedAssignment) {
|
||||||
getAssignment(solvedAssignment).ifPresent(Assignment::solved);
|
getAssignment(solvedAssignment).ifPresent(solvedAssignments::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return did they user solved all solvedAssignments for the lesson?
|
* @return did they user solved all solvedAssignments for the lesson?
|
||||||
*/
|
*/
|
||||||
public boolean isLessonSolved() {
|
public boolean isLessonSolved() {
|
||||||
return assignments.stream().allMatch(Assignment::isSolved);
|
return allAssignments.size() == solvedAssignments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increase the number attempts to solve the lesson */
|
/** Increase the number attempts to solve the lesson */
|
||||||
@ -102,17 +105,18 @@ public class LessonProgress {
|
|||||||
|
|
||||||
/** Reset the tracker. We do not reset the number of attempts here! */
|
/** Reset the tracker. We do not reset the number of attempts here! */
|
||||||
void reset() {
|
void reset() {
|
||||||
assignments.clear();
|
solvedAssignments.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list containing all the assignments solved or not
|
* @return list containing all the assignments solved or not
|
||||||
*/
|
*/
|
||||||
public Map<Assignment, Boolean> getLessonOverview() {
|
public Map<Assignment, Boolean> getLessonOverview() {
|
||||||
return assignments.stream().collect(Collectors.toMap(a -> a, Assignment::isSolved));
|
List<Assignment> notSolved =
|
||||||
}
|
allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList();
|
||||||
|
Map<Assignment, Boolean> overview =
|
||||||
long numberOfSolvedAssignments() {
|
notSolved.stream().collect(Collectors.toMap(a -> a, b -> false));
|
||||||
return assignments.size();
|
overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true)));
|
||||||
|
return overview;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Scoreboard {
|
public class Scoreboard {
|
||||||
|
|
||||||
private final UserProgressRepository userTrackerRepository;
|
private final UserTrackerRepository userTrackerRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final Course course;
|
private final Course course;
|
||||||
private final PluginMessages pluginMessages;
|
private final PluginMessages pluginMessages;
|
||||||
@ -46,7 +46,7 @@ public class Scoreboard {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> challengesSolved(UserProgress userTracker) {
|
private List<String> challengesSolved(UserTracker userTracker) {
|
||||||
List<String> challenges =
|
List<String> challenges =
|
||||||
List.of(
|
List.of(
|
||||||
"Challenge1",
|
"Challenge1",
|
||||||
@ -59,10 +59,10 @@ public class Scoreboard {
|
|||||||
"Challenge8",
|
"Challenge8",
|
||||||
"Challenge9");
|
"Challenge9");
|
||||||
return challenges.stream()
|
return challenges.stream()
|
||||||
.map(userTracker::getLessonProgress)
|
.map(userTracker::getLessonTracker)
|
||||||
.flatMap(Optional::stream)
|
.flatMap(Optional::stream)
|
||||||
.filter(LessonProgress::isLessonSolved)
|
.filter(LessonTracker::isLessonSolved)
|
||||||
.map(LessonProgress::getLessonName)
|
.map(LessonTracker::getLessonName)
|
||||||
.map(this::toLessonTitle)
|
.map(this::toLessonTitle)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
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 java.util.function.Function;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
||||||
import org.owasp.webgoat.container.lessons.Initializable;
|
import org.owasp.webgoat.container.lessons.Initializeable;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
@ -19,10 +19,10 @@ import org.springframework.stereotype.Service;
|
|||||||
public class UserService implements UserDetailsService {
|
public class UserService implements UserDetailsService {
|
||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final UserProgressRepository userTrackerRepository;
|
private final UserTrackerRepository userTrackerRepository;
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final Function<String, Flyway> flywayLessons;
|
private final Function<String, Flyway> flywayLessons;
|
||||||
private final List<Initializable> lessonInitializables;
|
private final List<Initializeable> lessonInitializables;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {
|
public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
@ -43,7 +43,7 @@ public class UserService implements UserDetailsService {
|
|||||||
|
|
||||||
if (!userAlreadyExists) {
|
if (!userAlreadyExists) {
|
||||||
userTrackerRepository.save(
|
userTrackerRepository.save(
|
||||||
new UserProgress(username)); // if user previously existed it will not get another tracker
|
new UserTracker(username)); // if user previously existed it will not get another tracker
|
||||||
createLessonsForUser(webGoatUser);
|
createLessonsForUser(webGoatUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,13 @@ import jakarta.persistence.GenerationType;
|
|||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.owasp.webgoat.container.lessons.Assignment;
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +52,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Entity
|
@Entity
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class UserProgress {
|
public class UserTracker {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -59,11 +62,11 @@ public class UserProgress {
|
|||||||
private String user;
|
private String user;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
private Set<LessonProgress> lessonProgress = new HashSet<>();
|
private Set<LessonTracker> lessonTrackers = new HashSet<>();
|
||||||
|
|
||||||
protected UserProgress() {}
|
private UserTracker() {}
|
||||||
|
|
||||||
public UserProgress(final String user) {
|
public UserTracker(final String user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,15 +76,15 @@ public class UserProgress {
|
|||||||
* @param lesson the lesson
|
* @param lesson the lesson
|
||||||
* @return a lesson tracker created if not already present
|
* @return a lesson tracker created if not already present
|
||||||
*/
|
*/
|
||||||
public LessonProgress getLessonProgress(Lesson lesson) {
|
public LessonTracker getLessonTracker(Lesson lesson) {
|
||||||
Optional<LessonProgress> progress =
|
Optional<LessonTracker> lessonTracker =
|
||||||
lessonProgress.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
||||||
if (!progress.isPresent()) {
|
if (!lessonTracker.isPresent()) {
|
||||||
LessonProgress newLessonTracker = new LessonProgress(lesson);
|
LessonTracker newLessonTracker = new LessonTracker(lesson);
|
||||||
lessonProgress.add(newLessonTracker);
|
lessonTrackers.add(newLessonTracker);
|
||||||
return newLessonTracker;
|
return newLessonTracker;
|
||||||
} else {
|
} else {
|
||||||
return progress.get();
|
return lessonTracker.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,34 +94,43 @@ public class UserProgress {
|
|||||||
* @param id the id of the lesson
|
* @param id the id of the lesson
|
||||||
* @return optional due to the fact we can only create a lesson tracker based on a lesson
|
* @return optional due to the fact we can only create a lesson tracker based on a lesson
|
||||||
*/
|
*/
|
||||||
public Optional<LessonProgress> getLessonProgress(String id) {
|
public Optional<LessonTracker> getLessonTracker(String id) {
|
||||||
return lessonProgress.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignmentSolved(Lesson lesson, String assignmentName) {
|
public void assignmentSolved(Lesson lesson, String assignmentName) {
|
||||||
LessonProgress progress = getLessonProgress(lesson);
|
LessonTracker lessonTracker = getLessonTracker(lesson);
|
||||||
progress.incrementAttempts();
|
lessonTracker.incrementAttempts();
|
||||||
progress.assignmentSolved(assignmentName);
|
lessonTracker.assignmentSolved(assignmentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignmentFailed(Lesson lesson) {
|
public void assignmentFailed(Lesson lesson) {
|
||||||
LessonProgress progress = getLessonProgress(lesson);
|
LessonTracker lessonTracker = getLessonTracker(lesson);
|
||||||
progress.incrementAttempts();
|
lessonTracker.incrementAttempts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(Lesson al) {
|
public void reset(Lesson al) {
|
||||||
LessonProgress progress = getLessonProgress(al);
|
LessonTracker lessonTracker = getLessonTracker(al);
|
||||||
progress.reset();
|
lessonTracker.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long numberOfLessonsSolved() {
|
public int numberOfLessonsSolved() {
|
||||||
return lessonProgress.stream().filter(LessonProgress::isLessonSolved).count();
|
int numberOfLessonsSolved = 0;
|
||||||
|
for (LessonTracker lessonTracker : lessonTrackers) {
|
||||||
|
if (lessonTracker.isLessonSolved()) {
|
||||||
|
numberOfLessonsSolved = numberOfLessonsSolved + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numberOfLessonsSolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long numberOfAssignmentsSolved() {
|
public int numberOfAssignmentsSolved() {
|
||||||
return lessonProgress.stream()
|
int numberOfAssignmentsSolved = 0;
|
||||||
.map(LessonProgress::numberOfSolvedAssignments)
|
for (LessonTracker lessonTracker : lessonTrackers) {
|
||||||
.mapToLong(Long::valueOf)
|
Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview();
|
||||||
.sum();
|
numberOfAssignmentsSolved =
|
||||||
|
lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();
|
||||||
|
}
|
||||||
|
return numberOfAssignmentsSolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
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,7 +32,9 @@ import java.util.Map;
|
|||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -48,11 +50,9 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
})
|
})
|
||||||
public class VerifyAccount extends AssignmentEndpoint {
|
public class VerifyAccount extends AssignmentEndpoint {
|
||||||
|
|
||||||
private final LessonSession userSessionData;
|
@Autowired private WebSession webSession;
|
||||||
|
|
||||||
public VerifyAccount(LessonSession userSessionData) {
|
@Autowired UserSessionData userSessionData;
|
||||||
this.userSessionData = userSessionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
path = "/auth-bypass/verify-account",
|
path = "/auth-bypass/verify-account",
|
||||||
|
@ -2,13 +2,11 @@ package org.owasp.webgoat.lessons.challenges;
|
|||||||
|
|
||||||
import org.owasp.webgoat.container.lessons.Category;
|
import org.owasp.webgoat.container.lessons.Category;
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nbaars
|
* @author nbaars
|
||||||
* @since 3/21/17.
|
* @since 3/21/17.
|
||||||
*/
|
*/
|
||||||
@Component
|
|
||||||
public class ChallengeIntro extends Lesson {
|
public class ChallengeIntro extends Lesson {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,7 +25,8 @@ package org.owasp.webgoat.lessons.challenges;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -35,12 +36,13 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class FlagController extends AssignmentEndpoint {
|
public class FlagController extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private final WebSession webSession;
|
||||||
private final Flags flags;
|
private final Flags flags;
|
||||||
|
|
||||||
@PostMapping(path = "/challenge/flag/{flagNumber}")
|
@PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult postFlag(@PathVariable int flagNumber, @RequestParam String flag) {
|
public AttackResult postFlag(@RequestParam String flag) {
|
||||||
var expectedFlag = flags.getFlag(flagNumber);
|
Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson());
|
||||||
if (expectedFlag.isCorrect(flag)) {
|
if (expectedFlag.isCorrect(flag)) {
|
||||||
return success(this).feedback("challenge.flag.correct").build();
|
return success(this).feedback("challenge.flag.correct").build();
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,6 +4,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -14,6 +15,12 @@ public class Flags {
|
|||||||
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString())));
|
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) {
|
public Flag getFlag(int flagNumber) {
|
||||||
return FLAGS.get(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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is just a class used to make the HTTP request.
|
* This is just a class used to make the the HTTP request.
|
||||||
*
|
*
|
||||||
* @author TMelzer
|
* @author TMelzer
|
||||||
* @since 30.11.18
|
* @since 30.11.18
|
||||||
@ -39,16 +39,11 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
public class NetworkDummy extends AssignmentEndpoint {
|
public class NetworkDummy extends AssignmentEndpoint {
|
||||||
|
|
||||||
private final LessonSession lessonSession;
|
|
||||||
|
|
||||||
public NetworkDummy(LessonSession lessonSession) {
|
|
||||||
this.lessonSession = lessonSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/ChromeDevTools/dummy")
|
@PostMapping("/ChromeDevTools/dummy")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult completed(@RequestParam String successMessage) {
|
public AttackResult completed(@RequestParam String successMessage) {
|
||||||
String answer = (String) lessonSession.getValue("randValue");
|
UserSessionData userSessionData = getUserSessionData();
|
||||||
|
String answer = (String) userSessionData.getValue("randValue");
|
||||||
|
|
||||||
if (successMessage != null && successMessage.equals(answer)) {
|
if (successMessage != null && successMessage.equals(answer)) {
|
||||||
return success(this).feedback("xss-dom-message-success").build();
|
return success(this).feedback("xss-dom-message-success").build();
|
||||||
|
@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
|
|||||||
public class CSRF extends Lesson {
|
public class CSRF extends Lesson {
|
||||||
@Override
|
@Override
|
||||||
public Category getDefaultCategory() {
|
public Category getDefaultCategory() {
|
||||||
return Category.A5;
|
return Category.A10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,7 +25,7 @@ package org.owasp.webgoat.lessons.csrf;
|
|||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
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"})
|
@AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"})
|
||||||
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
|
public class CSRFConfirmFlag1 extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
path = "/csrf/confirm-flag-1",
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -42,11 +42,15 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 11/17/17.
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
|
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
|
||||||
public class CSRFFeedback extends AssignmentEndpoint {
|
public class CSRFFeedback extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired private LessonSession userSessionData;
|
@Autowired private UserSessionData userSessionData;
|
||||||
@Autowired private ObjectMapper objectMapper;
|
@Autowired private ObjectMapper objectMapper;
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
|
@ -27,7 +27,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -37,7 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
public class CSRFGetFlag {
|
public class CSRFGetFlag {
|
||||||
|
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
@Autowired private PluginMessages pluginMessages;
|
@Autowired private PluginMessages pluginMessages;
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
|
@ -22,26 +22,47 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.csrf;
|
package org.owasp.webgoat.lessons.csrf;
|
||||||
|
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.users.UserTracker;
|
||||||
|
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nbaars
|
||||||
|
* @since 11/17/17.
|
||||||
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
|
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
|
||||||
public class CSRFLogin extends AssignmentEndpoint {
|
public class CSRFLogin extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private final UserTrackerRepository userTrackerRepository;
|
||||||
|
|
||||||
|
public CSRFLogin(UserTrackerRepository userTrackerRepository) {
|
||||||
|
this.userTrackerRepository = userTrackerRepository;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
path = "/csrf/login",
|
path = "/csrf/login",
|
||||||
produces = {"application/json"})
|
produces = {"application/json"})
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult completed(@CurrentUsername String username) {
|
public AttackResult completed(HttpServletRequest request) {
|
||||||
if (username.startsWith("csrf")) {
|
String userName = request.getUserPrincipal().getName();
|
||||||
|
if (userName.startsWith("csrf")) {
|
||||||
|
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
|
||||||
return success(this).feedback("csrf-login-success").build();
|
return success(this).feedback("csrf-login-success").build();
|
||||||
}
|
}
|
||||||
return failed(this).feedback("csrf-login-failed").feedbackArgs(username).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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,11 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"})
|
@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"})
|
||||||
public class ForgedReviews extends AssignmentEndpoint {
|
public class ForgedReviews extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
@Autowired private WebSession webSession;
|
||||||
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
|
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
|
||||||
|
|
||||||
private static final Map<String, List<Review>> userReviews = new HashMap<>();
|
private static final Map<String, List<Review>> userReviews = new HashMap<>();
|
||||||
@ -71,9 +73,9 @@ public class ForgedReviews extends AssignmentEndpoint {
|
|||||||
produces = MediaType.APPLICATION_JSON_VALUE,
|
produces = MediaType.APPLICATION_JSON_VALUE,
|
||||||
consumes = ALL_VALUE)
|
consumes = ALL_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Collection<Review> retrieveReviews(@CurrentUsername String username) {
|
public Collection<Review> retrieveReviews() {
|
||||||
Collection<Review> allReviews = Lists.newArrayList();
|
Collection<Review> allReviews = Lists.newArrayList();
|
||||||
Collection<Review> newReviews = userReviews.get(username);
|
Collection<Review> newReviews = userReviews.get(webSession.getUserName());
|
||||||
if (newReviews != null) {
|
if (newReviews != null) {
|
||||||
allReviews.addAll(newReviews);
|
allReviews.addAll(newReviews);
|
||||||
}
|
}
|
||||||
@ -86,11 +88,7 @@ public class ForgedReviews extends AssignmentEndpoint {
|
|||||||
@PostMapping("/csrf/review")
|
@PostMapping("/csrf/review")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult createNewReview(
|
public AttackResult createNewReview(
|
||||||
String reviewText,
|
String reviewText, Integer stars, String validateReq, HttpServletRequest request) {
|
||||||
Integer stars,
|
|
||||||
String validateReq,
|
|
||||||
HttpServletRequest request,
|
|
||||||
@CurrentUsername String username) {
|
|
||||||
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
|
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
|
||||||
final String referer =
|
final String referer =
|
||||||
(request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
|
(request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
|
||||||
@ -99,11 +97,11 @@ public class ForgedReviews extends AssignmentEndpoint {
|
|||||||
Review review = new Review();
|
Review review = new Review();
|
||||||
review.setText(reviewText);
|
review.setText(reviewText);
|
||||||
review.setDateTime(LocalDateTime.now().format(fmt));
|
review.setDateTime(LocalDateTime.now().format(fmt));
|
||||||
review.setUser(username);
|
review.setUser(webSession.getUserName());
|
||||||
review.setStars(stars);
|
review.setStars(stars);
|
||||||
var reviews = userReviews.getOrDefault(username, new ArrayList<>());
|
var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>());
|
||||||
reviews.add(review);
|
reviews.add(review);
|
||||||
userReviews.put(username, reviews);
|
userReviews.put(webSession.getUserName(), reviews);
|
||||||
// short-circuit
|
// short-circuit
|
||||||
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
|
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
|
||||||
return failed(this).feedback("csrf-you-forgot-something").build();
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
})
|
})
|
||||||
public class IDOREditOtherProfile extends AssignmentEndpoint {
|
public class IDOREditOtherProfile extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired private LessonSession userSessionData;
|
@Autowired private UserSessionData userSessionData;
|
||||||
|
|
||||||
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
|
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -28,7 +28,7 @@ import java.util.Map;
|
|||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -38,12 +38,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@AssignmentHints({"idor.hints.idor_login"})
|
@AssignmentHints({"idor.hints.idor_login"})
|
||||||
public class IDORLogin extends AssignmentEndpoint {
|
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<>();
|
private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();
|
||||||
|
|
||||||
public void initIDORInfo() {
|
public void initIDORInfo() {
|
||||||
@ -65,11 +59,13 @@ public class IDORLogin extends AssignmentEndpoint {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
|
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
|
||||||
initIDORInfo();
|
initIDORInfo();
|
||||||
|
UserSessionData userSessionData = getUserSessionData();
|
||||||
|
|
||||||
if (idorUserInfo.containsKey(username)) {
|
if (idorUserInfo.containsKey(username)) {
|
||||||
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
|
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
|
||||||
lessonSession.setValue("idor-authenticated-as", username);
|
userSessionData.setValue("idor-authenticated-as", username);
|
||||||
lessonSession.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
|
userSessionData.setValue(
|
||||||
|
"idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
|
||||||
return success(this).feedback("idor.login.success").feedbackArgs(username).build();
|
return success(this).feedback("idor.login.success").feedbackArgs(username).build();
|
||||||
} else {
|
} else {
|
||||||
return failed(this).feedback("idor.login.failure").build();
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
})
|
})
|
||||||
public class IDORViewOtherProfile extends AssignmentEndpoint {
|
public class IDORViewOtherProfile extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
|
|
||||||
@GetMapping(
|
@GetMapping(
|
||||||
path = "/IDOR/profile/{userId}",
|
path = "/IDOR/profile/{userId}",
|
||||||
|
@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class IDORViewOwnProfile {
|
public class IDORViewOwnProfile {
|
||||||
|
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
|
|
||||||
@GetMapping(
|
@GetMapping(
|
||||||
path = {"/IDOR/own", "/IDOR/profile"},
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -41,7 +41,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
})
|
})
|
||||||
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {
|
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
|
|
||||||
@PostMapping("/IDOR/profile/alt-path")
|
@PostMapping("/IDOR/profile/alt-path")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.owasp.webgoat.lessons.jwt.claimmisuse;
|
package org.owasp.webgoat.lessons.jwt.claimmisuse;
|
||||||
|
|
||||||
import com.auth0.jwk.JwkException;
|
import com.auth0.jwk.JwkException;
|
||||||
|
import com.auth0.jwk.JwkProvider;
|
||||||
import com.auth0.jwk.JwkProviderBuilder;
|
import com.auth0.jwk.JwkProviderBuilder;
|
||||||
import com.auth0.jwt.JWT;
|
import com.auth0.jwt.JWT;
|
||||||
import com.auth0.jwt.algorithms.Algorithm;
|
import com.auth0.jwt.algorithms.Algorithm;
|
||||||
@ -47,12 +48,12 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint {
|
|||||||
try {
|
try {
|
||||||
var decodedJWT = JWT.decode(token);
|
var decodedJWT = JWT.decode(token);
|
||||||
var jku = decodedJWT.getHeaderClaim("jku");
|
var jku = decodedJWT.getHeaderClaim("jku");
|
||||||
var jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build();
|
JwkProvider jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build();
|
||||||
var jwk = jwkProvider.get(decodedJWT.getKeyId());
|
var jwk = jwkProvider.get(decodedJWT.getKeyId());
|
||||||
var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey());
|
var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey());
|
||||||
JWT.require(algorithm).build().verify(decodedJWT);
|
JWT.require(algorithm).build().verify(decodedJWT);
|
||||||
|
|
||||||
var username = decodedJWT.getClaims().get("username").asString();
|
String username = decodedJWT.getClaims().get("username").asString();
|
||||||
if ("Jerry".equals(username)) {
|
if ("Jerry".equals(username)) {
|
||||||
return failed(this).feedback("jwt-final-jerry-account").build();
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.LessonSession;
|
import org.owasp.webgoat.container.session.UserSessionData;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@ -44,7 +44,7 @@ public class SampleAttack extends AssignmentEndpoint {
|
|||||||
String secretValue = "secr37Value";
|
String secretValue = "secr37Value";
|
||||||
|
|
||||||
// UserSessionData is bound to session and can be used to persist data across multiple assignments
|
// UserSessionData is bound to session and can be used to persist data across multiple assignments
|
||||||
@Autowired LessonSession userSessionData;
|
@Autowired UserSessionData userSessionData;
|
||||||
|
|
||||||
@PostMapping("/lesson-template/sample-attack")
|
@PostMapping("/lesson-template/sample-attack")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -30,7 +30,7 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@ -47,6 +47,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
public class MissingFunctionACUsers {
|
public class MissingFunctionACUsers {
|
||||||
|
|
||||||
private final MissingAccessControlUserRepository userRepository;
|
private final MissingAccessControlUserRepository userRepository;
|
||||||
|
private final WebSession webSession;
|
||||||
|
|
||||||
@GetMapping(path = {"access-control/users"})
|
@GetMapping(path = {"access-control/users"})
|
||||||
public ModelAndView listUsers() {
|
public ModelAndView listUsers() {
|
||||||
@ -80,8 +81,8 @@ public class MissingFunctionACUsers {
|
|||||||
path = {"access-control/users-admin-fix"},
|
path = {"access-control/users-admin-fix"},
|
||||||
consumes = "application/json")
|
consumes = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<List<DisplayUser>> usersFixed(@CurrentUsername String username) {
|
public ResponseEntity<List<DisplayUser>> usersFixed() {
|
||||||
var currentUser = userRepository.findByUsername(username);
|
var currentUser = userRepository.findByUsername(webSession.getUserName());
|
||||||
if (currentUser != null && currentUser.isAdmin()) {
|
if (currentUser != null && currentUser.isAdmin()) {
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
userRepository.findAllUsers().stream()
|
userRepository.findAllUsers().stream()
|
||||||
|
@ -27,7 +27,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
@ -82,10 +81,10 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
|
|
||||||
@PostMapping("/PasswordReset/reset/login")
|
@PostMapping("/PasswordReset/reset/login")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult login(
|
public AttackResult login(@RequestParam String password, @RequestParam String email) {
|
||||||
@RequestParam String password, @RequestParam String email, @CurrentUsername String username) {
|
|
||||||
if (TOM_EMAIL.equals(email)) {
|
if (TOM_EMAIL.equals(email)) {
|
||||||
String passwordTom = usersToTomPassword.getOrDefault(username, PASSWORD_TOM_9);
|
String passwordTom =
|
||||||
|
usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
|
||||||
if (passwordTom.equals(PASSWORD_TOM_9)) {
|
if (passwordTom.equals(PASSWORD_TOM_9)) {
|
||||||
return failed(this).feedback("login_failed").build();
|
return failed(this).feedback("login_failed").build();
|
||||||
} else if (passwordTom.equals(password)) {
|
} else if (passwordTom.equals(password)) {
|
||||||
@ -113,9 +112,7 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
|
|
||||||
@PostMapping("/PasswordReset/reset/change-password")
|
@PostMapping("/PasswordReset/reset/change-password")
|
||||||
public ModelAndView changePassword(
|
public ModelAndView changePassword(
|
||||||
@ModelAttribute("form") PasswordChangeForm form,
|
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||||
BindingResult bindingResult,
|
|
||||||
@CurrentUsername String username) {
|
|
||||||
ModelAndView modelAndView = new ModelAndView();
|
ModelAndView modelAndView = new ModelAndView();
|
||||||
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {
|
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) {
|
||||||
bindingResult.rejectValue("password", "not.empty");
|
bindingResult.rejectValue("password", "not.empty");
|
||||||
@ -128,15 +125,15 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
if (checkIfLinkIsFromTom(form.getResetLink(), username)) {
|
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||||
usersToTomPassword.put(username, form.getPassword());
|
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||||
}
|
}
|
||||||
modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfLinkIsFromTom(String resetLinkFromForm, String username) {
|
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) {
|
||||||
String resetLink = userToTomResetLink.getOrDefault(username, "unknown");
|
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown");
|
||||||
return resetLink.equals(resetLinkFromForm);
|
return resetLink.equals(resetLinkFromForm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ package org.owasp.webgoat.lessons.passwordreset;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -68,14 +67,14 @@ public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint {
|
|||||||
@PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link")
|
@PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult sendPasswordResetLink(
|
public AttackResult sendPasswordResetLink(
|
||||||
@RequestParam String email, HttpServletRequest request, @CurrentUsername String username) {
|
@RequestParam String email, HttpServletRequest request) {
|
||||||
String resetLink = UUID.randomUUID().toString();
|
String resetLink = UUID.randomUUID().toString();
|
||||||
ResetLinkAssignment.resetLinks.add(resetLink);
|
ResetLinkAssignment.resetLinks.add(resetLink);
|
||||||
String host = request.getHeader(HttpHeaders.HOST);
|
String host = request.getHeader(HttpHeaders.HOST);
|
||||||
if (ResetLinkAssignment.TOM_EMAIL.equals(email)
|
if (ResetLinkAssignment.TOM_EMAIL.equals(email)
|
||||||
&& (host.contains(webWolfPort)
|
&& (host.contains(webWolfPort)
|
||||||
&& host.contains(webWolfHost))) { // User indeed changed the host header.
|
&& host.contains(webWolfHost))) { // User indeed changed the host header.
|
||||||
ResetLinkAssignment.userToTomResetLink.put(username, resetLink);
|
ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);
|
||||||
fakeClickingLinkEmail(webWolfURL, resetLink);
|
fakeClickingLinkEmail(webWolfURL, resetLink);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
@ -26,7 +26,6 @@ import static java.util.Optional.ofNullable;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -58,14 +57,12 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
|||||||
path = "/PasswordReset/simple-mail",
|
path = "/PasswordReset/simple-mail",
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult login(
|
public AttackResult login(@RequestParam String email, @RequestParam String password) {
|
||||||
@RequestParam String email,
|
|
||||||
@RequestParam String password,
|
|
||||||
@CurrentUsername String webGoatUsername) {
|
|
||||||
String emailAddress = ofNullable(email).orElse("unknown@webgoat.org");
|
String emailAddress = ofNullable(email).orElse("unknown@webgoat.org");
|
||||||
String username = extractUsername(emailAddress);
|
String username = extractUsername(emailAddress);
|
||||||
|
|
||||||
if (username.equals(webGoatUsername) && StringUtils.reverse(username).equals(password)) {
|
if (username.equals(getWebSession().getUserName())
|
||||||
|
&& StringUtils.reverse(username).equals(password)) {
|
||||||
return success(this).build();
|
return success(this).build();
|
||||||
} else {
|
} else {
|
||||||
return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build();
|
return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build();
|
||||||
@ -76,10 +73,9 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
|||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
value = "/PasswordReset/simple-mail/reset")
|
value = "/PasswordReset/simple-mail/reset")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult resetPassword(
|
public AttackResult resetPassword(@RequestParam String emailReset) {
|
||||||
@RequestParam String emailReset, @CurrentUsername String username) {
|
|
||||||
String email = ofNullable(emailReset).orElse("unknown@webgoat.org");
|
String email = ofNullable(emailReset).orElse("unknown@webgoat.org");
|
||||||
return sendEmail(extractUsername(email), email, username);
|
return sendEmail(extractUsername(email), email);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractUsername(String email) {
|
private String extractUsername(String email) {
|
||||||
@ -87,8 +83,8 @@ public class SimpleMailAssignment extends AssignmentEndpoint {
|
|||||||
return email.substring(0, index == -1 ? email.length() : index);
|
return email.substring(0, index == -1 ? email.length() : index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AttackResult sendEmail(String username, String email, String webGoatUsername) {
|
private AttackResult sendEmail(String username, String email) {
|
||||||
if (username.equals(webGoatUsername)) {
|
if (username.equals(getWebSession().getUserName())) {
|
||||||
PasswordResetEmail mailEvent =
|
PasswordResetEmail mailEvent =
|
||||||
PasswordResetEmail.builder()
|
PasswordResetEmail.builder()
|
||||||
.recipient(username)
|
.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.ALL_VALUE;
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_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.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -23,8 +23,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
})
|
})
|
||||||
public class ProfileUpload extends ProfileUploadBase {
|
public class ProfileUpload extends ProfileUploadBase {
|
||||||
|
|
||||||
public ProfileUpload(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) {
|
public ProfileUpload(
|
||||||
super(webGoatHomeDirectory);
|
@Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) {
|
||||||
|
super(webGoatHomeDirectory, webSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
@ -34,14 +35,13 @@ public class ProfileUpload extends ProfileUploadBase {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult uploadFileHandler(
|
public AttackResult uploadFileHandler(
|
||||||
@RequestParam("uploadedFile") MultipartFile file,
|
@RequestParam("uploadedFile") MultipartFile file,
|
||||||
@RequestParam(value = "fullName", required = false) String fullName,
|
@RequestParam(value = "fullName", required = false) String fullName) {
|
||||||
@CurrentUsername String username) {
|
return super.execute(file, fullName);
|
||||||
return super.execute(file, fullName, username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/PathTraversal/profile-picture")
|
@GetMapping("/PathTraversal/profile-picture")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<?> getProfilePicture(@CurrentUsername String username) {
|
public ResponseEntity<?> getProfilePicture() {
|
||||||
return super.getProfilePicture(username);
|
return super.getProfilePicture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
@ -26,8 +26,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class ProfileUploadBase extends AssignmentEndpoint {
|
public class ProfileUploadBase extends AssignmentEndpoint {
|
||||||
|
|
||||||
private String webGoatHomeDirectory;
|
private String webGoatHomeDirectory;
|
||||||
|
private WebSession webSession;
|
||||||
|
|
||||||
protected AttackResult execute(MultipartFile file, String fullName, String username) {
|
protected AttackResult execute(MultipartFile file, String fullName) {
|
||||||
if (file.isEmpty()) {
|
if (file.isEmpty()) {
|
||||||
return failed(this).feedback("path-traversal-profile-empty-file").build();
|
return failed(this).feedback("path-traversal-profile-empty-file").build();
|
||||||
}
|
}
|
||||||
@ -35,7 +36,7 @@ public class ProfileUploadBase extends AssignmentEndpoint {
|
|||||||
return failed(this).feedback("path-traversal-profile-empty-name").build();
|
return failed(this).feedback("path-traversal-profile-empty-name").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
File uploadDirectory = cleanupAndCreateDirectoryForUser(username);
|
File uploadDirectory = cleanupAndCreateDirectoryForUser();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var uploadedFile = new File(uploadDirectory, fullName);
|
var uploadedFile = new File(uploadDirectory, fullName);
|
||||||
@ -56,8 +57,9 @@ public class ProfileUploadBase extends AssignmentEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
protected File cleanupAndCreateDirectoryForUser(String username) {
|
protected File cleanupAndCreateDirectoryForUser() {
|
||||||
var uploadDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + username);
|
var uploadDirectory =
|
||||||
|
new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName());
|
||||||
if (uploadDirectory.exists()) {
|
if (uploadDirectory.exists()) {
|
||||||
FileSystemUtils.deleteRecursively(uploadDirectory);
|
FileSystemUtils.deleteRecursively(uploadDirectory);
|
||||||
}
|
}
|
||||||
@ -83,14 +85,15 @@ public class ProfileUploadBase extends AssignmentEndpoint {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity<?> getProfilePicture(@CurrentUsername String username) {
|
public ResponseEntity<?> getProfilePicture() {
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
.contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))
|
||||||
.body(getProfilePictureAsBase64(username));
|
.body(getProfilePictureAsBase64());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] getProfilePictureAsBase64(String username) {
|
protected byte[] getProfilePictureAsBase64() {
|
||||||
var profilePictureDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + username);
|
var profilePictureDirectory =
|
||||||
|
new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName());
|
||||||
var profileDirectoryFiles = profilePictureDirectory.listFiles();
|
var profileDirectoryFiles = profilePictureDirectory.listFiles();
|
||||||
|
|
||||||
if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) {
|
if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) {
|
||||||
|
@ -3,9 +3,9 @@ package org.owasp.webgoat.lessons.pathtraversal;
|
|||||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_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.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -23,8 +23,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
})
|
})
|
||||||
public class ProfileUploadFix extends ProfileUploadBase {
|
public class ProfileUploadFix extends ProfileUploadBase {
|
||||||
|
|
||||||
public ProfileUploadFix(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) {
|
public ProfileUploadFix(
|
||||||
super(webGoatHomeDirectory);
|
@Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) {
|
||||||
|
super(webGoatHomeDirectory, webSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
@ -34,14 +35,13 @@ public class ProfileUploadFix extends ProfileUploadBase {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult uploadFileHandler(
|
public AttackResult uploadFileHandler(
|
||||||
@RequestParam("uploadedFileFix") MultipartFile file,
|
@RequestParam("uploadedFileFix") MultipartFile file,
|
||||||
@RequestParam(value = "fullNameFix", required = false) String fullName,
|
@RequestParam(value = "fullNameFix", required = false) String fullName) {
|
||||||
@CurrentUsername String username) {
|
return super.execute(file, fullName != null ? fullName.replace("../", "") : "");
|
||||||
return super.execute(file, fullName != null ? fullName.replace("../", "") : "", username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/PathTraversal/profile-picture-fix")
|
@GetMapping("/PathTraversal/profile-picture-fix")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<?> getProfilePicture(@CurrentUsername String username) {
|
public ResponseEntity<?> getProfilePicture() {
|
||||||
return super.getProfilePicture(username);
|
return super.getProfilePicture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ package org.owasp.webgoat.lessons.pathtraversal;
|
|||||||
import static org.springframework.http.MediaType.ALL_VALUE;
|
import static org.springframework.http.MediaType.ALL_VALUE;
|
||||||
import static org.springframework.http.MediaType.APPLICATION_JSON_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.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -22,8 +22,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class ProfileUploadRemoveUserInput extends ProfileUploadBase {
|
public class ProfileUploadRemoveUserInput extends ProfileUploadBase {
|
||||||
|
|
||||||
public ProfileUploadRemoveUserInput(
|
public ProfileUploadRemoveUserInput(
|
||||||
@Value("${webgoat.server.directory}") String webGoatHomeDirectory) {
|
@Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) {
|
||||||
super(webGoatHomeDirectory);
|
super(webGoatHomeDirectory, webSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
@ -32,8 +32,7 @@ public class ProfileUploadRemoveUserInput extends ProfileUploadBase {
|
|||||||
produces = APPLICATION_JSON_VALUE)
|
produces = APPLICATION_JSON_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult uploadFileHandler(
|
public AttackResult uploadFileHandler(
|
||||||
@RequestParam("uploadedFileRemoveUserInput") MultipartFile file,
|
@RequestParam("uploadedFileRemoveUserInput") MultipartFile file) {
|
||||||
@CurrentUsername String username) {
|
return super.execute(file, file.getOriginalFilename());
|
||||||
return super.execute(file, file.getOriginalFilename(), username);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import java.nio.file.Files;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
@ -72,10 +71,8 @@ public class ProfileUploadRetrieval extends AssignmentEndpoint {
|
|||||||
|
|
||||||
@PostMapping("/PathTraversal/random")
|
@PostMapping("/PathTraversal/random")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult execute(
|
public AttackResult execute(@RequestParam(value = "secret", required = false) String secret) {
|
||||||
@RequestParam(value = "secret", required = false) String secret,
|
if (Sha512DigestUtils.shaHex(getWebSession().getUserName()).equalsIgnoreCase(secret)) {
|
||||||
@CurrentUsername String username) {
|
|
||||||
if (Sha512DigestUtils.shaHex(username).equalsIgnoreCase(secret)) {
|
|
||||||
return success(this).build();
|
return success(this).build();
|
||||||
}
|
}
|
||||||
return failed(this).build();
|
return failed(this).build();
|
||||||
|
@ -14,9 +14,9 @@ import java.util.zip.ZipEntry;
|
|||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.owasp.webgoat.container.CurrentUsername;
|
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
|
import org.owasp.webgoat.container.session.WebSession;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
@ -38,8 +38,9 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProfileZipSlip extends ProfileUploadBase {
|
public class ProfileZipSlip extends ProfileUploadBase {
|
||||||
|
|
||||||
public ProfileZipSlip(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) {
|
public ProfileZipSlip(
|
||||||
super(webGoatHomeDirectory);
|
@Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) {
|
||||||
|
super(webGoatHomeDirectory, webSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
@ -47,20 +48,19 @@ public class ProfileZipSlip extends ProfileUploadBase {
|
|||||||
consumes = ALL_VALUE,
|
consumes = ALL_VALUE,
|
||||||
produces = APPLICATION_JSON_VALUE)
|
produces = APPLICATION_JSON_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult uploadFileHandler(
|
public AttackResult uploadFileHandler(@RequestParam("uploadedFileZipSlip") MultipartFile file) {
|
||||||
@RequestParam("uploadedFileZipSlip") MultipartFile file, @CurrentUsername String username) {
|
|
||||||
if (!file.getOriginalFilename().toLowerCase().endsWith(".zip")) {
|
if (!file.getOriginalFilename().toLowerCase().endsWith(".zip")) {
|
||||||
return failed(this).feedback("path-traversal-zip-slip.no-zip").build();
|
return failed(this).feedback("path-traversal-zip-slip.no-zip").build();
|
||||||
} else {
|
} else {
|
||||||
return processZipUpload(file, username);
|
return processZipUpload(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private AttackResult processZipUpload(MultipartFile file, String username) {
|
private AttackResult processZipUpload(MultipartFile file) {
|
||||||
var tmpZipDirectory = Files.createTempDirectory(username);
|
var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName());
|
||||||
cleanupAndCreateDirectoryForUser(username);
|
cleanupAndCreateDirectoryForUser();
|
||||||
var currentImage = getProfilePictureAsBase64(username);
|
var currentImage = getProfilePictureAsBase64();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename());
|
var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename());
|
||||||
@ -75,7 +75,7 @@ public class ProfileZipSlip extends ProfileUploadBase {
|
|||||||
Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isSolved(currentImage, getProfilePictureAsBase64(username));
|
return isSolved(currentImage, getProfilePictureAsBase64());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return failed(this).output(e.getMessage()).build();
|
return failed(this).output(e.getMessage()).build();
|
||||||
}
|
}
|
||||||
@ -90,13 +90,13 @@ public class ProfileZipSlip extends ProfileUploadBase {
|
|||||||
|
|
||||||
@GetMapping("/PathTraversal/zip-slip/")
|
@GetMapping("/PathTraversal/zip-slip/")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<?> getProfilePicture(@CurrentUsername String username) {
|
public ResponseEntity<?> getProfilePicture() {
|
||||||
return super.getProfilePicture(username);
|
return super.getProfilePicture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/PathTraversal/zip-slip/profile-image/{username}")
|
@GetMapping("/PathTraversal/zip-slip/profile-image/{username}")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResponseEntity<?> getProfileImage(@PathVariable String username) {
|
public ResponseEntity<?> getProfilePicture(@PathVariable("username") String username) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user