Compare commits
123 Commits
v2023.5
...
nbaars/ref
Author | SHA1 | Date | |
---|---|---|---|
6a83ddcdef | |||
67c1f622ef | |||
a3e0fcc9b3 | |||
d8100385b6 | |||
4880afa0e3 | |||
e60ca6ce72 | |||
88a763f513 | |||
7f33d3609f | |||
bf02077427 | |||
e1e00bca73 | |||
d59153d6d7 | |||
87fae00f03 | |||
3f6a74ad86 | |||
1d37ee0877 | |||
4f6ab25ebd | |||
af687e71fe | |||
83ed4c3d5c | |||
62cdfd0824 | |||
e7457f4821 | |||
4efaf87c7e | |||
cf5101a633 | |||
3f049ba53a | |||
7e294fbdb5 | |||
2177eb663a | |||
50692300eb | |||
e2c2d425cb | |||
6bbd3cb66b | |||
d08a56d351 | |||
ec97568ec2 | |||
9b68368b23 | |||
ab068901f1 | |||
cb7c508046 | |||
f4c86be6c7 | |||
cf2c115093 | |||
bb6e84ddcf | |||
5fc2666b43 | |||
6e946f21a2 | |||
d38ba2a626 | |||
4c7e6ae4f4 | |||
58b762eade | |||
2b0c22ac68 | |||
85103bbcad | |||
b98e1a98e1 | |||
73de259809 | |||
4a804fabb6 | |||
7f652dadec | |||
f66dff1aeb | |||
777cec5a57 | |||
98fd280459 | |||
8e0addff0b | |||
a30fbc223a | |||
a0b6decf34 | |||
1531987da5 | |||
bec6580c84 | |||
900702481b | |||
f35b23970e | |||
074fd8f5ff | |||
8c890b090c | |||
630c1e1afd | |||
061c75a05f | |||
5e5a1363b0 | |||
47df923c8c | |||
5dbc642264 | |||
96bc6fa608 | |||
1d5a2bb6a2 | |||
8b37bd58d7 | |||
6545e2a066 | |||
9573b30667 | |||
30e40f2e59 | |||
8f11fb6729 | |||
10e36c203f | |||
edcce09b5f | |||
3134f18066 | |||
e219887f14 | |||
508703ffce | |||
e308d7cde7 | |||
4ab820e1d1 | |||
1a6a7e0be1 | |||
2e9140ab64 | |||
b79c83a52e | |||
297c6f49b5 | |||
d2049a8fcc | |||
24db39eae2 | |||
98443184e9 | |||
62931a1836 | |||
c18430752a | |||
57d5b313b9 | |||
dd0f135088 | |||
ad0286d5ba | |||
b67eb44142 | |||
7e75e9b8fc | |||
40c679ec5a | |||
4ebb869f5d | |||
6bb7a182dc | |||
cb2c99d38d | |||
84029345b4 | |||
a0ca199cdc | |||
2058298e2d | |||
17acef57b4 | |||
d913967ec5 | |||
87edc7d1db | |||
ac7a9c7863 | |||
2803ef45e4 | |||
5357a65e05 | |||
d343c60781 | |||
98acc1f55a | |||
f99888e61b | |||
29dda49190 | |||
369be6f688 | |||
d5f869c006 | |||
a9caaabb47 | |||
fb2ff01775 | |||
89ecf1d2ad | |||
1b66a742da | |||
a831da5886 | |||
fd5189c102 | |||
ae261f201a | |||
3d651526be | |||
c7c2a61f65 | |||
b7f657ad2c | |||
7fea42afe9 | |||
826887cc83 | |||
62db86246e |
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@ -8,10 +8,7 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
ignore:
|
|
||||||
- dependency-name: "org.webjars:bootstrap" # First the WebWolf UI needs to be refactored due to breaking changes
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
java-version: [ 17, 21 ]
|
java-version: [ 21 ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up JDK ${{ matrix.java-version }}
|
- name: Set up JDK ${{ matrix.java-version }}
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
83
.github/workflows/build.yml
vendored
83
.github/workflows/build.yml
vendored
@ -1,60 +1,51 @@
|
|||||||
name: "Pull requests build"
|
name: "Main / Pull requests build"
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.txt'
|
- '.txt'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
branches: [ main ]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr-build:
|
pre-commit:
|
||||||
if: >
|
name: Pre-commit check
|
||||||
github.event_name == 'pull_request' && !github.event.pull_request.draft && (
|
runs-on: ubuntu-latest
|
||||||
github.event.action == 'opened' ||
|
|
||||||
github.event.action == 'reopened' ||
|
|
||||||
github.event.action == 'synchronize'
|
|
||||||
)
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout git repository
|
||||||
- name: Set up JDK 17
|
uses: actions/checkout@v4.1.6
|
||||||
uses: actions/setup-java@v3
|
- name: Setup python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.9"
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: '21'
|
||||||
architecture: x64
|
- name: Pre-commit checks
|
||||||
- name: Cache Maven packages
|
uses: pre-commit/action@v3.0.1
|
||||||
uses: actions/cache@v3.3.1
|
- name: pre-commit-ci-lite
|
||||||
|
uses: pre-commit-ci/lite-action@v1.1.0
|
||||||
|
if: always()
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: [ pre-commit ]
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
os: [ windows-latest, ubuntu-latest, macos-13 ]
|
||||||
|
max-parallel: 1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4.1.6
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v4.2.1
|
||||||
with:
|
with:
|
||||||
path: ~/.m2
|
distribution: 'temurin'
|
||||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
java-version: 21
|
||||||
restore-keys: ${{ runner.os }}-m2-
|
architecture: x64
|
||||||
|
cache: 'maven'
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn --no-transfer-progress verify
|
run: mvn --no-transfer-progress verify
|
||||||
- name: "Set up QEMU"
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
uses: docker/setup-qemu-action@v2.2.0
|
|
||||||
- name: "Set up Docker Buildx"
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: "Verify Docker WebGoat build"
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
uses: docker/build-push-action@v5.1.0
|
|
||||||
with:
|
|
||||||
context: ./
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: false
|
|
||||||
build-args: |
|
|
||||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
|
||||||
- name: "Verify Docker WebGoat desktop build"
|
|
||||||
uses: docker/build-push-action@v5.1.0
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
with:
|
|
||||||
context: ./
|
|
||||||
file: ./Dockerfile_desktop
|
|
||||||
push: false
|
|
||||||
build-args: |
|
|
||||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
|
||||||
|
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
@ -8,24 +8,20 @@ jobs:
|
|||||||
if: github.repository == 'WebGoat/WebGoat'
|
if: github.repository == 'WebGoat/WebGoat'
|
||||||
name: Release WebGoat
|
name: Release WebGoat
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
environment:
|
environment:
|
||||||
name: release
|
name: release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 21
|
||||||
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: |
|
||||||
@ -72,7 +68,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@v2.2.0
|
uses: docker/setup-qemu-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
platforms: all
|
platforms: all
|
||||||
|
|
||||||
@ -80,18 +76,18 @@ 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.0.0
|
uses: docker/login-action@v3.3.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@v5.1.0
|
uses: docker/build-push-action@v6.9.0
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64, linux/arm64, linux/arm/v7
|
platforms: linux/amd64, linux/arm64
|
||||||
tags: |
|
tags: |
|
||||||
webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }}
|
webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }}
|
||||||
webgoat/webgoat:latest
|
webgoat/webgoat:latest
|
||||||
@ -99,7 +95,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@v5.1.0
|
uses: docker/build-push-action@v6.9.0
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./Dockerfile_desktop
|
file: ./Dockerfile_desktop
|
||||||
@ -116,15 +112,15 @@ jobs:
|
|||||||
needs: [ release ]
|
needs: [ release ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 21
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|
||||||
- name: Set version to next snapshot
|
- name: Set version to next snapshot
|
||||||
@ -145,4 +141,3 @@ jobs:
|
|||||||
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
title: ${{ github.event.commits[0].message }}
|
title: ${{ github.event.commits[0].message }}
|
||||||
target_branch: main
|
target_branch: main
|
||||||
|
|
||||||
|
22
.github/workflows/test.yml
vendored
22
.github/workflows/test.yml
vendored
@ -21,27 +21,21 @@ 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@v3
|
- uses: actions/checkout@v4.1.6
|
||||||
# Uses an action to add Python to the VM
|
# Uses an action to add Python to the VM
|
||||||
- name: Setup Pyton
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
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 17 to the VM (and mvn?)
|
# Uses an action to add JDK 21 to the VM (and mvn?)
|
||||||
- name: set up JDK 17
|
- name: set up JDK 21
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4.2.1
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 21
|
||||||
architecture: x64
|
architecture: x64
|
||||||
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
|
cache: 'maven'
|
||||||
- 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: |
|
||||||
|
2
.github/workflows/welcome.yml
vendored
2
.github/workflows/welcome.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
if: github.repository == 'WebGoat/WebGoat'
|
if: github.repository == 'WebGoat/WebGoat'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/first-interaction@v1.2.0
|
- uses: actions/first-interaction@v1.3.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
issue-message: 'Thanks for submitting your first issue, we will have a look as quickly as possible.'
|
issue-message: 'Thanks for submitting your first issue, we will have a look as quickly as possible.'
|
||||||
|
28
.pre-commit-config.yaml
Normal file
28
.pre-commit-config.yaml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
ci:
|
||||||
|
autofix_commit_msg: |
|
||||||
|
[pre-commit.ci] auto fixes from pre-commit.com hooks
|
||||||
|
autofix_prs: false # managed in the action step
|
||||||
|
autoupdate_branch: ""
|
||||||
|
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
|
||||||
|
autoupdate_schedule: weekly
|
||||||
|
skip: []
|
||||||
|
submodules: false
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.4.0
|
||||||
|
hooks:
|
||||||
|
- id: check-yaml
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
exclude: ^(README.md|CREATE_RELEASE.md)
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||||
|
rev: v9.5.0
|
||||||
|
hooks:
|
||||||
|
- id: commitlint
|
||||||
|
stages: [commit-msg]
|
||||||
|
- repo: https://github.com/ejba/pre-commit-maven
|
||||||
|
rev: v0.3.4
|
||||||
|
hooks:
|
||||||
|
- id: maven
|
||||||
|
args: [ 'clean compile' ]
|
||||||
|
- id: maven-spotless-apply
|
19
Dockerfile
19
Dockerfile
@ -1,6 +1,8 @@
|
|||||||
FROM docker.io/eclipse-temurin:19-jre-focal
|
# We need JDK as some of the lessons needs to be able to compile Java code
|
||||||
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
FROM docker.io/eclipse-temurin:21-jdk-jammy
|
||||||
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 && \
|
||||||
@ -14,6 +16,8 @@ COPY --chown=webgoat target/webgoat-*.jar /home/webgoat/webgoat.jar
|
|||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
EXPOSE 9090
|
EXPOSE 9090
|
||||||
|
|
||||||
|
ENV TZ=Europe/Amsterdam
|
||||||
|
|
||||||
WORKDIR /home/webgoat
|
WORKDIR /home/webgoat
|
||||||
ENTRYPOINT [ "java", \
|
ENTRYPOINT [ "java", \
|
||||||
"-Duser.home=/home/webgoat", \
|
"-Duser.home=/home/webgoat", \
|
||||||
@ -30,8 +34,7 @@ ENTRYPOINT [ "java", \
|
|||||||
"--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \
|
"--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \
|
||||||
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
|
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
|
||||||
"-Drunning.in.docker=true", \
|
"-Drunning.in.docker=true", \
|
||||||
"-Dwebgoat.host=0.0.0.0", \
|
"-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
|
||||||
"-Dwebwolf.host=0.0.0.0", \
|
|
||||||
"-Dwebgoat.port=8080", \
|
HEALTHCHECK --interval=5s --timeout=3s \
|
||||||
"-Dwebwolf.port=9090", \
|
CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1
|
||||||
"-jar", "webgoat.jar" ]
|
|
||||||
|
@ -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"
|
||||||
MAINTAINER "WebGoat team"
|
LABEL maintainer = "WebGoat team"
|
||||||
|
|
||||||
WORKDIR /config
|
WORKDIR /config
|
||||||
|
|
||||||
@ -9,26 +9,38 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
|
|||||||
COPY config/desktop/start_zap.sh /config/start_zap.sh
|
COPY config/desktop/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 && \
|
||||||
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \
|
echo ${ARCH}
|
||||||
tar zfxv ZAP_2.12.0_Linux.tar.gz && \
|
|
||||||
rm -rf ZAP_2.12.0_Linux.tar.gz && \
|
RUN \
|
||||||
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 && \
|
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \
|
||||||
tar zfxv OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
tar zfxv ZAP_2.15.0_Linux.tar.gz && \
|
||||||
rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \
|
rm -rf ZAP_2.15.0_Linux.tar.gz
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
case $(uname -m) in \
|
||||||
|
x86_64) ARCH=x64;; \
|
||||||
|
aarch64) ARCH=aarch64;; \
|
||||||
|
*) ARCH=unknown;; \
|
||||||
|
esac && \
|
||||||
|
echo "oeps == ${ARCH}==" && \
|
||||||
|
curl -L https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_"${ARCH}"_linux_hotspot_21.0.3_9.tar.gz -o java.tar.gz && \
|
||||||
|
tar zfxv java.tar.gz && \
|
||||||
|
rm -rf java.tar.gz && \
|
||||||
chmod +x /config/start_webgoat.sh && \
|
chmod +x /config/start_webgoat.sh && \
|
||||||
chmod +x /config/start_zap.sh && \
|
chmod +x /config/start_zap.sh && \
|
||||||
apt-get update && \
|
echo "JAVA_HOME=/config/jdk-21.0.3+9-jre/" >> .bash_aliases && \
|
||||||
apt-get --yes install vim nano && \
|
|
||||||
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
|
|
||||||
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
||||||
|
|
||||||
|
ENV JAVA_HOME=/config/jdk-21.0.3+9-jre
|
||||||
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
|
|
||||||
|
|
||||||
WORKDIR /config/Desktop
|
WORKDIR /config/Desktop
|
||||||
|
1
FAQ.md
1
FAQ.md
@ -5,4 +5,3 @@
|
|||||||
### Integration tests fail
|
### Integration tests fail
|
||||||
|
|
||||||
Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line.
|
Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line.
|
||||||
|
|
||||||
|
38
README.md
38
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)
|
||||||
@ -44,19 +44,27 @@ Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/
|
|||||||
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to reuse the container, give it a name:
|
For some lessons you need the container run in the same timezone. For this you can set the TZ environment variable.
|
||||||
|
E.g.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=America/Boise webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
As long as you don't remove the container you can use:
|
If you want to use OWASP ZAP or another proxy, you can no longer use 127.0.0.1 or localhost. but
|
||||||
|
you can use custom host entries. For example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker start webgoat
|
127.0.0.1 www.webgoat.local www.webwolf.local
|
||||||
```
|
```
|
||||||
|
|
||||||
This way, you can start where you left off. If you remove the container, you need to use `docker run` again.
|
Then you can run the container with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e WEBGOAT_HOST=www.webgoat.local -e WEBWOLF_HOST=www.webwolf.local -e TZ=America/Boise webgoat/webgoat
|
||||||
|
```
|
||||||
|
|
||||||
|
Then visit http://www.webgoat.local:8080/WebGoat/ and http://www.webwolf.local:9090/WebWolf/
|
||||||
|
|
||||||
## 2. Run using Docker with complete Linux Desktop
|
## 2. Run using Docker with complete Linux Desktop
|
||||||
|
|
||||||
@ -71,16 +79,27 @@ docker run -p 127.0.0.1:3000:3000 webgoat/webgoat-desktop
|
|||||||
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-2023.4.jar
|
export TZ=Europe/Amsterdam # or your timezone
|
||||||
|
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Click the link in the log to start WebGoat.
|
Click the link in the log to start WebGoat.
|
||||||
|
|
||||||
|
### 3.1 Running on a different port
|
||||||
|
|
||||||
|
If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002
|
||||||
|
```
|
||||||
|
|
||||||
|
For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties).
|
||||||
|
|
||||||
## 4. Run from the sources
|
## 4. Run from the sources
|
||||||
|
|
||||||
### Prerequisites:
|
### Prerequisites:
|
||||||
|
|
||||||
* Java 17
|
* Java 17 or 21
|
||||||
* Your favorite IDE
|
* Your favorite IDE
|
||||||
* Git, or Git support in your IDE
|
* Git, or Git support in your IDE
|
||||||
|
|
||||||
@ -132,9 +151,10 @@ For specialist only. There is a way to set up WebGoat with a personalized menu.
|
|||||||
For instance running as a jar on a Linux/macOS it will look like this:
|
For instance running as a jar on a Linux/macOS it will look like this:
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
|
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.4-SNAPSHOT.jar
|
java -jar target/webgoat-2023.8-SNAPSHOT.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
||||||
|
@ -17,18 +17,18 @@ The following steps are required when you want to add a new language
|
|||||||
1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html)
|
1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html)
|
||||||
1. Add the parts for showing the flag and providing the correct value for the flag= parameter
|
1. Add the parts for showing the flag and providing the correct value for the flag= parameter
|
||||||
2.
|
2.
|
||||||
3. Add a flag image to src/main/resources/webgoat/static/css/img
|
2. Add a flag image to src/main/resources/webgoat/static/css/img
|
||||||
1. See the main_new.html for a link to download flag resources
|
1. See the main_new.html for a link to download flag resources
|
||||||
4. Add a welcome page to the introduction lesson
|
3. Add a welcome page to the introduction lesson
|
||||||
1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish)
|
1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish)
|
||||||
2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable
|
2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable
|
||||||
5. Translate the main labels
|
4. Translate the main labels
|
||||||
1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish)
|
1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish)
|
||||||
2. Translate the label values
|
2. Translate the label values
|
||||||
6. Optionally translate lessons by
|
5. Optionally translate lessons by
|
||||||
1. Adding lang specifc adoc files in documentation folder of the lesson
|
1. Adding lang specifc adoc files in documentation folder of the lesson
|
||||||
2. Adding WebGoatLabels.properties of a specific language if you want to
|
2. Adding WebGoatLabels.properties of a specific language if you want to
|
||||||
7. Run mvn clean to see if the LabelAndHintIntegration test passes
|
6. Run mvn clean to see if the LabelAndHintIntegration test passes
|
||||||
8. Run WebGoat and verify that your own language and the other languages work as expected
|
7. Run WebGoat and verify that your own language and the other languages work as expected
|
||||||
|
|
||||||
If you only want to translate more for a certain language, you only need to do step 4-8
|
If you only want to translate more for a certain language, you only need to do step 4-8
|
||||||
|
@ -1,5 +1,25 @@
|
|||||||
# WebGoat release notes
|
# WebGoat release notes
|
||||||
|
|
||||||
|
## Version 2023.8
|
||||||
|
|
||||||
|
### 🚀 New functionality
|
||||||
|
|
||||||
|
- Consistent environment values and url references (#1677)
|
||||||
|
- Show directly requested file in requests overview
|
||||||
|
- Show creating time in file upload overview
|
||||||
|
|
||||||
|
### 🐞 Bug fixes
|
||||||
|
|
||||||
|
- Fix startup message (#1687)
|
||||||
|
- Fix/state of software supply chain links (#1683)
|
||||||
|
- Fix WebWolf UI (#1686)
|
||||||
|
|
||||||
|
### 🔄 Technical tasks
|
||||||
|
|
||||||
|
- bump actions/setup-java from 3 to 4 (#1690)
|
||||||
|
- bump commons-io:commons-io from 2.14.0 to 2.15.1 (#1689)
|
||||||
|
- bump com.diffplug.spotless:spotless-maven-plugin (#1688)
|
||||||
|
|
||||||
## Version 2023.5
|
## Version 2023.5
|
||||||
|
|
||||||
### New functionality
|
### New functionality
|
||||||
@ -195,4 +215,3 @@ Special thanks to the following contributors providing us with a pull request:
|
|||||||
And everyone who provided feedback through Github.
|
And everyone who provided feedback through Github.
|
||||||
|
|
||||||
Team WebGoat
|
Team WebGoat
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
/config/jdk-17.0.6+10-jre/bin/java \
|
/config/jdk-21.0.3+9-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-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
|
/config/jdk-21.0.3+9-jre/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# WebGoat landing page
|
# WebGoat landing page
|
||||||
|
|
||||||
Old GitHub page which now redirects to OWASP website.
|
Old GitHub page which now redirects to OWASP website.
|
||||||
|
|
||||||
|
203
pom.xml
203
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.1.5</version>
|
<version>3.3.5</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat</artifactId>
|
<artifactId>webgoat</artifactId>
|
||||||
<version>2023.5</version>
|
<version>2024.2-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>WebGoat</name>
|
<name>WebGoat</name>
|
||||||
@ -29,13 +29,6 @@
|
|||||||
</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>
|
||||||
@ -43,11 +36,6 @@
|
|||||||
<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>
|
||||||
@ -58,43 +46,8 @@
|
|||||||
<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>
|
||||||
@ -109,62 +62,56 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Shared properties with plugins and version numbers across submodules-->
|
<!-- Shared properties with plugins and version numbers across submodules-->
|
||||||
<asciidoctorj.version>2.5.10</asciidoctorj.version>
|
<asciidoctorj.version>3.0.0</asciidoctorj.version>
|
||||||
<bootstrap.version>5.3.1</bootstrap.version>
|
<bootstrap.version>5.3.3</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.3.1</checkstyle.version>
|
<checkstyle.version>3.6.0</checkstyle.version>
|
||||||
<commons-collections.version>3.2.1</commons-collections.version>
|
<commons-collections.version>3.2.1</commons-collections.version>
|
||||||
<commons-io.version>2.14.0</commons-io.version>
|
<commons-compress.version>1.27.1</commons-compress.version>
|
||||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
<commons-io.version>2.17.0</commons-io.version>
|
||||||
<commons-text.version>1.10.0</commons-text.version>
|
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||||
<guava.version>32.1.3-jre</guava.version>
|
<commons-text.version>1.12.0</commons-text.version>
|
||||||
|
<guava.version>33.3.1-jre</guava.version>
|
||||||
<jacoco.version>0.8.11</jacoco.version>
|
<jacoco.version>0.8.11</jacoco.version>
|
||||||
<java.version>17</java.version>
|
<java.version>21</java.version>
|
||||||
<jaxb.version>2.3.1</jaxb.version>
|
<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.0</jquery.version>
|
<jquery.version>3.7.1</jquery.version>
|
||||||
<jsoup.version>1.16.1</jsoup.version>
|
<jsoup.version>1.18.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.2.1</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.5.2</maven-surefire-plugin.version>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>21</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.1.RELEASE</thymeleaf.version>
|
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
|
||||||
<webdriver.version>5.3.3</webdriver.version>
|
<waittimeForServerStart>60</waittimeForServerStart>
|
||||||
|
<webdriver.version>5.9.2</webdriver.version>
|
||||||
<webgoat.context>/</webgoat.context>
|
<webgoat.context>/</webgoat.context>
|
||||||
<webgoat.port>8080</webgoat.port>
|
<webgoat.sslenabled>false</webgoat.sslenabled>
|
||||||
<webjars-locator-core.version>0.53</webjars-locator-core.version>
|
<webjars-locator-core.version>0.59</webjars-locator-core.version>
|
||||||
<webwolf.context>/</webwolf.context>
|
<webwolf.context>/</webwolf.context>
|
||||||
<webwolf.port>9090</webwolf.port>
|
<wiremock.version>3.9.2</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.8.0</zxcvbn.version>
|
<zxcvbn.version>1.9.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.3</version>
|
<version>1.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.asciidoctor</groupId>
|
<groupId>org.asciidoctor</groupId>
|
||||||
@ -249,8 +196,8 @@
|
|||||||
<version>${webjars-locator-core.version}</version>
|
<version>${webjars-locator-core.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>org.wiremock</groupId>
|
||||||
<artifactId>wiremock</artifactId>
|
<artifactId>wiremock-standalone</artifactId>
|
||||||
<version>${wiremock.version}</version>
|
<version>${wiremock.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -261,12 +208,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.25.0</version>
|
<version>${commons-compress.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jruby</groupId>
|
<groupId>org.jruby</groupId>
|
||||||
<artifactId>jruby</artifactId>
|
<artifactId>jruby</artifactId>
|
||||||
<version>9.4.3.0</version>
|
<version>9.4.9.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
@ -285,24 +232,26 @@
|
|||||||
<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.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testcontainers</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>1.20.3</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>
|
||||||
@ -312,6 +261,10 @@
|
|||||||
<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>
|
||||||
@ -418,6 +371,12 @@
|
|||||||
<artifactId>jaxb-impl</artifactId>
|
<artifactId>jaxb-impl</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.terma</groupId>
|
||||||
|
<artifactId>javaniotcpproxy</artifactId>
|
||||||
|
<version>1.6</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -430,10 +389,8 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.tomakehurst</groupId>
|
<groupId>org.wiremock</groupId>
|
||||||
<artifactId>wiremock</artifactId>
|
<artifactId>wiremock-standalone</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>
|
||||||
@ -513,10 +470,19 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
<environmentVariables>
|
||||||
|
<WEBGOAT_SSLENABLED>${webgoat.sslenabled}</WEBGOAT_SSLENABLED>
|
||||||
|
<WEBGOAT_HOST>127.0.0.1</WEBGOAT_HOST>
|
||||||
|
<WEBGOAT_PORT>${webgoat.port}</WEBGOAT_PORT>
|
||||||
|
<WEBGOAT_CONTEXT>${webgoat.context}</WEBGOAT_CONTEXT>
|
||||||
|
<WEBWOLF_HOST>127.0.0.1</WEBWOLF_HOST>
|
||||||
|
<WEBWOLF_PORT>${webwolf.port}</WEBWOLF_PORT>
|
||||||
|
<WEBWOLF_CONTEXT>${webwolf.context}</WEBWOLF_CONTEXT>
|
||||||
|
</environmentVariables>
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile>
|
<logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile>
|
||||||
</systemPropertyVariables>
|
</systemPropertyVariables>
|
||||||
<argLine>-Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} -Dwebwolfcontext=${webwolf.context} -Dwebgoatcontext=${webgoat.context}</argLine>
|
<argLine>-Xmx512m</argLine>
|
||||||
<includes>org/owasp/webgoat/*Test</includes>
|
<includes>org/owasp/webgoat/*Test</includes>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
@ -540,6 +506,7 @@
|
|||||||
<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
|
||||||
@ -547,8 +514,6 @@
|
|||||||
--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>
|
||||||
@ -557,7 +522,6 @@
|
|||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
<version>${checkstyle.version}</version>
|
<version>${checkstyle.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<encoding>UTF-8</encoding>
|
|
||||||
<consoleOutput>true</consoleOutput>
|
<consoleOutput>true</consoleOutput>
|
||||||
<failsOnError>true</failsOnError>
|
<failsOnError>true</failsOnError>
|
||||||
<configLocation>config/checkstyle/checkstyle.xml</configLocation>
|
<configLocation>config/checkstyle/checkstyle.xml</configLocation>
|
||||||
@ -568,7 +532,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.diffplug.spotless</groupId>
|
<groupId>com.diffplug.spotless</groupId>
|
||||||
<artifactId>spotless-maven-plugin</artifactId>
|
<artifactId>spotless-maven-plugin</artifactId>
|
||||||
<version>2.38.0</version>
|
<version>2.41.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<formats>
|
<formats>
|
||||||
<format>
|
<format>
|
||||||
@ -629,7 +593,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.3.0</version>
|
<version>3.5.0</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>restrict-log4j-versions</id>
|
<id>restrict-log4j-versions</id>
|
||||||
@ -653,10 +617,6 @@
|
|||||||
<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>
|
||||||
@ -686,16 +646,15 @@
|
|||||||
<portNames>
|
<portNames>
|
||||||
<portName>webgoat.port</portName>
|
<portName>webgoat.port</portName>
|
||||||
<portName>webwolf.port</portName>
|
<portName>webwolf.port</portName>
|
||||||
<portName>jmxPort</portName>
|
|
||||||
</portNames>
|
</portNames>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.bazaarvoice.maven.plugins</groupId>
|
<groupId>org.honton.chas</groupId>
|
||||||
<artifactId>process-exec-maven-plugin</artifactId>
|
<artifactId>process-exec-maven-plugin</artifactId>
|
||||||
<version>0.9</version>
|
<version>0.9.2</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>start-jar</id>
|
<id>start-jar</id>
|
||||||
@ -703,8 +662,18 @@
|
|||||||
<goal>start</goal>
|
<goal>start</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<phase>pre-integration-test</phase>
|
<phase>pre-integration-test</phase>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<workingDir>${project.build.directory}</workingDir>
|
<workingDir>${project.build.directory}</workingDir>
|
||||||
|
<environment>
|
||||||
|
<WEBGOAT_SSLENABLED>${webgoat.sslenabled}</WEBGOAT_SSLENABLED>
|
||||||
|
<WEBGOAT_HOST>127.0.0.1</WEBGOAT_HOST>
|
||||||
|
<WEBGOAT_PORT>${webgoat.port}</WEBGOAT_PORT>
|
||||||
|
<WEBGOAT_CONTEXT>${webgoat.context}</WEBGOAT_CONTEXT>
|
||||||
|
<WEBWOLF_HOST>127.0.0.1</WEBWOLF_HOST>
|
||||||
|
<WEBWOLF_PORT>${webwolf.port}</WEBWOLF_PORT>
|
||||||
|
<WEBWOLF_CONTEXT>${webwolf.context}</WEBWOLF_CONTEXT>
|
||||||
|
</environment>
|
||||||
<arguments>
|
<arguments>
|
||||||
<argument>java</argument>
|
<argument>java</argument>
|
||||||
<argument>-jar</argument>
|
<argument>-jar</argument>
|
||||||
@ -712,10 +681,6 @@
|
|||||||
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||||
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||||
<argument>-Dspring.main.banner-mode=off</argument>
|
<argument>-Dspring.main.banner-mode=off</argument>
|
||||||
<argument>-Dwebgoat.port=${webgoat.port}</argument>
|
|
||||||
<argument>-Dwebgoat.context=${webgoat.context}</argument>
|
|
||||||
<argument>-Dwebwolf.port=${webwolf.port}</argument>
|
|
||||||
<argument>-Dwebwolf.context=${webwolf.context}</argument>
|
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
<argument>java.base/java.lang=ALL-UNNAMED</argument>
|
<argument>java.base/java.lang=ALL-UNNAMED</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
@ -723,26 +688,18 @@
|
|||||||
<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>120</waitAfterLaunch>
|
<waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
|
||||||
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/actuator/health</healthcheckUrl>
|
<healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}login</healthCheckUrl>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
@ -767,7 +724,6 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.owasp</groupId>
|
<groupId>org.owasp</groupId>
|
||||||
<artifactId>dependency-check-maven</artifactId>
|
<artifactId>dependency-check-maven</artifactId>
|
||||||
<version>8.4.3</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<failBuildOnCVSS>7</failBuildOnCVSS>
|
<failBuildOnCVSS>7</failBuildOnCVSS>
|
||||||
<skipProvidedScope>false</skipProvidedScope>
|
<skipProvidedScope>false</skipProvidedScope>
|
||||||
@ -816,7 +772,6 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>${jacoco.version}</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>before-unit-test</id>
|
<id>before-unit-test</id>
|
||||||
|
@ -15,7 +15,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
assignment2();
|
assignment2();
|
||||||
assignment3();
|
assignment3();
|
||||||
|
|
||||||
checkResults("/access-control");
|
checkResults("MissingFunctionAC");
|
||||||
}
|
}
|
||||||
|
|
||||||
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(webWolfUrl("fileupload"))
|
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||||
.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(webWolfUrl("files/" + this.getUser() + "/" + htmlName))
|
.get(new WebWolfUrlBuilder().path("files/%s/%s", this.getUser(), htmlName).build())
|
||||||
.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", webWolfUrl("files/fake.html"))
|
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||||
.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", webWolfUrl("files/fake.html"))
|
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||||
.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", webWolfUrl("files/fake.html"))
|
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||||
.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", webWolfUrl("files/fake.html"))
|
.header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
|
||||||
.params(params)
|
.params(params)
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
@ -253,15 +253,16 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
Overview[] assignments =
|
Overview[] assignments =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/lessonoverview.mvc"))
|
.relaxedHTTPSValidation()
|
||||||
|
.get(url("service/lessonoverview.mvc/CSRF"))
|
||||||
.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"), params, true);
|
checkAssignment(url("challenge/flag/1"), params, true);
|
||||||
|
|
||||||
checkResults("/challenge/1");
|
checkResults("Challenge1");
|
||||||
|
|
||||||
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"), params, true);
|
checkAssignment(url("challenge/flag/5"), params, true);
|
||||||
|
|
||||||
checkResults("/challenge/5");
|
checkResults("Challenge5");
|
||||||
|
|
||||||
List<String> capturefFlags =
|
List<String> capturefFlags =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -126,7 +126,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.extract()
|
.extract()
|
||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
// Should send an email to WebWolf inbox this should give a hint to the link being static
|
// Should email WebWolf inbox this should give a hint to the link being static
|
||||||
RestAssured.given()
|
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(webWolfUrl("mail"))
|
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||||
.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"), Map.of("flag", flag), true);
|
checkAssignment(url("challenge/flag/7"), Map.of("flag", flag), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
checkAssignmentDefaults();
|
checkAssignmentDefaults();
|
||||||
|
|
||||||
checkResults("/crypto");
|
checkResults("Cryptography");
|
||||||
}
|
}
|
||||||
|
|
||||||
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,7 +31,17 @@ 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
|
||||||
@ -51,7 +61,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
.path("lessonCompleted"),
|
.path("lessonCompleted"),
|
||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
|
|
||||||
checkResults("/HttpProxies/");
|
checkResults("HttpProxies");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -73,7 +83,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
|
||||||
@ -96,7 +106,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +118,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
|
||||||
@ -118,7 +128,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();
|
||||||
@ -128,7 +138,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("/auth-bypass/");
|
checkResults("AuthBypass");
|
||||||
|
|
||||||
startLesson("HttpProxies");
|
startLesson("HttpProxies");
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
@ -144,7 +154,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
.extract()
|
.extract()
|
||||||
.path("lessonCompleted"),
|
.path("lessonCompleted"),
|
||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
checkResults("/HttpProxies/");
|
checkResults("HttpProxies");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -180,7 +190,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
|
||||||
@ -194,7 +204,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("/auth-bypass/");
|
checkResults("AuthBypass");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -205,6 +215,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("/lesson-template/");
|
checkResults("LessonTemplate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,9 +3,9 @@ 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 java.util.Objects;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
@ -15,41 +15,81 @@ import org.springframework.http.HttpStatus;
|
|||||||
|
|
||||||
public abstract class IntegrationTest {
|
public abstract class IntegrationTest {
|
||||||
|
|
||||||
private static String webGoatPort =
|
private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
|
||||||
Objects.requireNonNull(System.getProperty("webgoatport", "8080"));
|
@Getter private static String webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090");
|
||||||
private static String webGoatContext =
|
|
||||||
Objects.requireNonNull(System.getProperty("webgoatcontext", "/WebGoat/"));
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static String webWolfPort =
|
private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
|
||||||
Objects.requireNonNull(System.getProperty("webwolfport", "9090"));
|
|
||||||
|
|
||||||
|
private static String webGoatContext =
|
||||||
|
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
|
||||||
private static String webWolfContext =
|
private static String webWolfContext =
|
||||||
Objects.requireNonNull(System.getProperty("webwolfcontext", "/WebWolf/"));
|
System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
|
||||||
|
|
||||||
private static boolean useSSL = false;
|
|
||||||
private static String webgoatUrl =
|
|
||||||
(useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + webGoatContext;
|
|
||||||
private static String webWolfUrl =
|
|
||||||
(useSSL ? "https:" : "http:") + "//localhost:" + 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 webgoatUrl + url;
|
return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String webWolfUrl(String url) {
|
protected class WebWolfUrlBuilder {
|
||||||
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) {
|
/**
|
||||||
return webWolfUrl("files") + "/" + getUser() + "/" + fileName;
|
* In attack mode it means WebGoat calls WebWolf to perform an attack. In this case we need to
|
||||||
|
* use port 9090 in a Docker environment.
|
||||||
|
*/
|
||||||
|
protected WebWolfUrlBuilder attackMode() {
|
||||||
|
attackMode = true;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected WebWolfUrlBuilder path(String path) {
|
||||||
|
this.path = path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WebWolfUrlBuilder path(String path, String... uriVariables) {
|
||||||
|
this.path = path.formatted(uriVariables);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debugging options: install TestContainers Desktop and map port 5005 to the host machine with
|
||||||
|
* https://newsletter.testcontainers.com/announcements/set-fixed-ports-to-easily-debug-development-services
|
||||||
|
*
|
||||||
|
* <p>Start the test and connect a remote debugger in IntelliJ to localhost:5005 and attach it.
|
||||||
|
*/
|
||||||
|
// private static GenericContainer<?> webGoatContainer =
|
||||||
|
// new GenericContainer(new ImageFromDockerfile("webgoat").withFileFromPath("/",
|
||||||
|
// Paths.get(".")))
|
||||||
|
// .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("webgoat")))
|
||||||
|
// .withExposedPorts(8080, 9090, 5005)
|
||||||
|
// .withEnv(
|
||||||
|
// "_JAVA_OPTIONS",
|
||||||
|
// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005")
|
||||||
|
// .waitingFor(Wait.forHealthcheck());
|
||||||
|
//
|
||||||
|
// static {
|
||||||
|
// webGoatContainer.start();
|
||||||
|
// }
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void login() {
|
public void login() {
|
||||||
|
login("webgoat");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void login(String user) {
|
||||||
String location =
|
String location =
|
||||||
given()
|
given()
|
||||||
.when()
|
.when()
|
||||||
@ -58,6 +98,8 @@ 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()
|
||||||
@ -98,7 +140,7 @@ public abstract class IntegrationTest {
|
|||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
.formParam("password", "password")
|
.formParam("password", "password")
|
||||||
.post(webWolfUrl("login"))
|
.post(new WebWolfUrlBuilder().path("login").build())
|
||||||
.then()
|
.then()
|
||||||
.statusCode(302)
|
.statusCode(302)
|
||||||
.cookie("WEBWOLFSESSION")
|
.cookie("WEBWOLFSESSION")
|
||||||
@ -129,7 +171,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/restartlesson.mvc"))
|
.get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName)))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
}
|
}
|
||||||
@ -165,23 +207,18 @@ public abstract class IntegrationTest {
|
|||||||
CoreMatchers.is(expectedResult));
|
CoreMatchers.is(expectedResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
|
public void checkResults(String lesson) {
|
||||||
// only required to be in the same package)
|
var result =
|
||||||
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"))
|
.get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson)))
|
||||||
.then()
|
.andReturn();
|
||||||
.statusCode(200)
|
|
||||||
.extract()
|
MatcherAssert.assertThat(
|
||||||
.jsonPath()
|
result.then().statusCode(200).extract().jsonPath().getList("solved"),
|
||||||
.getList("assignment.path"),
|
CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||||
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkResults() {
|
public void checkResults() {
|
||||||
@ -236,7 +273,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("file-server-location"))
|
.get(new WebWolfUrlBuilder().path("file-server-location").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -264,7 +301,7 @@ public abstract class IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.delete(webWolfUrl("mail"))
|
.delete(new WebWolfUrlBuilder().path("mail").build())
|
||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.ACCEPTED.value());
|
.statusCode(HttpStatus.ACCEPTED.value());
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ 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;
|
||||||
@ -34,7 +33,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, InvalidKeyException, NoSuchAlgorithmException {
|
public void solveAssignment() throws IOException, NoSuchAlgorithmException {
|
||||||
startLesson("JWT");
|
startLesson("JWT");
|
||||||
|
|
||||||
decodingToken();
|
decodingToken();
|
||||||
@ -51,11 +50,10 @@ 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")
|
||||||
@ -96,7 +94,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
CoreMatchers.is(true));
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
private void findPassword() {
|
||||||
|
|
||||||
String accessToken =
|
String accessToken =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -256,7 +254,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(webWolfUrl("fileupload"))
|
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -265,7 +263,10 @@ 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(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json"));
|
header.put(
|
||||||
|
JwsHeader.JWK_SET_URL,
|
||||||
|
new WebWolfUrlBuilder().attackMode().path("files/%s/jwks.json", getUser()).build());
|
||||||
|
|
||||||
String token =
|
String token =
|
||||||
Jwts.builder()
|
Jwts.builder()
|
||||||
.setHeader(header)
|
.setHeader(header)
|
||||||
|
@ -151,7 +151,6 @@ 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) {
|
||||||
|
@ -69,7 +69,6 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
// WebWolf
|
// WebWolf
|
||||||
var link = getPasswordResetLinkFromLandingPage();
|
var link = getPasswordResetLinkFromLandingPage();
|
||||||
|
|
||||||
// WebGoat
|
// WebGoat
|
||||||
changePassword(link);
|
changePassword(link);
|
||||||
checkAssignment(
|
checkAssignment(
|
||||||
@ -86,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("mail"))
|
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -100,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) {
|
||||||
@ -120,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("requests"))
|
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -137,7 +136,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
private void clickForgotEmailLink(String user) {
|
private void clickForgotEmailLink(String user) {
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.header(HttpHeaders.HOST, String.format("%s:%s", "127.0.0.1", getWebWolfPort()))
|
.header(HttpHeaders.HOST, String.format("%s:%s", getWebWolfHost(), getWebWolfPort()))
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.formParams("email", user)
|
.formParams("email", user)
|
||||||
|
@ -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"));
|
.post(url("challenge/flag/1"));
|
||||||
};
|
};
|
||||||
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,6 +1,5 @@
|
|||||||
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;
|
||||||
@ -8,7 +7,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
public class SSRFIntegrationTest extends IntegrationTest {
|
public class SSRFIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void runTests() throws IOException {
|
public void runTests() {
|
||||||
startLesson("SSRF");
|
startLesson("SSRF");
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
@ -21,6 +20,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();
|
checkResults("SqlInjectionMitigations");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("mail"))
|
.get(new WebWolfUrlBuilder().path("mail").build())
|
||||||
.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(webWolfUrl("landing"))
|
.get(new WebWolfUrlBuilder().path("landing").build())
|
||||||
.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(webWolfUrl("requests"))
|
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||||
.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("/WebWolf");
|
checkResults("WebWolfIntroduction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,9 +3,6 @@ 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 {
|
||||||
@ -28,47 +25,40 @@ 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
|
// */
|
||||||
public void xxeSecure() throws IOException {
|
// @Test
|
||||||
startLesson("XXE");
|
// public void xxeSecure() throws IOException {
|
||||||
webGoatHomeDirectory = webGoatServerDirectory();
|
// startLesson("XXE");
|
||||||
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
// webGoatHomeDirectory = webGoatServerDirectory();
|
||||||
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() throws IOException {
|
private String getSecret() {
|
||||||
// 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 dtd7String =
|
String webWolfCallback = new WebWolfUrlBuilder().path("landing").attackMode().build();
|
||||||
dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile);
|
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile);
|
||||||
|
|
||||||
// upload DTD
|
// upload DTD
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -76,15 +66,17 @@ 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(webWolfUrl("fileupload"))
|
.post(new WebWolfUrlBuilder().path("fileupload").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
.getBody()
|
.getBody()
|
||||||
.asString();
|
.asString();
|
||||||
|
|
||||||
// upload attack
|
// upload attack
|
||||||
String xxe7String =
|
String xxe7String =
|
||||||
xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser());
|
xxe7.replace("WEBWOLFURL", new WebWolfUrlBuilder().attackMode().path("files").build())
|
||||||
|
.replace("USERNAME", this.getUser());
|
||||||
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
|
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
|
||||||
|
|
||||||
// read results from WebWolf
|
// read results from WebWolf
|
||||||
@ -93,7 +85,7 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("requests"))
|
.get(new WebWolfUrlBuilder().path("requests").build())
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.response()
|
.response()
|
||||||
@ -113,7 +105,6 @@ 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(
|
||||||
@ -121,6 +112,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,22 +32,23 @@ 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;
|
||||||
@ -135,17 +136,17 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
|
|||||||
return computedResourceName;
|
return computedResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> createAttributes() {
|
private Options 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);
|
|
||||||
|
|
||||||
Map<String, Object> options = new HashMap<>();
|
return Options.builder()
|
||||||
options.put("attributes", attributes);
|
.attributes(
|
||||||
|
Attributes.builder()
|
||||||
return options;
|
.attribute("source-highlighter", "coderay")
|
||||||
|
.attribute("backend", "xhtml")
|
||||||
|
.attribute("lang", determineLanguage())
|
||||||
|
.attribute("icons", org.asciidoctor.Attributes.FONT_ICONS)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String determineLanguage() {
|
private String determineLanguage() {
|
||||||
@ -159,7 +160,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(Headers.ACCEPT_LANGUAGE_STRING);
|
String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
|
||||||
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);
|
||||||
|
14
src/main/java/org/owasp/webgoat/container/CurrentUser.java
Normal file
14
src/main/java/org/owasp/webgoat/container/CurrentUser.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package org.owasp.webgoat.container;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
|
||||||
|
@Target({ElementType.PARAMETER, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@AuthenticationPrincipal
|
||||||
|
public @interface CurrentUser {}
|
@ -0,0 +1,14 @@
|
|||||||
|
package org.owasp.webgoat.container;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
|
||||||
|
@Target({ElementType.PARAMETER, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@AuthenticationPrincipal(expression = "#this.getUsername()")
|
||||||
|
public @interface CurrentUsername {}
|
@ -6,8 +6,8 @@ import javax.sql.DataSource;
|
|||||||
import lombok.RequiredArgsConstructor;
|
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.lessons.LessonScanner;
|
|
||||||
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;
|
||||||
@ -20,7 +20,6 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
|||||||
public class DatabaseConfiguration {
|
public class DatabaseConfiguration {
|
||||||
|
|
||||||
private final DataSourceProperties properties;
|
private final DataSourceProperties properties;
|
||||||
private final LessonScanner lessonScanner;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
@Primary
|
||||||
@ -36,8 +35,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()} for how we clean the lesson
|
* quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we
|
||||||
* related tables.
|
* clean the lesson related tables.
|
||||||
*/
|
*/
|
||||||
@Bean(initMethod = "migrate")
|
@Bean(initMethod = "migrate")
|
||||||
public Flyway flyWayContainer() {
|
public Flyway flyWayContainer() {
|
||||||
@ -62,7 +61,7 @@ public class DatabaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LessonDataSource lessonDataSource() {
|
public LessonDataSource lessonDataSource(DataSource dataSource) {
|
||||||
return new LessonDataSource(dataSource());
|
return new LessonDataSource(dataSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ import org.thymeleaf.templateresource.StringTemplateResource;
|
|||||||
public class LessonTemplateResolver extends FileTemplateResolver {
|
public class LessonTemplateResolver extends FileTemplateResolver {
|
||||||
|
|
||||||
private static final String PREFIX = "lesson:";
|
private static final String PREFIX = "lesson:";
|
||||||
private ResourceLoader resourceLoader;
|
private final ResourceLoader resourceLoader;
|
||||||
private Map<String, byte[]> resources = new HashMap<>();
|
private final Map<String, byte[]> resources = new HashMap<>();
|
||||||
|
|
||||||
public LessonTemplateResolver(ResourceLoader resourceLoader) {
|
public LessonTemplateResolver(ResourceLoader resourceLoader) {
|
||||||
this.resourceLoader = resourceLoader;
|
this.resourceLoader = resourceLoader;
|
||||||
|
@ -40,7 +40,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.owasp.webgoat.container.i18n.Language;
|
import org.owasp.webgoat.container.i18n.Language;
|
||||||
import org.owasp.webgoat.container.i18n.Messages;
|
import org.owasp.webgoat.container.i18n.Messages;
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
import org.owasp.webgoat.container.lessons.LessonScanner;
|
|
||||||
import org.owasp.webgoat.container.session.LabelDebugger;
|
import org.owasp.webgoat.container.session.LabelDebugger;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -74,8 +73,6 @@ public class MvcConfiguration implements WebMvcConfigurer {
|
|||||||
|
|
||||||
private static final String UTF8 = "UTF-8";
|
private static final String UTF8 = "UTF-8";
|
||||||
|
|
||||||
private final LessonScanner lessonScanner;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(ViewControllerRegistry registry) {
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
registry.addViewController("/login").setViewName("login");
|
registry.addViewController("/login").setViewName("login");
|
||||||
@ -187,28 +184,6 @@ public class MvcConfiguration implements WebMvcConfigurer {
|
|||||||
registry
|
registry
|
||||||
.addResourceHandler("/fonts/**")
|
.addResourceHandler("/fonts/**")
|
||||||
.addResourceLocations("classpath:/webgoat/static/fonts/");
|
.addResourceLocations("classpath:/webgoat/static/fonts/");
|
||||||
|
|
||||||
// WebGoat lessons
|
|
||||||
registry
|
|
||||||
.addResourceHandler("/images/**")
|
|
||||||
.addResourceLocations(
|
|
||||||
lessonScanner.applyPattern("classpath:/lessons/%s/images/").toArray(String[]::new));
|
|
||||||
registry
|
|
||||||
.addResourceHandler("/lesson_js/**")
|
|
||||||
.addResourceLocations(
|
|
||||||
lessonScanner.applyPattern("classpath:/lessons/%s/js/").toArray(String[]::new));
|
|
||||||
registry
|
|
||||||
.addResourceHandler("/lesson_css/**")
|
|
||||||
.addResourceLocations(
|
|
||||||
lessonScanner.applyPattern("classpath:/lessons/%s/css/").toArray(String[]::new));
|
|
||||||
registry
|
|
||||||
.addResourceHandler("/lesson_templates/**")
|
|
||||||
.addResourceLocations(
|
|
||||||
lessonScanner.applyPattern("classpath:/lessons/%s/templates/").toArray(String[]::new));
|
|
||||||
registry
|
|
||||||
.addResourceHandler("/video/**")
|
|
||||||
.addResourceLocations(
|
|
||||||
lessonScanner.applyPattern("classpath:/lessons/%s/video/").toArray(String[]::new));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -32,31 +32,27 @@
|
|||||||
package org.owasp.webgoat.container;
|
package org.owasp.webgoat.container;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import org.owasp.webgoat.container.session.UserSessionData;
|
import org.owasp.webgoat.container.session.LessonSession;
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
|
||||||
import org.owasp.webgoat.container.users.UserRepository;
|
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
import org.springframework.context.annotation.PropertySource;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
import org.springframework.context.annotation.ScopedProxyMode;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
|
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
|
||||||
@PropertySource("classpath:application-webgoat.properties")
|
@PropertySource("classpath:application-webgoat.properties")
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
|
@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"})
|
||||||
|
@EntityScan(basePackages = "org.owasp.webgoat.container")
|
||||||
public class WebGoat {
|
public class WebGoat {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Bean(name = "pluginTargetDirectory")
|
@Bean(name = "pluginTargetDirectory")
|
||||||
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
|
||||||
return new File(webgoatHome);
|
return new File(webgoatHome);
|
||||||
@ -64,21 +60,8 @@ public class WebGoat {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||||
public WebSession webSession() {
|
public LessonSession userSessionData() {
|
||||||
WebGoatUser webGoatUser = null;
|
return new LessonSession();
|
||||||
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,6 +35,7 @@ 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;
|
||||||
@ -57,7 +58,6 @@ public class WebSecurityConfig {
|
|||||||
return http.authorizeHttpRequests(
|
return http.authorizeHttpRequests(
|
||||||
auth ->
|
auth ->
|
||||||
auth.requestMatchers(
|
auth.requestMatchers(
|
||||||
"/",
|
|
||||||
"/favicon.ico",
|
"/favicon.ico",
|
||||||
"/css/**",
|
"/css/**",
|
||||||
"/images/**",
|
"/images/**",
|
||||||
@ -65,7 +65,8 @@ public class WebSecurityConfig {
|
|||||||
"fonts/**",
|
"fonts/**",
|
||||||
"/plugins/**",
|
"/plugins/**",
|
||||||
"/registration",
|
"/registration",
|
||||||
"/register.mvc")
|
"/register.mvc",
|
||||||
|
"/actuator/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated())
|
.authenticated())
|
||||||
@ -97,6 +98,7 @@ 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,7 +1,8 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.ContentNode;
|
import org.asciidoctor.ast.PhraseNode;
|
||||||
|
import org.asciidoctor.ast.StructuralNode;
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class OperatingSystemMacro extends InlineMacroProcessor {
|
public class OperatingSystemMacro extends InlineMacroProcessor {
|
||||||
@ -15,7 +16,8 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
public PhraseNode process(
|
||||||
|
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
var osName = System.getProperty("os.name");
|
var osName = System.getProperty("os.name");
|
||||||
|
|
||||||
// see
|
// see
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.ContentNode;
|
import org.asciidoctor.ast.PhraseNode;
|
||||||
|
import org.asciidoctor.ast.StructuralNode;
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.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;
|
||||||
@ -17,7 +18,8 @@ public class UsernameMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
public PhraseNode process(
|
||||||
|
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
var auth = SecurityContextHolder.getContext().getAuthentication();
|
var auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
var username = "unknown";
|
var username = "unknown";
|
||||||
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {
|
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.ContentNode;
|
import org.asciidoctor.ast.PhraseNode;
|
||||||
|
import org.asciidoctor.ast.StructuralNode;
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
||||||
@ -15,11 +16,12 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
public PhraseNode process(
|
||||||
|
StructuralNode structuralNode, String target, Map<String, Object> attributes) {
|
||||||
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
|
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(contentNode, "quoted", env);
|
return createPhraseNode(structuralNode, "quoted", env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.ContentNode;
|
import org.asciidoctor.ast.PhraseNode;
|
||||||
|
import org.asciidoctor.ast.StructuralNode;
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
|
|
||||||
public class WebGoatVersionMacro extends InlineMacroProcessor {
|
public class WebGoatVersionMacro extends InlineMacroProcessor {
|
||||||
@ -15,7 +16,8 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) {
|
public PhraseNode process(
|
||||||
|
StructuralNode contentNode, String target, Map<String, Object> attributes) {
|
||||||
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
|
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
|
||||||
|
|
||||||
// see
|
// see
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package org.owasp.webgoat.container.asciidoc;
|
package org.owasp.webgoat.container.asciidoc;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.asciidoctor.ast.ContentNode;
|
import org.asciidoctor.ast.PhraseNode;
|
||||||
|
import org.asciidoctor.ast.StructuralNode;
|
||||||
import org.asciidoctor.extension.InlineMacroProcessor;
|
import org.asciidoctor.extension.InlineMacroProcessor;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage in asciidoc:
|
* Usage in asciidoc:
|
||||||
@ -24,9 +22,10 @@ public class WebWolfMacro extends InlineMacroProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) {
|
public PhraseNode process(
|
||||||
|
StructuralNode contentNode, String linkText, Map<String, Object> attributes) {
|
||||||
var env = EnvironmentExposure.getEnv();
|
var env = EnvironmentExposure.getEnv();
|
||||||
var hostname = determineHost(env.getProperty("webwolf.port"));
|
var hostname = env.getProperty("webwolf.url");
|
||||||
var target = (String) attributes.getOrDefault("target", "home");
|
var target = (String) attributes.getOrDefault("target", "home");
|
||||||
var href = hostname + "/" + target;
|
var href = hostname + "/" + target;
|
||||||
|
|
||||||
@ -39,35 +38,10 @@ 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).convert();
|
return createPhraseNode(contentNode, "anchor", linkText, attributes, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
|
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
|
||||||
return attributes.values().stream().anyMatch(a -> a.equals("noLink"));
|
return attributes.values().stream().anyMatch(a -> a.equals("noLink"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the host from the hostname and ports that were used. The purpose is to make it
|
|
||||||
* possible to use the application behind a reverse proxy. For instance in the docker
|
|
||||||
* compose/stack version with webgoat webwolf and nginx proxy. You do not have to use the
|
|
||||||
* indicated hostname, but if you do, you should define two hosts aliases 127.0.0.1
|
|
||||||
* www.webgoat.local www.webwolf.local
|
|
||||||
*/
|
|
||||||
private String determineHost(String port) {
|
|
||||||
HttpServletRequest request =
|
|
||||||
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
|
|
||||||
String host = request.getHeader("Host");
|
|
||||||
int semicolonIndex = host.indexOf(":");
|
|
||||||
if (semicolonIndex == -1 || host.endsWith(":80")) {
|
|
||||||
host = host.replace(":80", "").replace("www.webgoat.local", "www.webwolf.local");
|
|
||||||
} else {
|
|
||||||
host = host.substring(0, semicolonIndex);
|
|
||||||
host = host.concat(":").concat(port);
|
|
||||||
}
|
|
||||||
return "http://" + host + (includeWebWolfContext() ? "/WebWolf" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean includeWebWolfContext() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,4 @@ public class WebWolfRootMacro extends WebWolfMacro {
|
|||||||
public WebWolfRootMacro(String macroName, Map<String, Object> config) {
|
public WebWolfRootMacro(String macroName, Map<String, Object> config) {
|
||||||
super(macroName, config);
|
super(macroName, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean includeWebWolfContext() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,68 +25,4 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.assignments;
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
import lombok.Getter;
|
public interface AssignmentEndpoint {}
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
|
||||||
import org.owasp.webgoat.container.lessons.Initializeable;
|
|
||||||
import org.owasp.webgoat.container.session.UserSessionData;
|
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
public abstract class AssignmentEndpoint implements Initializeable {
|
|
||||||
|
|
||||||
@Autowired private WebSession webSession;
|
|
||||||
@Autowired private UserSessionData userSessionData;
|
|
||||||
@Getter @Autowired private PluginMessages messages;
|
|
||||||
|
|
||||||
protected WebSession getWebSession() {
|
|
||||||
return webSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UserSessionData getUserSessionData() {
|
|
||||||
return userSessionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method for create a successful result:
|
|
||||||
*
|
|
||||||
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
|
|
||||||
*
|
|
||||||
* <p>Of course you can overwrite these values in a specific lesson
|
|
||||||
*
|
|
||||||
* @return a builder for creating a result from a lesson
|
|
||||||
* @param assignment
|
|
||||||
*/
|
|
||||||
protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {
|
|
||||||
return AttackResult.builder(messages)
|
|
||||||
.lessonCompleted(true)
|
|
||||||
.attemptWasMade()
|
|
||||||
.feedback("assignment.solved")
|
|
||||||
.assignment(assignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience method for create a failed result:
|
|
||||||
*
|
|
||||||
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
|
|
||||||
*
|
|
||||||
* <p>Of course you can overwrite these values in a specific lesson
|
|
||||||
*
|
|
||||||
* @return a builder for creating a result from a lesson
|
|
||||||
* @param assignment
|
|
||||||
*/
|
|
||||||
protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {
|
|
||||||
return AttackResult.builder(messages)
|
|
||||||
.lessonCompleted(false)
|
|
||||||
.attemptWasMade()
|
|
||||||
.feedback("assignment.not.solved")
|
|
||||||
.assignment(assignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
|
||||||
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(WebGoatUser user) {}
|
|
||||||
}
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.assignments;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
/** Created by nbaars on 1/14/17. */
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface AssignmentPath {
|
|
||||||
|
|
||||||
String[] path() default {};
|
|
||||||
|
|
||||||
RequestMethod[] method() default {};
|
|
||||||
|
|
||||||
String value() default "";
|
|
||||||
}
|
|
@ -30,82 +30,18 @@ import static org.apache.commons.text.StringEscapeUtils.escapeJson;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public class AttackResult {
|
public class AttackResult {
|
||||||
|
|
||||||
public static class AttackResultBuilder {
|
|
||||||
|
|
||||||
private boolean lessonCompleted;
|
private boolean lessonCompleted;
|
||||||
private PluginMessages messages;
|
private String feedback;
|
||||||
private Object[] feedbackArgs;
|
private Object[] feedbackArgs;
|
||||||
private String feedbackResourceBundleKey;
|
|
||||||
private String output;
|
private String output;
|
||||||
private Object[] outputArgs;
|
private Object[] outputArgs;
|
||||||
private AssignmentEndpoint assignment;
|
private final String assignment;
|
||||||
private boolean attemptWasMade = false;
|
private boolean attemptWasMade;
|
||||||
|
|
||||||
public AttackResultBuilder(PluginMessages messages) {
|
private AttackResult(
|
||||||
this.messages = messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {
|
|
||||||
this.lessonCompleted = lessonCompleted;
|
|
||||||
this.feedbackResourceBundleKey = "lesson.completed";
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {
|
|
||||||
this.lessonCompleted = lessonCompleted;
|
|
||||||
this.feedbackResourceBundleKey = resourceBundleKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder feedbackArgs(Object... args) {
|
|
||||||
this.feedbackArgs = args;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder feedback(String resourceBundleKey) {
|
|
||||||
this.feedbackResourceBundleKey = resourceBundleKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder output(String output) {
|
|
||||||
this.output = output;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder outputArgs(Object... args) {
|
|
||||||
this.outputArgs = args;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder attemptWasMade() {
|
|
||||||
this.attemptWasMade = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResult build() {
|
|
||||||
return new AttackResult(
|
|
||||||
lessonCompleted,
|
|
||||||
messages.getMessage(feedbackResourceBundleKey, feedbackArgs),
|
|
||||||
messages.getMessage(output, output, outputArgs),
|
|
||||||
assignment.getClass().getSimpleName(),
|
|
||||||
attemptWasMade);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
|
|
||||||
this.assignment = assignment;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter private boolean lessonCompleted;
|
|
||||||
@Getter private String feedback;
|
|
||||||
@Getter private String output;
|
|
||||||
@Getter private final String assignment;
|
|
||||||
@Getter private boolean attemptWasMade;
|
|
||||||
|
|
||||||
public AttackResult(
|
|
||||||
boolean lessonCompleted,
|
boolean lessonCompleted,
|
||||||
String feedback,
|
String feedback,
|
||||||
String output,
|
String output,
|
||||||
@ -118,11 +54,33 @@ public class AttackResult {
|
|||||||
this.attemptWasMade = attemptWasMade;
|
this.attemptWasMade = attemptWasMade;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AttackResultBuilder builder(PluginMessages messages) {
|
public AttackResult(
|
||||||
return new AttackResultBuilder(messages);
|
boolean lessonCompleted,
|
||||||
|
String feedback,
|
||||||
|
Object[] feedbackArgs,
|
||||||
|
String output,
|
||||||
|
Object[] outputArgs,
|
||||||
|
String assignment,
|
||||||
|
boolean attemptWasMade) {
|
||||||
|
this.lessonCompleted = lessonCompleted;
|
||||||
|
this.feedback = feedback;
|
||||||
|
this.feedbackArgs = feedbackArgs;
|
||||||
|
this.output = output;
|
||||||
|
this.outputArgs = outputArgs;
|
||||||
|
this.assignment = assignment;
|
||||||
|
this.attemptWasMade = attemptWasMade;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean assignmentSolved() {
|
public boolean assignmentSolved() {
|
||||||
return lessonCompleted;
|
return lessonCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AttackResult apply(PluginMessages pluginMessages) {
|
||||||
|
return new AttackResult(
|
||||||
|
lessonCompleted,
|
||||||
|
pluginMessages.getMessage(feedback, feedback, feedbackArgs),
|
||||||
|
pluginMessages.getMessage(output, output, outputArgs),
|
||||||
|
assignment,
|
||||||
|
attemptWasMade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
|
|
||||||
|
public class AttackResultBuilder {
|
||||||
|
|
||||||
|
private PluginMessages messages;
|
||||||
|
private boolean lessonCompleted;
|
||||||
|
private Object[] feedbackArgs;
|
||||||
|
private String feedbackResourceBundleKey;
|
||||||
|
private String output;
|
||||||
|
private Object[] outputArgs;
|
||||||
|
private AssignmentEndpoint assignment;
|
||||||
|
private boolean attemptWasMade = false;
|
||||||
|
private boolean assignmentCompleted;
|
||||||
|
|
||||||
|
public AttackResultBuilder(PluginMessages messages) {
|
||||||
|
this.messages = messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder() {}
|
||||||
|
|
||||||
|
public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {
|
||||||
|
this.lessonCompleted = lessonCompleted;
|
||||||
|
this.feedbackResourceBundleKey = "lesson.completed";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {
|
||||||
|
this.lessonCompleted = lessonCompleted;
|
||||||
|
this.feedbackResourceBundleKey = resourceBundleKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder assignmentCompleted(boolean assignmentCompleted) {
|
||||||
|
this.assignmentCompleted = assignmentCompleted;
|
||||||
|
this.feedbackResourceBundleKey = "assignment.completed";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder assignmentCompleted(
|
||||||
|
boolean assignmentCompleted, String resourceBundleKey) {
|
||||||
|
this.assignmentCompleted = assignmentCompleted;
|
||||||
|
this.feedbackResourceBundleKey = resourceBundleKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder feedbackArgs(Object... args) {
|
||||||
|
this.feedbackArgs = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder feedback(String resourceBundleKey) {
|
||||||
|
this.feedbackResourceBundleKey = resourceBundleKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder output(String output) {
|
||||||
|
this.output = output;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder outputArgs(Object... args) {
|
||||||
|
this.outputArgs = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder attemptWasMade() {
|
||||||
|
this.attemptWasMade = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResult build() {
|
||||||
|
return new AttackResult(
|
||||||
|
lessonCompleted,
|
||||||
|
feedbackResourceBundleKey,
|
||||||
|
feedbackArgs,
|
||||||
|
output,
|
||||||
|
outputArgs,
|
||||||
|
assignment.getClass().getSimpleName(),
|
||||||
|
attemptWasMade);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
|
||||||
|
this.assignment = assignment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for create a successful result:
|
||||||
|
*
|
||||||
|
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
|
||||||
|
*
|
||||||
|
* <p>Of course you can overwrite these values in a specific lesson
|
||||||
|
*
|
||||||
|
* @return a builder for creating a result from a lesson
|
||||||
|
* @param assignment
|
||||||
|
*/
|
||||||
|
public static AttackResultBuilder success(AssignmentEndpoint assignment) {
|
||||||
|
return new AttackResultBuilder()
|
||||||
|
.lessonCompleted(true)
|
||||||
|
.assignmentCompleted(true)
|
||||||
|
.attemptWasMade()
|
||||||
|
.feedback("assignment.solved")
|
||||||
|
.assignment(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for create a failed result:
|
||||||
|
*
|
||||||
|
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
|
||||||
|
*
|
||||||
|
* <p>Of course you can overwrite these values in a specific lesson
|
||||||
|
*
|
||||||
|
* @return a builder for creating a result from a lesson
|
||||||
|
* @param assignment
|
||||||
|
*/
|
||||||
|
public static AttackResultBuilder failed(AssignmentEndpoint assignment) {
|
||||||
|
return new AttackResultBuilder()
|
||||||
|
.lessonCompleted(false)
|
||||||
|
.assignmentCompleted(true)
|
||||||
|
.attemptWasMade()
|
||||||
|
.feedback("assignment.not.solved")
|
||||||
|
.assignment(assignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
|
||||||
|
return new AttackResultBuilder().lessonCompleted(false).assignment(assignment);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServerHttpResponse;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||||
|
|
||||||
|
/** This class intercepts the response body and applies the plugin messages to the attack result. */
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class AttackResultMessageResponseBodyAdvice implements ResponseBodyAdvice<Object> {
|
||||||
|
|
||||||
|
private final PluginMessages pluginMessages;
|
||||||
|
|
||||||
|
public AttackResultMessageResponseBodyAdvice(PluginMessages pluginMessages) {
|
||||||
|
this.pluginMessages = pluginMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(
|
||||||
|
MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object beforeBodyWrite(
|
||||||
|
Object body,
|
||||||
|
MethodParameter returnType,
|
||||||
|
MediaType selectedContentType,
|
||||||
|
Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||||
|
ServerHttpRequest request,
|
||||||
|
ServerHttpResponse response) {
|
||||||
|
if (body instanceof AttackResult a) {
|
||||||
|
return a.apply(pluginMessages);
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
@ -22,27 +22,30 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.assignments;
|
package org.owasp.webgoat.container.assignments;
|
||||||
|
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
import org.owasp.webgoat.container.lessons.Lesson;
|
||||||
import org.owasp.webgoat.container.users.UserTracker;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
import org.owasp.webgoat.container.users.UserProgress;
|
||||||
|
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||||
|
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.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 UserTrackerRepository userTrackerRepository;
|
private final Course course;
|
||||||
private WebSession webSession;
|
private final UserProgressRepository userProgressRepository;
|
||||||
|
|
||||||
public LessonTrackerInterceptor(
|
public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) {
|
||||||
UserTrackerRepository userTrackerRepository, WebSession webSession) {
|
this.course = course;
|
||||||
this.userTrackerRepository = userTrackerRepository;
|
this.userProgressRepository = userProgressRepository;
|
||||||
this.webSession = webSession;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -65,18 +68,30 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttackResult trackProgress(AttackResult attackResult) {
|
private void trackProgress(AttackResult attackResult) {
|
||||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
if (userTracker == null) {
|
Assert.notNull(user, "User not found in SecurityContext");
|
||||||
userTracker = new UserTracker(webSession.getUserName());
|
var username = realUsername(user);
|
||||||
}
|
|
||||||
if (attackResult.assignmentSolved()) {
|
|
||||||
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
|
|
||||||
} else {
|
|
||||||
userTracker.assignmentFailed(webSession.getCurrentLesson());
|
|
||||||
}
|
|
||||||
userTrackerRepository.save(userTracker);
|
|
||||||
|
|
||||||
return attackResult;
|
var userProgress = userProgressRepository.findByUser(username);
|
||||||
|
if (userProgress == null) {
|
||||||
|
userProgress = new UserProgress(username);
|
||||||
|
}
|
||||||
|
Lesson lesson = course.getLessonByAssignment(attackResult.getAssignment());
|
||||||
|
Assert.notNull(lesson, "Lesson not found for assignment " + attackResult.getAssignment());
|
||||||
|
|
||||||
|
if (attackResult.assignmentSolved()) {
|
||||||
|
userProgress.assignmentSolved(lesson, attackResult.getAssignment());
|
||||||
|
} else {
|
||||||
|
userProgress.assignmentFailed(lesson);
|
||||||
|
}
|
||||||
|
userProgressRepository.save(userProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String realUsername(WebGoatUser user) {
|
||||||
|
// maybe we shouldn't hard code this with just csrf- prefix for now it works
|
||||||
|
return user.getUsername().startsWith("csrf-")
|
||||||
|
? user.getUsername().substring("csrf-".length())
|
||||||
|
: user.getUsername();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,42 +33,20 @@ package org.owasp.webgoat.container.controller;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import 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.RequestMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
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(WebSession ws, Course course) {
|
public StartLesson(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) {
|
||||||
@ -81,8 +59,7 @@ public class StartLesson {
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(
|
.ifPresent(
|
||||||
lesson -> {
|
lesson -> {
|
||||||
ws.setCurrentLesson(lesson);
|
request.setAttribute("lesson", lesson);
|
||||||
model.addObject("lesson", lesson);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
|
@ -51,10 +51,11 @@ 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;
|
||||||
|
|
||||||
private Assignment() {
|
protected Assignment() {
|
||||||
// Hibernate
|
// Hibernate
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,4 +75,8 @@ public class Assignment {
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
this.hints = hints;
|
this.hints = hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void solved() {
|
||||||
|
this.solved = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,30 +34,28 @@ import lombok.Getter;
|
|||||||
* @since October 28, 2003
|
* @since October 28, 2003
|
||||||
*/
|
*/
|
||||||
public enum Category {
|
public enum Category {
|
||||||
INTRODUCTION("Introduction", 5),
|
INTRODUCTION("Introduction"),
|
||||||
GENERAL("General", 100),
|
GENERAL("General"),
|
||||||
|
|
||||||
A1("(A1) Broken Access Control", 301),
|
A1("(A1) Broken Access Control"),
|
||||||
A2("(A2) Cryptographic Failures", 302),
|
A2("(A2) Cryptographic Failures"),
|
||||||
A3("(A3) Injection", 303),
|
A3("(A3) Injection"),
|
||||||
|
|
||||||
A5("(A5) Security Misconfiguration", 305),
|
A5("(A5) Security Misconfiguration"),
|
||||||
A6("(A6) Vuln & Outdated Components", 306),
|
A6("(A6) Vuln & Outdated Components"),
|
||||||
A7("(A7) Identity & Auth Failure", 307),
|
A7("(A7) Identity & Auth Failure"),
|
||||||
A8("(A8) Software & Data Integrity", 308),
|
A8("(A8) Software & Data Integrity"),
|
||||||
A9("(A9) Security Logging Failures", 309),
|
A9("(A9) Security Logging Failures"),
|
||||||
A10("(A10) Server-side Request Forgery", 310),
|
A10("(A10) Server-side Request Forgery"),
|
||||||
|
|
||||||
CLIENT_SIDE("Client side", 1700),
|
CLIENT_SIDE("Client side"),
|
||||||
|
|
||||||
CHALLENGE("Challenges", 3000);
|
CHALLENGE("Challenges");
|
||||||
|
|
||||||
@Getter private String name;
|
@Getter private String name;
|
||||||
@Getter private Integer ranking;
|
|
||||||
|
|
||||||
Category(String name, Integer ranking) {
|
Category(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.ranking = ranking;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,58 +22,107 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.container.lessons;
|
package org.owasp.webgoat.container.lessons;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.groupingBy;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.owasp.webgoat.container.session.Course;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.Assert;
|
||||||
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;
|
private final String contextPath;
|
||||||
|
|
||||||
public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
|
public CourseConfiguration(
|
||||||
|
List<Lesson> lessons,
|
||||||
|
List<AssignmentEndpoint> assignments,
|
||||||
|
@Value("${server.servlet.context-path}") String contextPath) {
|
||||||
this.lessons = lessons;
|
this.lessons = lessons;
|
||||||
this.assignments = assignments;
|
this.assignments = assignments;
|
||||||
assignmentsByPackage =
|
this.contextPath = contextPath.equals("/") ? "" : contextPath;
|
||||||
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() {
|
||||||
lessons.stream().forEach(l -> l.setAssignments(createAssignment(l)));
|
assignments.stream().forEach(this::attachToLesson);
|
||||||
|
|
||||||
|
// Check if all assignments are attached to a lesson
|
||||||
|
var assignmentsAttachedToLessons =
|
||||||
|
lessons.stream().mapToInt(l -> l.getAssignments().size()).sum();
|
||||||
|
Assert.isTrue(
|
||||||
|
assignmentsAttachedToLessons == assignments.size(),
|
||||||
|
"Not all assignments are attached to a lesson, please check the configuration. The"
|
||||||
|
+ " following assignments are not attached to any lesson: "
|
||||||
|
+ findDiff());
|
||||||
return new Course(lessons);
|
return new Course(lessons);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Assignment> createAssignment(Lesson lesson) {
|
private List<String> findDiff() {
|
||||||
var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName());
|
var matchedToLessons =
|
||||||
if (CollectionUtils.isEmpty(endpoints)) {
|
lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList();
|
||||||
log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle());
|
var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList();
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
var diff = new ArrayList<>(allAssignments);
|
||||||
return endpoints.stream()
|
diff.removeAll(matchedToLessons);
|
||||||
.map(
|
return diff;
|
||||||
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) {
|
||||||
@ -81,7 +130,7 @@ public class CourseConfiguration {
|
|||||||
if (methodReturnTypeIsOfTypeAttackResult(m)) {
|
if (methodReturnTypeIsOfTypeAttackResult(m)) {
|
||||||
var mapping = getMapping(m);
|
var mapping = getMapping(m);
|
||||||
if (mapping != null) {
|
if (mapping != null) {
|
||||||
return mapping;
|
return contextPath + mapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 Initializeable {
|
public interface Initializable {
|
||||||
|
|
||||||
void initialize(WebGoatUser webGoatUser);
|
default void initialize(WebGoatUser webGoatUser) {}
|
||||||
}
|
}
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
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;
|
||||||
@ -30,13 +31,10 @@ import lombok.Setter;
|
|||||||
@Setter
|
@Setter
|
||||||
public abstract class Lesson {
|
public abstract class Lesson {
|
||||||
|
|
||||||
private static int count = 1;
|
private List<Assignment> assignments = new ArrayList<>();
|
||||||
private Integer id = null;
|
|
||||||
private List<Assignment> assignments;
|
|
||||||
|
|
||||||
/** Constructor for the Lesson object */
|
public void addAssignment(Assignment assignment) {
|
||||||
protected Lesson() {
|
this.assignments.add(assignment);
|
||||||
id = ++count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,9 +42,9 @@ public abstract class Lesson {
|
|||||||
*
|
*
|
||||||
* @return a {@link java.lang.String} object.
|
* @return a {@link java.lang.String} object.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public LessonName getName() {
|
||||||
String className = getClass().getName();
|
String className = getClass().getName();
|
||||||
return className.substring(className.lastIndexOf('.') + 1);
|
return new LessonName(className.substring(className.lastIndexOf('.') + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,6 +114,10 @@ public abstract class Lesson {
|
|||||||
return this.getClass().getSimpleName();
|
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)
|
||||||
|
@ -35,6 +35,5 @@ package org.owasp.webgoat.container.lessons;
|
|||||||
*/
|
*/
|
||||||
public enum LessonMenuItemType {
|
public enum LessonMenuItemType {
|
||||||
CATEGORY,
|
CATEGORY,
|
||||||
LESSON,
|
LESSON
|
||||||
STAGE
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package org.owasp.webgoat.container.lessons;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class for the name of a lesson. This class is used to ensure that the lesson name is not
|
||||||
|
* null and does not contain the ".lesson" suffix. The front-end passes the lesson name as a string
|
||||||
|
* to the back-end, which then creates a new LessonName object with the lesson name as a parameter.
|
||||||
|
* The constructor of the LessonName class checks if the lesson name is null and removes the
|
||||||
|
* ".lesson" suffix if it is present.
|
||||||
|
*
|
||||||
|
* @param lessonName
|
||||||
|
*/
|
||||||
|
public record LessonName(String lessonName) {
|
||||||
|
public LessonName {
|
||||||
|
Assert.notNull(lessonName, "Lesson name cannot be null");
|
||||||
|
if (lessonName.contains(".lesson")) {
|
||||||
|
lessonName = lessonName.substring(0, lessonName.indexOf(".lesson"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.lessons;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Slf4j
|
|
||||||
public class LessonScanner {
|
|
||||||
|
|
||||||
private static final Pattern lessonPattern = Pattern.compile("^.*/lessons/([^/]*)/.*$");
|
|
||||||
|
|
||||||
@Getter private final Set<String> lessons = new HashSet<>();
|
|
||||||
|
|
||||||
public LessonScanner(ResourcePatternResolver resourcePatternResolver) {
|
|
||||||
try {
|
|
||||||
var resources = resourcePatternResolver.getResources("classpath:/lessons/*/*");
|
|
||||||
for (var resource : resources) {
|
|
||||||
// WG can run as a fat jar or as directly from file system we need to support both so use
|
|
||||||
// the URL
|
|
||||||
var url = resource.getURL();
|
|
||||||
var matcher = lessonPattern.matcher(url.toString());
|
|
||||||
if (matcher.matches()) {
|
|
||||||
lessons.add(matcher.group(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.debug("Found {} lessons", lessons.size());
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warn("No lessons found...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> applyPattern(String pattern) {
|
|
||||||
return lessons.stream().map(lesson -> String.format(pattern, lesson)).toList();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,3 @@
|
|||||||
|
package org.owasp.webgoat.container.report;
|
||||||
|
|
||||||
|
record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
|
@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* *************************************************************************************************
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
||||||
|
* please see http://www.owasp.org/
|
||||||
|
*
|
||||||
|
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
|
||||||
|
*
|
||||||
|
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* <p>You should have received a copy of the GNU General Public License along with this program; if
|
||||||
|
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
* 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* <p>Getting Source ==============
|
||||||
|
*
|
||||||
|
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
|
||||||
|
* for free software projects.
|
||||||
|
*/
|
||||||
|
package org.owasp.webgoat.container.report;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.owasp.webgoat.container.CurrentUsername;
|
||||||
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
|
import org.owasp.webgoat.container.session.Course;
|
||||||
|
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class ReportCardController {
|
||||||
|
|
||||||
|
private final UserProgressRepository userProgressRepository;
|
||||||
|
private final Course course;
|
||||||
|
private final PluginMessages pluginMessages;
|
||||||
|
|
||||||
|
public ReportCardController(
|
||||||
|
UserProgressRepository userProgressRepository, Course course, PluginMessages pluginMessages) {
|
||||||
|
this.userProgressRepository = userProgressRepository;
|
||||||
|
this.course = course;
|
||||||
|
this.pluginMessages = pluginMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint which generates the report card for the current use to show the stats on the solved
|
||||||
|
* lessons
|
||||||
|
*/
|
||||||
|
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
|
||||||
|
@ResponseBody
|
||||||
|
public ReportCard reportCard(@CurrentUsername String username) {
|
||||||
|
var userProgress = userProgressRepository.findByUser(username);
|
||||||
|
var lessonStatistics =
|
||||||
|
course.getLessons().stream()
|
||||||
|
.map(
|
||||||
|
lesson -> {
|
||||||
|
var lessonTracker = userProgress.getLessonProgress(lesson);
|
||||||
|
return new LessonStatistics(
|
||||||
|
pluginMessages.getMessage(lesson.getTitle()),
|
||||||
|
lessonTracker.isLessonSolved(),
|
||||||
|
lessonTracker.getNumberOfAttempts());
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
return new ReportCard(
|
||||||
|
course.getTotalOfLessons(),
|
||||||
|
course.getTotalOfAssignments(),
|
||||||
|
userProgress.numberOfAssignmentsSolved(),
|
||||||
|
userProgress.numberOfLessonsSolved(),
|
||||||
|
lessonStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ReportCard(
|
||||||
|
int totalNumberOfLessons,
|
||||||
|
int totalNumberOfAssignments,
|
||||||
|
long numberOfAssignmentsSolved,
|
||||||
|
long numberOfLessonsSolved,
|
||||||
|
List<LessonStatistics> lessonStatistics) {}
|
||||||
|
|
||||||
|
private record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
|
||||||
|
}
|
@ -10,26 +10,24 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import 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.lessons.Lesson;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
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 WebSession webSession;
|
private final List<Hint> allHints;
|
||||||
|
|
||||||
public HintService(WebSession webSession) {
|
public HintService(Course course) {
|
||||||
this.webSession = webSession;
|
this.allHints =
|
||||||
|
course.getLessons().stream()
|
||||||
|
.flatMap(lesson -> lesson.getAssignments().stream())
|
||||||
|
.map(this::createHint)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,15 +38,7 @@ public class HintService {
|
|||||||
@GetMapping(path = URL_HINTS_MVC, produces = "application/json")
|
@GetMapping(path = URL_HINTS_MVC, produces = "application/json")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<Hint> getHints() {
|
public List<Hint> getHints() {
|
||||||
Lesson l = webSession.getCurrentLesson();
|
return allHints;
|
||||||
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,33 +1,24 @@
|
|||||||
package org.owasp.webgoat.container.service;
|
package org.owasp.webgoat.container.service;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
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.session.WebSession;
|
import org.owasp.webgoat.container.lessons.LessonName;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
|
||||||
* LessonInfoService class.
|
|
||||||
*
|
|
||||||
* @author dm
|
|
||||||
* @version $Id: $Id
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@AllArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LessonInfoService {
|
public class LessonInfoService {
|
||||||
|
|
||||||
private final WebSession webSession;
|
private final Course course;
|
||||||
|
|
||||||
/**
|
@GetMapping(path = "/service/lessoninfo.mvc/{lesson}")
|
||||||
* getLessonInfo.
|
public @ResponseBody LessonInfoModel getLessonInfo(
|
||||||
*
|
@PathVariable("lesson") LessonName lessonName) {
|
||||||
* @return a {@link LessonInfoModel} object.
|
var lesson = course.getLessonByName(lessonName);
|
||||||
*/
|
|
||||||
@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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,18 +30,16 @@ package org.owasp.webgoat.container.service;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.owasp.webgoat.container.lessons.Assignment;
|
import org.owasp.webgoat.container.CurrentUsername;
|
||||||
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.session.WebSession;
|
import org.owasp.webgoat.container.users.LessonProgress;
|
||||||
import org.owasp.webgoat.container.users.LessonTracker;
|
import org.owasp.webgoat.container.users.UserProgress;
|
||||||
import org.owasp.webgoat.container.users.UserTracker;
|
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||||
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,8 +57,7 @@ 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 final WebSession webSession;
|
private UserProgressRepository userTrackerRepository;
|
||||||
private UserTrackerRepository userTrackerRepository;
|
|
||||||
|
|
||||||
@Value("#{'${exclude.categories}'.split(',')}")
|
@Value("#{'${exclude.categories}'.split(',')}")
|
||||||
private List<String> excludeCategories;
|
private List<String> excludeCategories;
|
||||||
@ -74,10 +71,13 @@ 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() {
|
public @ResponseBody List<LessonMenuItem> showLeftNav(@CurrentUsername String username) {
|
||||||
|
// TODO: this looks way too complicated. Either we save it incorrectly or we miss something to
|
||||||
|
// easily find out
|
||||||
|
// if a lesson if solved or not.
|
||||||
List<LessonMenuItem> menu = new ArrayList<>();
|
List<LessonMenuItem> menu = new ArrayList<>();
|
||||||
List<Category> categories = course.getCategories();
|
List<Category> categories = course.getCategories();
|
||||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
UserProgress userTracker = userTrackerRepository.findByUser(username);
|
||||||
|
|
||||||
for (Category category : categories) {
|
for (Category category : categories) {
|
||||||
if (excludeCategories.contains(category.name())) {
|
if (excludeCategories.contains(category.name())) {
|
||||||
@ -97,28 +97,14 @@ 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);
|
||||||
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
|
LessonProgress lessonTracker = userTracker.getLessonProgress(lesson);
|
||||||
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
|
boolean lessonSolved = lessonTracker.isLessonSolved();
|
||||||
lessonItem.setComplete(lessonSolved);
|
lessonItem.setComplete(lessonSolved);
|
||||||
categoryItem.addChild(lessonItem);
|
categoryItem.addChild(lessonItem);
|
||||||
}
|
}
|
||||||
categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking());
|
categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking));
|
||||||
menu.add(categoryItem);
|
menu.add(categoryItem);
|
||||||
}
|
}
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean lessonCompleted(Map<Assignment, Boolean> map, Lesson currentLesson) {
|
|
||||||
boolean result = true;
|
|
||||||
for (Map.Entry<Assignment, Boolean> entry : map.entrySet()) {
|
|
||||||
Assignment storedAssignment = entry.getKey();
|
|
||||||
for (Assignment lessonAssignment : currentLesson.getAssignments()) {
|
|
||||||
if (lessonAssignment.getName().equals(storedAssignment.getName())) {
|
|
||||||
result = result && entry.getValue();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,15 @@ 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.session.WebSession;
|
import org.owasp.webgoat.container.lessons.LessonName;
|
||||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
|
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,8 +24,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LessonProgressService {
|
public class LessonProgressService {
|
||||||
|
|
||||||
private final UserTrackerRepository userTrackerRepository;
|
private final UserProgressRepository userProgressRepository;
|
||||||
private final WebSession webSession;
|
private final Course course;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -29,20 +33,20 @@ public class LessonProgressService {
|
|||||||
*
|
*
|
||||||
* @return list of assignments
|
* @return list of assignments
|
||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json")
|
@GetMapping(value = "/service/lessonoverview.mvc/{lesson}")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<LessonOverview> lessonOverview() {
|
public List<LessonOverview> lessonOverview(
|
||||||
var userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
@PathVariable("lesson") LessonName lessonName, @CurrentUsername String username) {
|
||||||
var currentLesson = webSession.getCurrentLesson();
|
var userProgress = userProgressRepository.findByUser(username);
|
||||||
|
var lesson = course.getLessonByName(lessonName);
|
||||||
|
|
||||||
if (currentLesson != null) {
|
Assert.isTrue(lesson != null, "Lesson not found: " + lessonName);
|
||||||
var lessonTracker = userTracker.getLessonTracker(currentLesson);
|
|
||||||
return lessonTracker.getLessonOverview().entrySet().stream()
|
var lessonProgress = userProgress.getLessonProgress(lesson);
|
||||||
|
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
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.service;
|
|
||||||
|
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LessonTitleService class.
|
|
||||||
*
|
|
||||||
* @author dm
|
|
||||||
* @version $Id: $Id
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
public class LessonTitleService {
|
|
||||||
|
|
||||||
private final WebSession webSession;
|
|
||||||
|
|
||||||
public LessonTitleService(final WebSession webSession) {
|
|
||||||
this.webSession = webSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the title for the current attack
|
|
||||||
*
|
|
||||||
* @return a {@link java.lang.String} object.
|
|
||||||
*/
|
|
||||||
@RequestMapping(path = "/service/lessontitle.mvc", produces = "application/html")
|
|
||||||
public @ResponseBody String showPlan() {
|
|
||||||
Lesson lesson = webSession.getCurrentLesson();
|
|
||||||
return lesson != null ? lesson.getTitle() : "";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
/**
|
|
||||||
* *************************************************************************************************
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
|
||||||
* please see http://www.owasp.org/
|
|
||||||
*
|
|
||||||
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
|
|
||||||
*
|
|
||||||
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* <p>You should have received a copy of the GNU General Public License along with this program; if
|
|
||||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* <p>Getting Source ==============
|
|
||||||
*
|
|
||||||
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
|
|
||||||
* for free software projects.
|
|
||||||
*/
|
|
||||||
package org.owasp.webgoat.container.service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
|
||||||
import org.owasp.webgoat.container.session.Course;
|
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
|
||||||
import org.owasp.webgoat.container.users.LessonTracker;
|
|
||||||
import org.owasp.webgoat.container.users.UserTracker;
|
|
||||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ReportCardService
|
|
||||||
*
|
|
||||||
* @author nbaars
|
|
||||||
* @version $Id: $Id
|
|
||||||
*/
|
|
||||||
@Controller
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class ReportCardService {
|
|
||||||
|
|
||||||
private final WebSession webSession;
|
|
||||||
private final UserTrackerRepository userTrackerRepository;
|
|
||||||
private final Course course;
|
|
||||||
private final PluginMessages pluginMessages;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Endpoint which generates the report card for the current use to show the stats on the solved
|
|
||||||
* lessons
|
|
||||||
*/
|
|
||||||
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
|
|
||||||
@ResponseBody
|
|
||||||
public ReportCard reportCard() {
|
|
||||||
final ReportCard reportCard = new ReportCard();
|
|
||||||
reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());
|
|
||||||
reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments());
|
|
||||||
|
|
||||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
|
||||||
reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved());
|
|
||||||
reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved());
|
|
||||||
for (Lesson lesson : course.getLessons()) {
|
|
||||||
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
|
|
||||||
final LessonStatistics lessonStatistics = new LessonStatistics();
|
|
||||||
lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));
|
|
||||||
lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());
|
|
||||||
lessonStatistics.setSolved(lessonTracker.isLessonSolved());
|
|
||||||
reportCard.lessonStatistics.add(lessonStatistics);
|
|
||||||
}
|
|
||||||
return reportCard;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private final class ReportCard {
|
|
||||||
|
|
||||||
private int totalNumberOfLessons;
|
|
||||||
private int totalNumberOfAssignments;
|
|
||||||
private int solvedLessons;
|
|
||||||
private int numberOfAssignmentsSolved;
|
|
||||||
private int numberOfLessonsSolved;
|
|
||||||
private List<LessonStatistics> lessonStatistics = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
private final class LessonStatistics {
|
|
||||||
private String name;
|
|
||||||
private boolean solved;
|
|
||||||
private int numberOfAttempts;
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,14 +29,17 @@ import java.util.function.Function;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.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.lessons.Initializeable;
|
import org.owasp.webgoat.container.CurrentUser;
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
import org.owasp.webgoat.container.lessons.Initializable;
|
||||||
import org.owasp.webgoat.container.session.WebSession;
|
import org.owasp.webgoat.container.lessons.LessonName;
|
||||||
import org.owasp.webgoat.container.users.UserTracker;
|
import org.owasp.webgoat.container.session.Course;
|
||||||
import org.owasp.webgoat.container.users.UserTrackerRepository;
|
import org.owasp.webgoat.container.users.UserProgress;
|
||||||
|
import org.owasp.webgoat.container.users.UserProgressRepository;
|
||||||
|
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@ -44,25 +47,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class RestartLessonService {
|
public class RestartLessonService {
|
||||||
|
|
||||||
private final WebSession webSession;
|
private final Course course;
|
||||||
private final UserTrackerRepository userTrackerRepository;
|
private final UserProgressRepository userTrackerRepository;
|
||||||
private final Function<String, Flyway> flywayLessons;
|
private final Function<String, Flyway> flywayLessons;
|
||||||
private final List<Initializeable> lessonsToInitialize;
|
private final List<Initializable> lessonsToInitialize;
|
||||||
|
|
||||||
@RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text")
|
@GetMapping(path = "/service/restartlesson.mvc/{lesson}")
|
||||||
@ResponseStatus(value = HttpStatus.OK)
|
@ResponseStatus(value = HttpStatus.OK)
|
||||||
public void restartLesson() {
|
public void restartLesson(
|
||||||
Lesson al = webSession.getCurrentLesson();
|
@PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) {
|
||||||
log.debug("Restarting lesson: " + al);
|
var lesson = course.getLessonByName(lessonName);
|
||||||
|
|
||||||
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
|
UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername());
|
||||||
userTracker.reset(al);
|
userTracker.reset(lesson);
|
||||||
userTrackerRepository.save(userTracker);
|
userTrackerRepository.save(userTracker);
|
||||||
|
|
||||||
var flyway = flywayLessons.apply(webSession.getUserName());
|
var flyway = flywayLessons.apply(user.getUsername());
|
||||||
flyway.clean();
|
flyway.clean();
|
||||||
flyway.migrate();
|
flyway.migrate();
|
||||||
|
|
||||||
lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser()));
|
lessonsToInitialize.forEach(i -> i.initialize(user));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@
|
|||||||
package org.owasp.webgoat.container.service;
|
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.session.WebSession;
|
import org.owasp.webgoat.container.users.WebGoatUser;
|
||||||
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;
|
||||||
@ -17,17 +18,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() {
|
public String applySecurity(@CurrentUser WebGoatUser user) {
|
||||||
webSession.toggleSecurity();
|
// webSession.toggleSecurity();
|
||||||
restartLessonService.restartLesson();
|
// restartLessonService.restartLesson(user);
|
||||||
|
|
||||||
var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
// TODO disabled for now
|
||||||
return messages.getMessage(msg);
|
// var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
|
||||||
|
return messages.getMessage("Not working...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ************************************************************************************************
|
* ************************************************************************************************
|
||||||
@ -96,4 +97,21 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package org.owasp.webgoat.container.session;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible for managing user session data within a lesson. It uses a HashMap to
|
||||||
|
* store key-value pairs representing session data.
|
||||||
|
*/
|
||||||
|
public class LessonSession {
|
||||||
|
|
||||||
|
private Map<String, Object> userSessionData = new HashMap<>();
|
||||||
|
|
||||||
|
/** Default constructor initializing an empty session. */
|
||||||
|
public LessonSession() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value associated with the given key.
|
||||||
|
*
|
||||||
|
* @param key the key for the session data
|
||||||
|
* @return the value associated with the key, or null if the key does not exist
|
||||||
|
*/
|
||||||
|
public Object getValue(String key) {
|
||||||
|
if (!userSessionData.containsKey(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
return userSessionData.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for the given key. If the key already exists, its value is updated.
|
||||||
|
*
|
||||||
|
* @param key the key for the session data
|
||||||
|
* @param value the value to be associated with the key
|
||||||
|
*/
|
||||||
|
public void setValue(String key, Object value) {
|
||||||
|
if (userSessionData.containsKey(key)) {
|
||||||
|
userSessionData.replace(key, value);
|
||||||
|
} else {
|
||||||
|
userSessionData.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.session;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/** Created by jason on 1/4/17. */
|
|
||||||
public class UserSessionData {
|
|
||||||
|
|
||||||
private HashMap<String, Object> userSessionData = new HashMap<>();
|
|
||||||
|
|
||||||
public UserSessionData() {}
|
|
||||||
|
|
||||||
public UserSessionData(String key, String value) {
|
|
||||||
setValue(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GETTERS & SETTERS
|
|
||||||
public Object getValue(String key) {
|
|
||||||
if (!userSessionData.containsKey(key)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
return userSessionData.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String key, Object value) {
|
|
||||||
if (userSessionData.containsKey(key)) {
|
|
||||||
userSessionData.replace(key, value);
|
|
||||||
} else {
|
|
||||||
userSessionData.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.session;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import org.owasp.webgoat.container.lessons.Lesson;
|
|
||||||
import org.owasp.webgoat.container.users.WebGoatUser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* *************************************************************************************************
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
|
|
||||||
* please see http://www.owasp.org/
|
|
||||||
*
|
|
||||||
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
|
|
||||||
*
|
|
||||||
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* <p>You should have received a copy of the GNU General Public License along with this program; if
|
|
||||||
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* <p>Getting Source ==============
|
|
||||||
*
|
|
||||||
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
|
|
||||||
* for free software projects.
|
|
||||||
*
|
|
||||||
* @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
|
|
||||||
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
|
|
||||||
* @version $Id: $Id
|
|
||||||
* @since October 28, 2003
|
|
||||||
*/
|
|
||||||
public class WebSession implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -4270066103101711560L;
|
|
||||||
private WebGoatUser currentUser;
|
|
||||||
private transient Lesson currentLesson;
|
|
||||||
private boolean securityEnabled;
|
|
||||||
|
|
||||||
public WebSession(WebGoatUser webGoatUser) {
|
|
||||||
this.currentUser = webGoatUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter for the field <code>currentScreen</code>.
|
|
||||||
*
|
|
||||||
* @param lesson current lesson
|
|
||||||
*/
|
|
||||||
public void setCurrentLesson(Lesson lesson) {
|
|
||||||
this.currentLesson = lesson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getCurrentLesson.
|
|
||||||
*
|
|
||||||
* @return a {@link Lesson} object.
|
|
||||||
*/
|
|
||||||
public Lesson getCurrentLesson() {
|
|
||||||
return this.currentLesson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the userName attribute of the WebSession object
|
|
||||||
*
|
|
||||||
* @return The userName value
|
|
||||||
*/
|
|
||||||
public String getUserName() {
|
|
||||||
return currentUser.getUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebGoatUser getUser() {
|
|
||||||
return currentUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toggleSecurity() {
|
|
||||||
this.securityEnabled = !this.securityEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSecurityEnabled() {
|
|
||||||
return securityEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,7 +52,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
|
|||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class LessonTracker {
|
public class LessonProgress {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -61,25 +61,22 @@ public class LessonTracker {
|
|||||||
@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> solvedAssignments = new HashSet<>();
|
private final Set<Assignment> assignments = 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;
|
||||||
|
|
||||||
private LessonTracker() {
|
protected LessonProgress() {
|
||||||
// JPA
|
// JPA
|
||||||
}
|
}
|
||||||
|
|
||||||
public LessonTracker(Lesson lesson) {
|
public LessonProgress(Lesson lesson) {
|
||||||
lessonName = lesson.getId();
|
lessonName = lesson.getId();
|
||||||
allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
assignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Assignment> getAssignment(String name) {
|
public Optional<Assignment> getAssignment(String name) {
|
||||||
return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
return assignments.stream().filter(a -> a.getName().equals(name)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,14 +85,14 @@ public class LessonTracker {
|
|||||||
* @param solvedAssignment the assignment which the user solved
|
* @param solvedAssignment the assignment which the user solved
|
||||||
*/
|
*/
|
||||||
public void assignmentSolved(String solvedAssignment) {
|
public void assignmentSolved(String solvedAssignment) {
|
||||||
getAssignment(solvedAssignment).ifPresent(solvedAssignments::add);
|
getAssignment(solvedAssignment).ifPresent(Assignment::solved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return did they user solved all solvedAssignments for the lesson?
|
* @return did they user solved all solvedAssignments for the lesson?
|
||||||
*/
|
*/
|
||||||
public boolean isLessonSolved() {
|
public boolean isLessonSolved() {
|
||||||
return allAssignments.size() == solvedAssignments.size();
|
return assignments.stream().allMatch(Assignment::isSolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increase the number attempts to solve the lesson */
|
/** Increase the number attempts to solve the lesson */
|
||||||
@ -105,18 +102,17 @@ public class LessonTracker {
|
|||||||
|
|
||||||
/** Reset the tracker. We do not reset the number of attempts here! */
|
/** Reset the tracker. We do not reset the number of attempts here! */
|
||||||
void reset() {
|
void reset() {
|
||||||
solvedAssignments.clear();
|
assignments.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() {
|
||||||
List<Assignment> notSolved =
|
return assignments.stream().collect(Collectors.toMap(a -> a, Assignment::isSolved));
|
||||||
allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList();
|
}
|
||||||
Map<Assignment, Boolean> overview =
|
|
||||||
notSolved.stream().collect(Collectors.toMap(a -> a, b -> false));
|
long numberOfSolvedAssignments() {
|
||||||
overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true)));
|
return assignments.size();
|
||||||
return overview;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Scoreboard {
|
public class Scoreboard {
|
||||||
|
|
||||||
private final UserTrackerRepository userTrackerRepository;
|
private final UserProgressRepository 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(UserTracker userTracker) {
|
private List<String> challengesSolved(UserProgress 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::getLessonTracker)
|
.map(userTracker::getLessonProgress)
|
||||||
.flatMap(Optional::stream)
|
.flatMap(Optional::stream)
|
||||||
.filter(LessonTracker::isLessonSolved)
|
.filter(LessonProgress::isLessonSolved)
|
||||||
.map(LessonTracker::getLessonName)
|
.map(LessonProgress::getLessonName)
|
||||||
.map(this::toLessonTitle)
|
.map(this::toLessonTitle)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,10 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +49,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Entity
|
@Entity
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class UserTracker {
|
public class UserProgress {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
@ -62,11 +59,11 @@ public class UserTracker {
|
|||||||
private String user;
|
private String user;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
private Set<LessonTracker> lessonTrackers = new HashSet<>();
|
private Set<LessonProgress> lessonProgress = new HashSet<>();
|
||||||
|
|
||||||
private UserTracker() {}
|
protected UserProgress() {}
|
||||||
|
|
||||||
public UserTracker(final String user) {
|
public UserProgress(final String user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,15 +73,15 @@ public class UserTracker {
|
|||||||
* @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 LessonTracker getLessonTracker(Lesson lesson) {
|
public LessonProgress getLessonProgress(Lesson lesson) {
|
||||||
Optional<LessonTracker> lessonTracker =
|
Optional<LessonProgress> progress =
|
||||||
lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
lessonProgress.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
|
||||||
if (!lessonTracker.isPresent()) {
|
if (!progress.isPresent()) {
|
||||||
LessonTracker newLessonTracker = new LessonTracker(lesson);
|
LessonProgress newLessonTracker = new LessonProgress(lesson);
|
||||||
lessonTrackers.add(newLessonTracker);
|
lessonProgress.add(newLessonTracker);
|
||||||
return newLessonTracker;
|
return newLessonTracker;
|
||||||
} else {
|
} else {
|
||||||
return lessonTracker.get();
|
return progress.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,43 +91,34 @@ public class UserTracker {
|
|||||||
* @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<LessonTracker> getLessonTracker(String id) {
|
public Optional<LessonProgress> getLessonProgress(String id) {
|
||||||
return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
return lessonProgress.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignmentSolved(Lesson lesson, String assignmentName) {
|
public void assignmentSolved(Lesson lesson, String assignmentName) {
|
||||||
LessonTracker lessonTracker = getLessonTracker(lesson);
|
LessonProgress progress = getLessonProgress(lesson);
|
||||||
lessonTracker.incrementAttempts();
|
progress.incrementAttempts();
|
||||||
lessonTracker.assignmentSolved(assignmentName);
|
progress.assignmentSolved(assignmentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignmentFailed(Lesson lesson) {
|
public void assignmentFailed(Lesson lesson) {
|
||||||
LessonTracker lessonTracker = getLessonTracker(lesson);
|
LessonProgress progress = getLessonProgress(lesson);
|
||||||
lessonTracker.incrementAttempts();
|
progress.incrementAttempts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(Lesson al) {
|
public void reset(Lesson al) {
|
||||||
LessonTracker lessonTracker = getLessonTracker(al);
|
LessonProgress progress = getLessonProgress(al);
|
||||||
lessonTracker.reset();
|
progress.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int numberOfLessonsSolved() {
|
public long numberOfLessonsSolved() {
|
||||||
int numberOfLessonsSolved = 0;
|
return lessonProgress.stream().filter(LessonProgress::isLessonSolved).count();
|
||||||
for (LessonTracker lessonTracker : lessonTrackers) {
|
|
||||||
if (lessonTracker.isLessonSolved()) {
|
|
||||||
numberOfLessonsSolved = numberOfLessonsSolved + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numberOfLessonsSolved;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int numberOfAssignmentsSolved() {
|
public long numberOfAssignmentsSolved() {
|
||||||
int numberOfAssignmentsSolved = 0;
|
return lessonProgress.stream()
|
||||||
for (LessonTracker lessonTracker : lessonTrackers) {
|
.map(LessonProgress::numberOfSolvedAssignments)
|
||||||
Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview();
|
.mapToLong(Long::valueOf)
|
||||||
numberOfAssignmentsSolved =
|
.sum();
|
||||||
lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();
|
|
||||||
}
|
|
||||||
return numberOfAssignmentsSolved;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package org.owasp.webgoat.container.users;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface UserProgressRepository extends JpaRepository<UserProgress, String> {
|
||||||
|
|
||||||
|
// TODO: make optional
|
||||||
|
UserProgress findByUser(String user);
|
||||||
|
}
|
@ -4,7 +4,7 @@ import java.util.List;
|
|||||||
import java.util.function.Function;
|
import 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.Initializeable;
|
import org.owasp.webgoat.container.lessons.Initializable;
|
||||||
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 UserTrackerRepository userTrackerRepository;
|
private final UserProgressRepository userTrackerRepository;
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final Function<String, Flyway> flywayLessons;
|
private final Function<String, Flyway> flywayLessons;
|
||||||
private final List<Initializeable> lessonInitializables;
|
private final List<Initializable> 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 UserTracker(username)); // if user previously existed it will not get another tracker
|
new UserProgress(username)); // if user previously existed it will not get another tracker
|
||||||
createLessonsForUser(webGoatUser);
|
createLessonsForUser(webGoatUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package org.owasp.webgoat.container.users;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/30/17.
|
|
||||||
*/
|
|
||||||
public interface UserTrackerRepository extends JpaRepository<UserTracker, String> {
|
|
||||||
|
|
||||||
UserTracker findByUser(String user);
|
|
||||||
}
|
|
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.authbypass;
|
package org.owasp.webgoat.lessons.authbypass;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -32,9 +35,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.UserSessionData;
|
import org.owasp.webgoat.container.session.LessonSession;
|
||||||
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 +49,13 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
"auth-bypass.hints.verify.3",
|
"auth-bypass.hints.verify.3",
|
||||||
"auth-bypass.hints.verify.4"
|
"auth-bypass.hints.verify.4"
|
||||||
})
|
})
|
||||||
public class VerifyAccount extends AssignmentEndpoint {
|
public class VerifyAccount implements AssignmentEndpoint {
|
||||||
|
|
||||||
@Autowired private WebSession webSession;
|
private final LessonSession userSessionData;
|
||||||
|
|
||||||
@Autowired UserSessionData userSessionData;
|
public VerifyAccount(LessonSession userSessionData) {
|
||||||
|
this.userSessionData = userSessionData;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
path = "/auth-bypass/verify-account",
|
path = "/auth-bypass/verify-account",
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.bypassrestrictions;
|
package org.owasp.webgoat.lessons.bypassrestrictions;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint {
|
public class BypassRestrictionsFieldRestrictions implements AssignmentEndpoint {
|
||||||
|
|
||||||
@PostMapping("/BypassRestrictions/FieldRestrictions")
|
@PostMapping("/BypassRestrictions/FieldRestrictions")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.bypassrestrictions;
|
package org.owasp.webgoat.lessons.bypassrestrictions;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint {
|
public class BypassRestrictionsFrontendValidation implements AssignmentEndpoint {
|
||||||
|
|
||||||
@PostMapping("/BypassRestrictions/frontendValidation")
|
@PostMapping("/BypassRestrictions/frontendValidation")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -2,11 +2,13 @@ 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
|
||||||
|
@ -22,27 +22,30 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.challenges;
|
package org.owasp.webgoat.lessons.challenges;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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.web.bind.annotation.PathVariable;
|
||||||
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;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@AllArgsConstructor
|
public class FlagController implements AssignmentEndpoint {
|
||||||
public class FlagController extends AssignmentEndpoint {
|
|
||||||
|
|
||||||
private final WebSession webSession;
|
|
||||||
private final Flags flags;
|
private final Flags flags;
|
||||||
|
|
||||||
@PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE)
|
public FlagController(Flags flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path = "/challenge/flag/{flagNumber}")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult postFlag(@RequestParam String flag) {
|
public AttackResult postFlag(@PathVariable int flagNumber, @RequestParam String flag) {
|
||||||
Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson());
|
var expectedFlag = flags.getFlag(flagNumber);
|
||||||
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,7 +4,6 @@ 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
|
||||||
@ -15,12 +14,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package org.owasp.webgoat.lessons.challenges.challenge1;
|
package org.owasp.webgoat.lessons.challenges.challenge1;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
|
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
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.lessons.challenges.Flags;
|
import org.owasp.webgoat.lessons.challenges.Flags;
|
||||||
@ -42,11 +43,14 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
* @since August 11, 2016
|
* @since August 11, 2016
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequiredArgsConstructor
|
public class Assignment1 implements AssignmentEndpoint {
|
||||||
public class Assignment1 extends AssignmentEndpoint {
|
|
||||||
|
|
||||||
private final Flags flags;
|
private final Flags flags;
|
||||||
|
|
||||||
|
public Assignment1(Flags flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/challenge/1")
|
@PostMapping("/challenge/1")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
|
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.challenges.challenge5;
|
package org.owasp.webgoat.lessons.challenges.challenge5;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -39,7 +42,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class Assignment5 extends AssignmentEndpoint {
|
public class Assignment5 implements AssignmentEndpoint {
|
||||||
|
|
||||||
private final LessonDataSource dataSource;
|
private final LessonDataSource dataSource;
|
||||||
private final Flags flags;
|
private final Flags flags;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.owasp.webgoat.lessons.challenges.challenge7;
|
package org.owasp.webgoat.lessons.challenges.challenge7;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@ -29,7 +31,7 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Assignment7 extends AssignmentEndpoint {
|
public class Assignment7 implements AssignmentEndpoint {
|
||||||
|
|
||||||
public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class Assignment8 extends AssignmentEndpoint {
|
public class Assignment8 implements AssignmentEndpoint {
|
||||||
|
|
||||||
private static final Map<Integer, Integer> votes = new HashMap<>();
|
private static final Map<Integer, Integer> votes = new HashMap<>();
|
||||||
|
|
||||||
|
@ -22,28 +22,36 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.chromedevtools;
|
package org.owasp.webgoat.lessons.chromedevtools;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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.UserSessionData;
|
import org.owasp.webgoat.container.session.LessonSession;
|
||||||
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 the HTTP request.
|
* This is just a class used to make the HTTP request.
|
||||||
*
|
*
|
||||||
* @author TMelzer
|
* @author TMelzer
|
||||||
* @since 30.11.18
|
* @since 30.11.18
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class NetworkDummy extends AssignmentEndpoint {
|
public class NetworkDummy implements 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) {
|
||||||
UserSessionData userSessionData = getUserSessionData();
|
String answer = (String) lessonSession.getValue("randValue");
|
||||||
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();
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.chromedevtools;
|
package org.owasp.webgoat.lessons.chromedevtools;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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;
|
||||||
@ -40,7 +43,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"networkHint1", "networkHint2"})
|
@AssignmentHints({"networkHint1", "networkHint2"})
|
||||||
public class NetworkLesson extends AssignmentEndpoint {
|
public class NetworkLesson implements AssignmentEndpoint {
|
||||||
|
|
||||||
@PostMapping(
|
@PostMapping(
|
||||||
value = "/ChromeDevTools/network",
|
value = "/ChromeDevTools/network",
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package org.owasp.webgoat.lessons.cia;
|
package org.owasp.webgoat.lessons.cia;
|
||||||
|
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
|
||||||
|
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
|
||||||
|
|
||||||
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.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -9,9 +12,9 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public class CIAQuiz extends AssignmentEndpoint {
|
public class CIAQuiz implements AssignmentEndpoint {
|
||||||
|
|
||||||
String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
|
private final String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
|
||||||
boolean[] guesses = new boolean[solutions.length];
|
boolean[] guesses = new boolean[solutions.length];
|
||||||
|
|
||||||
@PostMapping("/cia/quiz")
|
@PostMapping("/cia/quiz")
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user