diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 696cb3a7f4..3b2f9aae32 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat \ No newline at end of file +custom: https://owasp.org/donate/?reponame=www-project-webgoat&title=OWASP+WebGoat diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 28d0b0bc6d..f2ea9477f6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,4 +12,3 @@ updates: directory: "/" schedule: interval: "weekly" - diff --git a/.github/workflows/branchbuild.txt b/.github/workflows/branchbuild.txt index 33ced2de96..95a45a1f04 100644 --- a/.github/workflows/branchbuild.txt +++ b/.github/workflows/branchbuild.txt @@ -11,9 +11,9 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - java-version: [ 17, 21 ] + java-version: [ 21 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java-version }} uses: actions/setup-java@v4 with: @@ -51,4 +51,4 @@ jobs: file: ./Dockerfile_desktop push: false build-args: | - webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} + webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 121940fee5..0caa5a2920 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,34 +1,51 @@ -name: "Pull requests build" +name: "Main / Pull requests build" on: pull_request: paths-ignore: - '.txt' - 'LICENSE' - 'docs/**' - branches: [main] + branches: [ main ] push: branches: - main jobs: + pre-commit: + name: Pre-commit check + runs-on: ubuntu-latest + steps: + - name: Checkout git repository + uses: actions/checkout@v4.1.6 + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '23' + - name: Pre-commit checks + uses: pre-commit/action@v3.0.1 + - name: pre-commit-c-lite + uses: pre-commit-ci/lite-action@v1.1.0 + if: always() build: runs-on: ${{ matrix.os }} + needs: [ pre-commit ] strategy: + fail-fast: true matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] + os: [ windows-latest, ubuntu-latest, macos-13 ] + max-parallel: 1 steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v4 + - uses: actions/checkout@v4.1.6 + - name: Set up JDK 23 + uses: actions/setup-java@v4.2.1 with: distribution: 'temurin' - java-version: 17 + java-version: 23 architecture: x64 - - name: Cache Maven packages - uses: actions/cache@v3.3.1 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2- + cache: 'maven' - name: Build with Maven run: mvn --no-transfer-progress verify diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cc87c2e81a..7fff20169a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,21 +13,15 @@ jobs: environment: name: release steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 23 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 23 architecture: x64 - - - name: Cache Maven packages - uses: actions/cache@v3.3.1 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 + cache: 'maven' - name: "Set labels for ${{ github.ref }}" run: | @@ -46,7 +40,7 @@ jobs: files: | target/webgoat-${{ env.WEBGOAT_MAVEN_VERSION }}.jar body: | - ## Version ${{ github.ref_name }} + ## Version ${{ github.ref_name }} ### New functionality @@ -55,8 +49,8 @@ jobs: ### Bug fixes - [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743) - - Full change log: https://github.com/WebGoat/WebGoat/compare/${{ github.ref_name }}...${{ github.ref_name }} + + Full change log: https://github.com/WebGoat/WebGoat/compare/${{ github.ref_name }}...${{ github.ref_name }} ## Contributors @@ -74,7 +68,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: "Set up QEMU" - uses: docker/setup-qemu-action@v2.2.0 + uses: docker/setup-qemu-action@v3.3.0 with: platforms: all @@ -82,13 +76,13 @@ jobs: uses: docker/setup-buildx-action@v3 - name: "Login to dockerhub" - uses: docker/login-action@v3.0.0 + uses: docker/login-action@v3.3.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: "Build and push WebGoat" - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v6.11.0 with: context: ./ file: ./Dockerfile @@ -101,7 +95,7 @@ jobs: webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} - name: "Build and push WebGoat desktop" - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v6.11.0 with: context: ./ file: ./Dockerfile_desktop @@ -118,15 +112,15 @@ jobs: needs: [ release ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up JDK 17 + - name: Set up JDK 23 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 23 architecture: x64 - name: Set version to next snapshot @@ -147,4 +141,3 @@ jobs: github_token: "${{ secrets.GITHUB_TOKEN }}" title: ${{ github.event.commits[0].message }} target_branch: main - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ab517903d7..f6ea94ad6a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,27 +21,21 @@ jobs: name: "Robot framework test" steps: # Uses an default action to checkout the code - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.6 # Uses an action to add Python to the VM - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.7' architecture: x64 - # Uses an action to add JDK 17 to the VM (and mvn?) - - name: set up JDK 17 - uses: actions/setup-java@v4 + # Uses an action to add JDK 23 to the VM (and mvn?) + - name: set up JDK 23 + uses: actions/setup-java@v4.2.1 with: distribution: 'temurin' - java-version: 17 + java-version: 23 architecture: x64 - #Uses an action to set up a cache using a certain key based on the hash of the dependencies - - name: Cache Maven packages - uses: actions/cache@v3.3.1 - with: - path: ~/.m2 - key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ubuntu-latest-m2- + cache: 'maven' - uses: BSFishy/pip-action@v1 with: packages: | diff --git a/.gitignore b/.gitignore index f914d3ab7f..06de08b13d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,4 +56,4 @@ TestClass.class **/*.flattened-pom.xml /.gitconfig -webgoat.gitconfig \ No newline at end of file +webgoat.gitconfig diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..b49ff10dc9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -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 diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index bed6f51c05..2e72cb505d 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -16,4 +16,4 @@ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, B Getting Source ============== -Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. \ No newline at end of file +Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. diff --git a/CREATE_RELEASE.md b/CREATE_RELEASE.md index fa1244b370..fc22906f04 100644 --- a/CREATE_RELEASE.md +++ b/CREATE_RELEASE.md @@ -11,11 +11,11 @@ Update the release notes with the correct version. Use `git shortlog -s -n --sin committers. In order to fetch the list of issues included use: `git log --graph --pretty='%C(auto)%d%Creset%s' v2023.4..origin/main` ``` -mvn versions:set +mvn versions:set << update release notes >> mvn verify git commit .... -git tag v2023.01 +git tag v2023.01 git push --tags ``` diff --git a/Dockerfile b/Dockerfile index 51bbcb1c16..c110ae3c21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ -FROM docker.io/eclipse-temurin:21.0.1_12-jre -LABEL NAME = "WebGoat: A deliberately insecure Web Application" -MAINTAINER "WebGoat team" +# We need JDK as some of the lessons needs to be able to compile Java code +FROM docker.io/eclipse-temurin:23-jdk-noble + +LABEL name="WebGoat: A deliberately insecure Web Application" +LABEL maintainer="WebGoat team" RUN \ useradd -ms /bin/bash webgoat && \ @@ -33,3 +35,6 @@ ENTRYPOINT [ "java", \ "--add-opens", "java.base/java.io=ALL-UNNAMED", \ "-Drunning.in.docker=true", \ "-jar", "webgoat.jar", "--server.address", "0.0.0.0" ] + +HEALTHCHECK --interval=5s --timeout=3s \ + CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1 diff --git a/Dockerfile_desktop b/Dockerfile_desktop index e6dbd15cb3..7bee56d038 100644 --- a/Dockerfile_desktop +++ b/Dockerfile_desktop @@ -1,6 +1,6 @@ FROM lscr.io/linuxserver/webtop:ubuntu-xfce -LABEL NAME = "WebGoat: A deliberately insecure Web Application" -MAINTAINER "WebGoat team" +LABEL name="WebGoat: A deliberately insecure Web Application" +LABEL maintainer="WebGoat team" WORKDIR /config @@ -9,26 +9,39 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh COPY config/desktop/start_zap.sh /config/start_zap.sh COPY config/desktop/WebGoat.txt /config/Desktop/ +RUN \ + apt-get update && \ + apt-get --yes install vim nano gzip + +RUN \ + case $(uname -m) in \ + x86_64) ARCH=x64;; \ + aarch64) ARCH=aarch64;; \ + *) ARCH=unknown;; \ + esac && \ + echo ${ARCH} + +RUN \ + curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \ + tar zfxv ZAP_2.15.0_Linux.tar.gz && \ + rm -rf ZAP_2.15.0_Linux.tar.gz + RUN \ case $(uname -m) in \ x86_64) ARCH=x64;; \ aarch64) ARCH=aarch64;; \ *) ARCH=unknown;; \ esac && \ - curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \ - tar zfxv ZAP_2.12.0_Linux.tar.gz && \ - rm -rf ZAP_2.12.0_Linux.tar.gz && \ - curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ - tar zfxv OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ - rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ + echo "oeps == ${ARCH}==" && \ + curl -L https://github.com/adoptium/temurin23-binaries/releases/download/jdk-23.0.1%2B11/OpenJDK23U-jre_"${ARCH}"_linux_hotspot_23.0.1_11.tar.gz -o java.tar.gz && \ + tar zfxv java.tar.gz && \ + rm -rf java.tar.gz && \ chmod +x /config/start_webgoat.sh && \ chmod +x /config/start_zap.sh && \ - apt-get update && \ - apt-get --yes install vim nano && \ - echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \ + mv /config/jdk-23.0.1+11-jre /config/java-jdk && \ + echo "JAVA_HOME=/config/java-jdk/" >> .bash_aliases && \ echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases - -ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre +ENV JAVA_HOME=/config/java-jdk WORKDIR /config/Desktop diff --git a/FAQ.md b/FAQ.md index 3e2968344f..db652e04ff 100644 --- a/FAQ.md +++ b/FAQ.md @@ -5,4 +5,3 @@ ### Integration tests fail Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line. - diff --git a/LICENSE.txt b/LICENSE.txt index 573d2b4ebd..cb10f880d0 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -16,4 +16,4 @@ not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, B Getting Source ============== -Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. \ No newline at end of file +Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. diff --git a/README.md b/README.md index b8203e0bd7..3efa8e7ac8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # WebGoat: A deliberately insecure Web Application [![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) -[![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/) +[![java-jdk](https://img.shields.io/badge/java%20jdk-23-green.svg)](https://jdk.java.net/) [![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -80,11 +80,21 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel ```shell export TZ=Europe/Amsterdam # or your timezone -java -Dfile.encoding=UTF-8 -jar webgoat-2023.5.jar +java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar ``` Click the link in the log to start WebGoat. +### 3.1 Running on a different port + +If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter: + +```shell +java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002 +``` + +For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties). + ## 4. Run from the sources ### Prerequisites: @@ -144,7 +154,7 @@ For instance running as a jar on a Linux/macOS it will look like this: export TZ=Europe/Amsterdam # or your timezone export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" -java -jar target/webgoat-2023.6-SNAPSHOT.jar +java -jar target/webgoat-2023.8-SNAPSHOT.jar ``` Or in a docker run it would (once this version is pushed into docker hub) look like this: diff --git a/README_I18N.md b/README_I18N.md index 6a4769f1e8..1286421c44 100644 --- a/README_I18N.md +++ b/README_I18N.md @@ -16,19 +16,19 @@ The following steps are required when you want to add a new language 1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html) 1. Add the parts for showing the flag and providing the correct value for the flag= parameter -2. -3. Add a flag image to src/main/resources/webgoat/static/css/img + 2. +2. Add a flag image to src/main/resources/webgoat/static/css/img 1. See the main_new.html for a link to download flag resources -4. Add a welcome page to the introduction lesson +3. Add a welcome page to the introduction lesson 1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish) 2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable -5. Translate the main labels +4. Translate the main labels 1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish) 2. Translate the label values -6. Optionally translate lessons by +5. Optionally translate lessons by 1. Adding lang specifc adoc files in documentation folder of the lesson 2. Adding WebGoatLabels.properties of a specific language if you want to -7. Run mvn clean to see if the LabelAndHintIntegration test passes -8. Run WebGoat and verify that your own language and the other languages work as expected +6. Run mvn clean to see if the LabelAndHintIntegration test passes +7. Run WebGoat and verify that your own language and the other languages work as expected If you only want to translate more for a certain language, you only need to do step 4-8 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0c29bfccb5..6a2842d6e7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -215,4 +215,3 @@ Special thanks to the following contributors providing us with a pull request: And everyone who provided feedback through Github. Team WebGoat - diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 6ce36f4e11..6ad83bb0f6 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -8,4 +8,4 @@ - \ No newline at end of file + diff --git a/config/desktop/start_webgoat.sh b/config/desktop/start_webgoat.sh index fe2fa042de..b57a71f24d 100644 --- a/config/desktop/start_webgoat.sh +++ b/config/desktop/start_webgoat.sh @@ -1,6 +1,6 @@ #!/bin/sh -/config/jdk-17.0.6+10-jre/bin/java \ +/config/java-jdk/bin/java \ -Duser.home=/config \ -Dfile.encoding=UTF-8 \ -DTZ=Europe/Amsterdam \ diff --git a/config/desktop/start_zap.sh b/config/desktop/start_zap.sh index 5a0cb999b3..0ad8f58021 100644 --- a/config/desktop/start_zap.sh +++ b/config/desktop/start_zap.sh @@ -1,3 +1,3 @@ #!/bin/sh -/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar +/config/java-jdk/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar diff --git a/docs/README.md b/docs/README.md index 6f04843410..10ddf6f049 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,3 @@ # WebGoat landing page Old GitHub page which now redirects to OWASP website. - diff --git a/docs/index.html b/docs/index.html index 78d11aff28..73e60fd445 100644 --- a/docs/index.html +++ b/docs/index.html @@ -11,4 +11,4 @@

The page been moved to https://owasp.org/www-project-webgoat/

- \ No newline at end of file + diff --git a/mvnw b/mvnw index d2f0ea3808..8719bb52c4 100755 --- a/mvnw +++ b/mvnw @@ -246,7 +246,7 @@ else else curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f fi - + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" diff --git a/pom.xml b/pom.xml index 2cc873a8e1..6facc958c2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ org.springframework.boot spring-boot-starter-parent - 3.1.5 + 3.4.1 org.owasp.webgoat webgoat - 2023.8 + 2024.2-SNAPSHOT jar WebGoat @@ -29,13 +29,6 @@ - - mayhew64 - Bruce Mayhew - webgoat@owasp.org - OWASP - https://github.com/WebGoat/WebGoat - nbaars Nanne Baars @@ -43,11 +36,6 @@ https://github.com/nbaars Europe/Amsterdam - - misfir3 - Jason White - jason.white@owasp.org - zubcevic René Zubcevic @@ -58,43 +46,8 @@ Àngel Ollé Blázquez angel@olleb.com - - jwayman - Jeff Wayman - - - - dcowden - Dave Cowden - - - - lawson89 - Richard Lawson - - - - dougmorato - Doug Morato - doug.morato@owasp.org - OWASP - https://github.com/dougmorato - America/New_York - - https://avatars2.githubusercontent.com/u/9654?v=3&s=150 - - - - - OWASP WebGoat Mailing List - https://lists.owasp.org/mailman/listinfo/owasp-webgoat - Owasp-webgoat-request@lists.owasp.org - owasp-webgoat@lists.owasp.org - http://lists.owasp.org/pipermail/owasp-webgoat/ - - scm:git:git@github.com:WebGoat/WebGoat.git scm:git:git@github.com:WebGoat/WebGoat.git @@ -109,61 +62,58 @@ - 2.5.10 - 5.3.1 + 3.0.0 + 5.3.3 3.3.0 - 3.3.1 + 3.6.0 3.2.1 - 2.15.1 - 3.12.0 - 1.10.0 - 32.1.3-jre + 1.27.1 + 2.18.0 + 3.14.0 + 1.13.0 + 33.4.0-jre 0.8.11 - 17 + 23 2.3.1 0.9.1 0.9.3 - 3.7.0 - 1.16.1 - 3.8.0 + 3.7.1 + 1.18.3 + 3.13.0 2.22.0 3.1.2 3.1.1 3.1.0 - 3.2.1 - 17 - 17 + 3.5.2 + full + 23 + 23 + 3.15.0 UTF-8 UTF-8 - 3.1.1.RELEASE - 5.3.3 + 3.1.2.RELEASE + 60 + 5.9.2 / false - 0.53 + 0.59 / - 2.27.2 + 3.10.0 1.2 1.4.5 - 1.8.0 + 1.9.0 - - - org.ow2.asm - asm - 9.5 - - org.apache.commons commons-exec - 1.3 + 1.4.0 org.asciidoctor @@ -248,8 +198,8 @@ ${webjars-locator-core.version} - com.github.tomakehurst - wiremock + org.wiremock + wiremock-standalone ${wiremock.version} @@ -260,12 +210,12 @@ org.apache.commons commons-compress - 1.25.0 + ${commons-compress.version} org.jruby jruby - 9.4.3.0 + 9.4.9.0 @@ -281,27 +231,30 @@ org.projectlombok lombok + 1.18.36 provided true + + org.testcontainers + testcontainers + 1.20.4 + test + + + org.testcontainers + junit-jupiter + 1.20.4 + test + javax.xml.bind jaxb-api ${jaxb.version} - - org.springframework.boot - spring-boot-starter-undertow - org.springframework.boot spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - - org.springframework.boot @@ -311,6 +264,10 @@ org.flywaydb flyway-core + + org.flywaydb + flyway-database-hsqldb + org.asciidoctor asciidoctorj @@ -417,6 +374,12 @@ jaxb-impl runtime + + com.github.terma + javaniotcpproxy + 1.6 + test + org.springframework.boot @@ -429,10 +392,8 @@ test - com.github.tomakehurst - wiremock - 3.0.0-beta-2 - test + org.wiremock + wiremock-standalone io.rest-assured @@ -548,6 +509,7 @@ ${maven-surefire-plugin.version} 600 + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED @@ -555,8 +517,6 @@ --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED **/*IntegrationTest.java - src/it/java - org/owasp/webgoat/*Test @@ -636,7 +596,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.3.0 + 3.5.0 restrict-log4j-versions @@ -660,10 +620,6 @@ org.apache.maven.plugins maven-compiler-plugin - - 17 - 17 - @@ -735,24 +691,18 @@ --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens - java.base/java.text=ALL-UNNAMED - --add-opens java.desktop/java.beans=ALL-UNNAMED --add-opens - java.desktop/java.awt.font=ALL-UNNAMED - --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED - --add-opens - java.base/sun.nio.ch=ALL-UNNAMED - --add-opens - java.base/java.io=ALL-UNNAMED ${project.build.directory}/webgoat-${project.version}.jar false + ${waittimeForServerStart} + http://127.0.0.1:${webgoat.port}${webgoat.context}login diff --git a/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java index 61582fd434..1add0d7252 100644 --- a/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java @@ -15,7 +15,7 @@ void testLesson() { assignment2(); assignment3(); - checkResults("/access-control"); + checkResults("MissingFunctionAC"); } private void assignment3() { diff --git a/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java index 54231e8935..ecff556d56 100644 --- a/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java @@ -86,7 +86,7 @@ public void shutdown() throws IOException { // logout(); login(); // because old cookie got replaced and invalidated startLesson("CSRF", false); - checkResults("/csrf"); + checkResults("CSRF"); } private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { @@ -103,7 +103,7 @@ private void uploadTrickHtml(String htmlName, String htmlContent) throws IOExcep .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", htmlName, htmlContent.getBytes()) - .post(webWolfUrl("fileupload")) + .post(new WebWolfUrlBuilder().path("fileupload").build()) .then() .extract() .response() @@ -118,7 +118,7 @@ private String callTrickHtml(String htmlName) { .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("files/" + this.getUser() + "/" + htmlName)) + .get(new WebWolfUrlBuilder().path("files/%s/%s", this.getUser(), htmlName).build()) .then() .extract() .response() @@ -136,7 +136,7 @@ private void checkAssignment3(String goatURL) { .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("files/fake.html")) + .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build()) .post(goatURL) .then() .extract() @@ -163,7 +163,7 @@ private void checkAssignment4(String goatURL) { .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("files/fake.html")) + .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build()) .formParams(params) .post(goatURL) .then() @@ -184,7 +184,7 @@ private void checkAssignment7(String goatURL) { .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("files/fake.html")) + .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build()) .contentType(ContentType.TEXT) .body( "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is" @@ -217,7 +217,7 @@ private void checkAssignment8(String goatURL) { .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("files/fake.html")) + .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build()) .params(params) .post(goatURL) .then() @@ -254,15 +254,15 @@ private void checkAssignment8(String goatURL) { RestAssured.given() .cookie("JSESSIONID", getWebGoatCookie()) .relaxedHTTPSValidation() - .get(url("service/lessonoverview.mvc")) + .get(url("service/lessonoverview.mvc/CSRF")) .then() .extract() .jsonPath() .getObject("$", Overview[].class); - // assertThat(assignments) - // .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) - // .extracting(o -> o.solved) - // .containsExactly(true); + assertThat(assignments) + .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) + .extracting(o -> o.solved) + .containsExactly(true); } @Data diff --git a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java index 30e771432b..917f8716be 100644 --- a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java @@ -50,9 +50,9 @@ void testChallenge1() { String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); params.clear(); params.put("flag", flag); - checkAssignment(url("challenge/flag"), params, true); + checkAssignment(url("challenge/flag/1"), params, true); - checkResults("/challenge/1"); + checkResults("Challenge1"); List capturefFlags = RestAssured.given() @@ -92,9 +92,9 @@ void testChallenge5() { String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); params.clear(); params.put("flag", flag); - checkAssignment(url("challenge/flag"), params, true); + checkAssignment(url("challenge/flag/5"), params, true); - checkResults("/challenge/5"); + checkResults("Challenge5"); List capturefFlags = RestAssured.given() @@ -126,7 +126,7 @@ void testChallenge7() { .extract() .asString(); - // Should send an email to WebWolf inbox this should give a hint to the link being static + // Should email WebWolf inbox this should give a hint to the link being static RestAssured.given() .when() .relaxedHTTPSValidation() @@ -144,7 +144,7 @@ void testChallenge7() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("mail")) + .get(new WebWolfUrlBuilder().path("mail").build()) .then() .extract() .response() @@ -165,6 +165,6 @@ void testChallenge7() { .asString(); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); - checkAssignment(url("challenge/flag"), Map.of("flag", flag), true); + checkAssignment(url("challenge/flag/7"), Map.of("flag", flag), true); } } diff --git a/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java index efb9c35b12..857b4429a3 100644 --- a/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java @@ -42,7 +42,7 @@ public void runTests() { checkAssignmentDefaults(); - checkResults("/crypto"); + checkResults("Cryptography"); } private void checkAssignment2() { diff --git a/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java index c3c26f359d..1f13255243 100644 --- a/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java @@ -28,6 +28,6 @@ public void runTests() throws IOException { } checkAssignment(url("InsecureDeserialization/task"), params, true); - checkResults("/InsecureDeserialization/"); + checkResults("InsecureDeserialization"); } } diff --git a/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java index 0ee905d157..db8557dad9 100644 --- a/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java @@ -31,7 +31,17 @@ public void httpBasics() { params.put("magic_num", "33"); checkAssignment(url("HttpBasics/attack2"), params, true); - checkResults("/HttpBasics/"); + checkResults("HttpBasics"); + } + + @Test + public void solveAsOtherUserHttpBasics() { + login("steven"); + startLesson("HttpBasics"); + Map params = new HashMap<>(); + params.clear(); + params.put("person", "goatuser"); + checkAssignment(url("HttpBasics/attack1"), params, true); } @Test @@ -51,7 +61,7 @@ public void httpProxies() { .path("lessonCompleted"), CoreMatchers.is(true)); - checkResults("/HttpProxies/"); + checkResults("HttpProxies"); } @Test @@ -73,7 +83,7 @@ public void cia() { "question_3_solution", "Solution 2: The systems security is compromised even if only one goal is harmed."); checkAssignment(url("cia/quiz"), params, true); - checkResults("/cia/"); + checkResults("CIA"); } @Test @@ -96,7 +106,7 @@ public void vulnerableComponents() { params.clear(); params.put("payload", solution); checkAssignment(url("VulnerableComponents/attack1"), params, true); - checkResults("/VulnerableComponents/"); + checkResults("VulnerableComponents"); } } @@ -108,7 +118,7 @@ public void insecureLogin() { params.put("username", "CaptainJack"); params.put("password", "BlackPearl"); checkAssignment(url("InsecureLogin/task"), params, true); - checkResults("/InsecureLogin/"); + checkResults("InsecureLogin"); } @Test @@ -118,7 +128,7 @@ public void securePasswords() { params.clear(); params.put("password", "ajnaeliclm^&&@kjn."); checkAssignment(url("SecurePasswords/assignment"), params, true); - checkResults("SecurePasswords/"); + checkResults("SecurePasswords"); startLesson("AuthBypass"); params.clear(); @@ -128,7 +138,7 @@ public void securePasswords() { params.put("verifyMethod", "SEC_QUESTIONS"); params.put("userId", "12309746"); checkAssignment(url("auth-bypass/verify-account"), params, true); - checkResults("/auth-bypass/"); + checkResults("AuthBypass"); startLesson("HttpProxies"); MatcherAssert.assertThat( @@ -144,7 +154,7 @@ public void securePasswords() { .extract() .path("lessonCompleted"), CoreMatchers.is(true)); - checkResults("/HttpProxies/"); + checkResults("HttpProxies"); } @Test @@ -180,7 +190,7 @@ public void chrome() { params.put("network_num", "24"); checkAssignment(url("ChromeDevTools/network"), params, true); - checkResults("/ChromeDevTools/"); + checkResults("ChromeDevTools"); } @Test @@ -194,7 +204,7 @@ public void authByPass() { params.put("verifyMethod", "SEC_QUESTIONS"); params.put("userId", "12309746"); checkAssignment(url("auth-bypass/verify-account"), params, true); - checkResults("/auth-bypass/"); + checkResults("AuthBypass"); } @Test @@ -205,6 +215,6 @@ public void lessonTemplate() { params.put("param1", "secr37Value"); params.put("param2", "Main"); checkAssignment(url("lesson-template/sample-attack"), params, true); - checkResults("/lesson-template/"); + checkResults("LessonTemplate"); } } diff --git a/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java index eba30f764a..4b75e53144 100644 --- a/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java @@ -30,7 +30,7 @@ Iterable testIDORLesson() { @AfterEach public void shutdown() { - checkResults("/IDOR"); + checkResults("IDOR"); } private void loginIDOR() { diff --git a/src/it/java/org/owasp/webgoat/IntegrationTest.java b/src/it/java/org/owasp/webgoat/IntegrationTest.java index 06a626047d..e115dc8bcf 100644 --- a/src/it/java/org/owasp/webgoat/IntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IntegrationTest.java @@ -3,6 +3,7 @@ import static io.restassured.RestAssured.given; import io.restassured.RestAssured; +import io.restassured.filter.log.LogDetail; import io.restassured.http.ContentType; import java.util.Map; import lombok.Getter; @@ -15,43 +16,80 @@ public abstract class IntegrationTest { private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080"); - private static String webGoatContext = - System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/"); - @Getter private static String webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090"); @Getter private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1"); - @Getter - private static String webGoatHost = System.getenv().getOrDefault("WEBGOAT_HOST", "127.0.0.1"); - + private static String webGoatContext = + System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/"); private static String webWolfContext = System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/"); - private static boolean useSSL = - Boolean.valueOf(System.getenv().getOrDefault("WEBGOAT_SSLENABLED", "false")); - private static String webgoatUrl = - (useSSL ? "https://" : "http://") + webGoatHost + ":" + webGoatPort + webGoatContext; - private static String webWolfUrl = "http://" + webWolfHost + ":" + webWolfPort + webWolfContext; @Getter private String webGoatCookie; @Getter private String webWolfCookie; @Getter private final String user = "webgoat"; protected String url(String url) { - return webgoatUrl + url; + return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url); } - protected String webWolfUrl(String url) { - return webWolfUrl + url; - } + protected class WebWolfUrlBuilder { - protected String webWolfFileUrl(String fileName) { - return webWolfUrl("files") + "/" + getUser() + "/" + fileName; + private boolean attackMode = false; + private String path = null; + + protected String build() { + return "http://localhost:%s%s%s" + .formatted(webWolfPort, webWolfContext, path != null ? path : ""); + } + + /** + * 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 + * + *

Start the test and connect a remote debugger in IntelliJ to localhost:5005 and attach it. + */ + // private static GenericContainer webGoatContainer = + // new GenericContainer(new ImageFromDockerfile("webgoat").withFileFromPath("/", + // Paths.get("."))) + // .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("webgoat"))) + // .withExposedPorts(8080, 9090, 5005) + // .withEnv( + // "_JAVA_OPTIONS", + // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005") + // .waitingFor(Wait.forHealthcheck()); + // + // static { + // webGoatContainer.start(); + // } + @BeforeEach public void login() { + login("webgoat"); + } + + protected void login(String user) { String location = given() .when() @@ -60,6 +98,8 @@ public void login() { .formParam("password", "password") .post(url("login")) .then() + .log() + .ifValidationFails(LogDetail.ALL) // Log the response details if validation fails .cookie("JSESSIONID") .statusCode(302) .extract() @@ -100,7 +140,7 @@ public void login() { .relaxedHTTPSValidation() .formParam("username", user) .formParam("password", "password") - .post(webWolfUrl("login")) + .post(new WebWolfUrlBuilder().path("login").build()) .then() .statusCode(302) .cookie("WEBWOLFSESSION") @@ -131,7 +171,7 @@ public void startLesson(String lessonName, boolean restart) { .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/restartlesson.mvc")) + .get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName))) .then() .statusCode(200); } @@ -167,23 +207,18 @@ public void checkAssignmentWithPUT(String url, Map params, boolean ex CoreMatchers.is(expectedResult)); } - // TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are - // only required to be in the same package) - public void checkResults(String prefix) { - checkResults(); - - MatcherAssert.assertThat( + public void checkResults(String lesson) { + var result = RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/lessonoverview.mvc")) - .then() - .statusCode(200) - .extract() - .jsonPath() - .getList("assignment.path"), - CoreMatchers.everyItem(CoreMatchers.startsWith(prefix))); + .get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson))) + .andReturn(); + + MatcherAssert.assertThat( + result.then().statusCode(200).extract().jsonPath().getList("solved"), + CoreMatchers.everyItem(CoreMatchers.is(true))); } public void checkResults() { @@ -238,7 +273,7 @@ public String getWebWolfFileServerLocation() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("file-server-location")) + .get(new WebWolfUrlBuilder().path("file-server-location").build()) .then() .extract() .response() @@ -266,7 +301,7 @@ public void cleanMailbox() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .delete(webWolfUrl("mail")) + .delete(new WebWolfUrlBuilder().path("mail").build()) .then() .statusCode(HttpStatus.ACCEPTED.value()); } diff --git a/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java index c970a15375..e69f9690ef 100644 --- a/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java @@ -13,7 +13,6 @@ import io.restassured.RestAssured; import java.io.IOException; import java.nio.charset.Charset; -import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; @@ -34,7 +33,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest { @Test - public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { + public void solveAssignment() throws IOException, NoSuchAlgorithmException { startLesson("JWT"); decodingToken(); @@ -51,11 +50,10 @@ public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlg quiz(); - checkResults("/JWT/"); + checkResults("JWT"); } private String generateToken(String key) { - return Jwts.builder() .setIssuer("WebGoat Token Builder") .setAudience("webgoat.org") @@ -96,7 +94,7 @@ private void decodingToken() { CoreMatchers.is(true)); } - private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { + private void findPassword() { String accessToken = RestAssured.given() @@ -256,7 +254,7 @@ private void deleteTomThroughJkuClaim() throws NoSuchAlgorithmException { .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", "jwks.json", jwks.toJson().getBytes()) - .post(webWolfUrl("fileupload")) + .post(new WebWolfUrlBuilder().path("fileupload").build()) .then() .extract() .response() @@ -265,7 +263,10 @@ private void deleteTomThroughJkuClaim() throws NoSuchAlgorithmException { Map header = new HashMap(); header.put(Header.TYPE, Header.JWT_TYPE); - header.put(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json")); + header.put( + JwsHeader.JWK_SET_URL, + new WebWolfUrlBuilder().attackMode().path("files/%s/jwks.json", getUser()).build()); + String token = Jwts.builder() .setHeader(header) diff --git a/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java index ae6feb803b..181504b8bd 100644 --- a/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java @@ -151,7 +151,6 @@ public void testLabels() { checkLang(propsDefault, "nl"); checkLang(propsDefault, "de"); checkLang(propsDefault, "fr"); - checkLang(propsDefault, "ru"); } private Properties getProperties(String lang) { diff --git a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java index f3b700b9a9..9dd7476b5b 100644 --- a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java @@ -85,7 +85,7 @@ public void sendEmailShouldBeAvailableInWebWolf() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("mail")) + .get(new WebWolfUrlBuilder().path("mail").build()) .then() .extract() .response() @@ -99,7 +99,7 @@ public void sendEmailShouldBeAvailableInWebWolf() { public void shutdown() { // this will run only once after the list of dynamic tests has run, this is to test if the // lesson is marked complete - checkResults("/PasswordReset"); + checkResults("PasswordReset"); } private void changePassword(String link) { @@ -119,7 +119,7 @@ private String getPasswordResetLinkFromLandingPage() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("requests")) + .get(new WebWolfUrlBuilder().path("requests").build()) .then() .extract() .response() diff --git a/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java index 22a91100f8..6deecedd68 100644 --- a/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java @@ -147,6 +147,6 @@ private void assignment5() throws IOException { void shutdown() { // this will run only once after the list of dynamic tests has run, this is to test if the // lesson is marked complete - checkResults("/PathTraversal"); + checkResults("PathTraversal"); } } diff --git a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java index 969ff6d2e1..07f56b9660 100644 --- a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java @@ -29,7 +29,7 @@ public void runTests() throws InterruptedException { .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(Map.of("flag", "test")) - .post(url("challenge/flag")); + .post(url("challenge/flag/1")); }; ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS); List> flagCalls = diff --git a/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java index e0b42a0aab..ba94cbd4b7 100644 --- a/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java @@ -1,6 +1,5 @@ package org.owasp.webgoat; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; @@ -8,7 +7,7 @@ public class SSRFIntegrationTest extends IntegrationTest { @Test - public void runTests() throws IOException { + public void runTests() { startLesson("SSRF"); Map params = new HashMap<>(); @@ -21,6 +20,6 @@ public void runTests() throws IOException { checkAssignment(url("SSRF/task2"), params, true); - checkResults("/SSRF/"); + checkResults("SSRF"); } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java index feec674c0f..11cbed2f81 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java @@ -56,6 +56,6 @@ public void runTests() { "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true); - checkResults("/SqlInjectionAdvanced/"); + checkResults("SqlInjectionAdvanced"); } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java index ac2f8e2fdb..661c70979a 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java @@ -73,6 +73,6 @@ public void runTests() { params.put("action_string", sql_13); checkAssignment(url("SqlInjection/attack10"), params, true); - checkResults("/SqlInjection/"); + checkResults("SqlInjection"); } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java index 1a1dc39c72..1cc8b95014 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java @@ -80,6 +80,6 @@ public void runTests() { params.put("ip", "104.130.219.202"); checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true); - checkResults(); + checkResults("SqlInjectionMitigations"); } } diff --git a/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java index 654f86399b..16d078db40 100644 --- a/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java @@ -23,7 +23,7 @@ public void runTests() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("mail")) + .get(new WebWolfUrlBuilder().path("mail").build()) .then() .extract() .response() @@ -53,7 +53,7 @@ public void runTests() { .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .queryParams(params) - .get(webWolfUrl("landing")) + .get(new WebWolfUrlBuilder().path("landing").build()) .then() .statusCode(200); responseBody = @@ -61,7 +61,7 @@ public void runTests() { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("requests")) + .get(new WebWolfUrlBuilder().path("requests").build()) .then() .extract() .response() @@ -72,6 +72,6 @@ public void runTests() { params.put("uniqueCode", uniqueCode); checkAssignment(url("WebWolf/landing"), params, true); - checkResults("/WebWolf"); + checkResults("WebWolfIntroduction"); } } diff --git a/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java index dc7a19a704..c3e391422b 100644 --- a/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java @@ -111,6 +111,6 @@ public void crossSiteScriptingAssignments() { + "MyCommentDAO.addComment(threadID, userID).getCleanHTML());"); checkAssignment(url("CrossSiteScripting/attack4"), params, true); - checkResults("/CrossSiteScripting"); + checkResults("CrossSiteScripting"); } } diff --git a/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java index 1448aec2f1..21598e5754 100644 --- a/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java @@ -3,9 +3,6 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import org.junit.jupiter.api.Test; public class XXEIntegrationTest extends IntegrationTest { @@ -28,47 +25,40 @@ public class XXEIntegrationTest extends IntegrationTest { """; private String webGoatHomeDirectory; - private String webWolfFileServerLocation; - /* - * This test is to verify that all is secure when XXE security patch is applied. - */ - @Test - public void xxeSecure() throws IOException { - startLesson("XXE"); - webGoatHomeDirectory = webGoatServerDirectory(); - webWolfFileServerLocation = getWebWolfFileServerLocation(); - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/enable-security.mvc")) - .then() - .statusCode(200); - checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false); - checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false); - checkAssignment( - url("xxe/blind"), - ContentType.XML, - "" + getSecret() + "", - false); - } + // TODO fix me + // /* + // * This test is to verify that all is secure when XXE security patch is applied. + // */ + // @Test + // public void xxeSecure() throws IOException { + // startLesson("XXE"); + // webGoatHomeDirectory = webGoatServerDirectory(); + // RestAssured.given() + // .when() + // .relaxedHTTPSValidation() + // .cookie("JSESSIONID", getWebGoatCookie()) + // .get(url("service/enable-security.mvc")) + // .then() + // .statusCode(200); + // checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false); + // checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false); + // checkAssignment( + // url("xxe/blind"), + // ContentType.XML, + // "" + getSecret() + "", + // false); + // } /** * This performs the steps of the exercise before the secret can be committed in the final step. * * @return - * @throws IOException */ - private String getSecret() throws IOException { - // remove any left over DTD - Path webWolfFilePath = Paths.get(webWolfFileServerLocation); - if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) { - Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd"))); - } + private String getSecret() { String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); - String dtd7String = - dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile); + String webWolfCallback = new WebWolfUrlBuilder().path("landing").attackMode().build(); + String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile); // upload DTD RestAssured.given() @@ -76,15 +66,17 @@ private String getSecret() throws IOException { .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .multiPart("file", "blind.dtd", dtd7String.getBytes()) - .post(webWolfUrl("fileupload")) + .post(new WebWolfUrlBuilder().path("fileupload").build()) .then() .extract() .response() .getBody() .asString(); + // upload attack String xxe7String = - xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser()); + xxe7.replace("WEBWOLFURL", new WebWolfUrlBuilder().attackMode().path("files").build()) + .replace("USERNAME", this.getUser()); checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false); // read results from WebWolf @@ -93,7 +85,7 @@ private String getSecret() throws IOException { .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("requests")) + .get(new WebWolfUrlBuilder().path("requests").build()) .then() .extract() .response() @@ -113,7 +105,6 @@ private String getSecret() throws IOException { public void runTests() throws IOException { startLesson("XXE", true); webGoatHomeDirectory = webGoatServerDirectory(); - webWolfFileServerLocation = getWebWolfFileServerLocation(); checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true); checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true); checkAssignment( @@ -121,6 +112,6 @@ public void runTests() throws IOException { ContentType.XML, "" + getSecret() + "", true); - checkResults("xxe/"); + checkResults("XXE"); } } diff --git a/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java index a496a0acbc..3f81798c18 100644 --- a/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java +++ b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java @@ -32,22 +32,23 @@ import static org.asciidoctor.Asciidoctor.Factory.create; -import io.undertow.util.Headers; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; -import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Attributes; +import org.asciidoctor.Options; import org.asciidoctor.extension.JavaExtensionRegistry; import org.owasp.webgoat.container.asciidoc.*; import org.owasp.webgoat.container.i18n.Language; import org.springframework.core.io.ResourceLoader; +import org.springframework.http.HttpHeaders; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @@ -135,17 +136,17 @@ private String computeResourceName(String resourceName, String language) { return computedResourceName; } - private Map createAttributes() { - Map attributes = new HashMap<>(); - attributes.put("source-highlighter", "coderay"); - attributes.put("backend", "xhtml"); - attributes.put("lang", determineLanguage()); - attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS); + private Options createAttributes() { - Map options = new HashMap<>(); - options.put("attributes", attributes); - - return options; + return Options.builder() + .attributes( + Attributes.builder() + .attribute("source-highlighter", "coderay") + .attribute("backend", "xhtml") + .attribute("lang", determineLanguage()) + .attribute("icons", org.asciidoctor.Attributes.FONT_ICONS) + .build()) + .build(); } private String determineLanguage() { @@ -159,7 +160,7 @@ private String determineLanguage() { log.debug("browser locale {}", browserLocale); return browserLocale.getLanguage(); } else { - String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING); + String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE); if (null != langHeader) { log.debug("browser locale {}", langHeader); return langHeader.substring(0, 2); diff --git a/src/main/java/org/owasp/webgoat/container/CurrentUser.java b/src/main/java/org/owasp/webgoat/container/CurrentUser.java new file mode 100644 index 0000000000..f94fb684b1 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/CurrentUser.java @@ -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 {} diff --git a/src/main/java/org/owasp/webgoat/container/CurrentUsername.java b/src/main/java/org/owasp/webgoat/container/CurrentUsername.java new file mode 100644 index 0000000000..0b9ffe1d11 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/CurrentUsername.java @@ -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 {} diff --git a/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java index 95e750a36c..c70782e0cd 100644 --- a/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java +++ b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.flywaydb.core.Flyway; import org.owasp.webgoat.container.service.RestartLessonService; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,8 +35,8 @@ public DataSource dataSource() { /** * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users * and 1 for lesson specific tables we use. This way we clean the data in the lesson database - * quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson - * related tables. + * quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we + * clean the lesson related tables. */ @Bean(initMethod = "migrate") public Flyway flyWayContainer() { @@ -60,7 +61,7 @@ public Function flywayLessons() { } @Bean - public LessonDataSource lessonDataSource() { - return new LessonDataSource(dataSource()); + public LessonDataSource lessonDataSource(DataSource dataSource) { + return new LessonDataSource(dataSource); } } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java b/src/main/java/org/owasp/webgoat/container/LessonResourceScanner.java similarity index 88% rename from src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java rename to src/main/java/org/owasp/webgoat/container/LessonResourceScanner.java index e21baef308..a6a55e6d13 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/LessonScanner.java +++ b/src/main/java/org/owasp/webgoat/container/LessonResourceScanner.java @@ -1,4 +1,4 @@ -package org.owasp.webgoat.container.lessons; +package org.owasp.webgoat.container; import java.io.IOException; import java.util.HashSet; @@ -12,13 +12,13 @@ @Component @Slf4j -public class LessonScanner { +public class LessonResourceScanner { private static final Pattern lessonPattern = Pattern.compile("^.*/lessons/([^/]*)/.*$"); @Getter private final Set lessons = new HashSet<>(); - public LessonScanner(ResourcePatternResolver resourcePatternResolver) { + public LessonResourceScanner(ResourcePatternResolver resourcePatternResolver) { try { var resources = resourcePatternResolver.getResources("classpath:/lessons/*/*"); for (var resource : resources) { diff --git a/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java b/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java index cfd5697204..59d903a100 100644 --- a/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java +++ b/src/main/java/org/owasp/webgoat/container/LessonTemplateResolver.java @@ -55,8 +55,8 @@ public class LessonTemplateResolver extends FileTemplateResolver { private static final String PREFIX = "lesson:"; - private ResourceLoader resourceLoader; - private Map resources = new HashMap<>(); + private final ResourceLoader resourceLoader; + private final Map resources = new HashMap<>(); public LessonTemplateResolver(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; diff --git a/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java index 6aff158131..deaaf24000 100644 --- a/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java +++ b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java @@ -40,7 +40,6 @@ import org.owasp.webgoat.container.i18n.Language; import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.i18n.PluginMessages; -import org.owasp.webgoat.container.lessons.LessonScanner; import org.owasp.webgoat.container.session.LabelDebugger; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -74,7 +73,7 @@ public class MvcConfiguration implements WebMvcConfigurer { private static final String UTF8 = "UTF-8"; - private final LessonScanner lessonScanner; + private final LessonResourceScanner lessonScanner; @Override public void addViewControllers(ViewControllerRegistry registry) { diff --git a/src/main/java/org/owasp/webgoat/container/WebGoat.java b/src/main/java/org/owasp/webgoat/container/WebGoat.java index 0efcf10e5b..71a4aa9fce 100644 --- a/src/main/java/org/owasp/webgoat/container/WebGoat.java +++ b/src/main/java/org/owasp/webgoat/container/WebGoat.java @@ -32,31 +32,27 @@ package org.owasp.webgoat.container; import java.io.File; -import org.owasp.webgoat.container.session.UserSessionData; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserRepository; -import org.owasp.webgoat.container.users.WebGoatUser; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.web.client.RestTemplate; @Configuration @ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"}) @PropertySource("classpath:application-webgoat.properties") @EnableAutoConfiguration +@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"}) +@EntityScan(basePackages = "org.owasp.webgoat.container") public class WebGoat { - @Autowired private UserRepository userRepository; - @Bean(name = "pluginTargetDirectory") public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) { return new File(webgoatHome); @@ -64,21 +60,8 @@ public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final Stri @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) - public WebSession webSession() { - WebGoatUser webGoatUser = null; - Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - if (principal instanceof WebGoatUser) { - webGoatUser = (WebGoatUser) principal; - } else if (principal instanceof DefaultOAuth2User) { - webGoatUser = userRepository.findByUsername(((DefaultOAuth2User) principal).getName()); - } - return new WebSession(webGoatUser); - } - - @Bean - @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) - public UserSessionData userSessionData() { - return new UserSessionData("test", "data"); + public LessonSession userSessionData() { + return new LessonSession(); } @Bean diff --git a/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java index e40be07b42..4344c5b93a 100644 --- a/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java +++ b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java @@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; @@ -57,7 +58,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests( auth -> auth.requestMatchers( - "/", "/favicon.ico", "/css/**", "/images/**", @@ -65,7 +65,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { "fonts/**", "/plugins/**", "/registration", - "/register.mvc") + "/register.mvc", + "/actuator/**") .permitAll() .anyRequest() .authenticated()) @@ -97,6 +98,7 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception } @Bean + @Primary public UserDetailsService userDetailsServiceBean() { return userDetailsService; } diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java b/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java index 0720794831..aa16d40a4c 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/EnvironmentExposure.java @@ -16,7 +16,7 @@ public class EnvironmentExposure implements ApplicationContextAware { private static ApplicationContext context; public static Environment getEnv() { - return (null != context) ? context.getEnvironment() : null; + return null != context ? context.getEnvironment() : null; } @Override diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java index 87a60a8799..383f1797c2 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/OperatingSystemMacro.java @@ -1,7 +1,8 @@ package org.owasp.webgoat.container.asciidoc; import java.util.Map; -import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; +import org.asciidoctor.ast.StructuralNode; import org.asciidoctor.extension.InlineMacroProcessor; public class OperatingSystemMacro extends InlineMacroProcessor { @@ -15,7 +16,8 @@ public OperatingSystemMacro(String macroName, Map config) { } @Override - public Object process(ContentNode contentNode, String target, Map attributes) { + public PhraseNode process( + StructuralNode contentNode, String target, Map attributes) { var osName = System.getProperty("os.name"); // see diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java index 7275ba9b16..fc4d2b9ec9 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/UsernameMacro.java @@ -1,7 +1,8 @@ package org.owasp.webgoat.container.asciidoc; import java.util.Map; -import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; +import org.asciidoctor.ast.StructuralNode; import org.asciidoctor.extension.InlineMacroProcessor; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; @@ -17,7 +18,8 @@ public UsernameMacro(String macroName, Map config) { } @Override - public Object process(ContentNode contentNode, String target, Map attributes) { + public PhraseNode process( + StructuralNode contentNode, String target, Map attributes) { var auth = SecurityContextHolder.getContext().getAuthentication(); var username = "unknown"; if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) { diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java index 12c283f9af..5e65e0a137 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatTmpDirMacro.java @@ -1,7 +1,8 @@ package org.owasp.webgoat.container.asciidoc; import java.util.Map; -import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; +import org.asciidoctor.ast.StructuralNode; import org.asciidoctor.extension.InlineMacroProcessor; public class WebGoatTmpDirMacro extends InlineMacroProcessor { @@ -15,11 +16,12 @@ public WebGoatTmpDirMacro(String macroName, Map config) { } @Override - public Object process(ContentNode contentNode, String target, Map attributes) { + public PhraseNode process( + StructuralNode structuralNode, String target, Map attributes) { var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory"); // see // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used - return createPhraseNode(contentNode, "quoted", env); + return createPhraseNode(structuralNode, "quoted", env); } } diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java index 09658e8b2d..539a0fffae 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebGoatVersionMacro.java @@ -1,7 +1,8 @@ package org.owasp.webgoat.container.asciidoc; import java.util.Map; -import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; +import org.asciidoctor.ast.StructuralNode; import org.asciidoctor.extension.InlineMacroProcessor; public class WebGoatVersionMacro extends InlineMacroProcessor { @@ -15,7 +16,8 @@ public WebGoatVersionMacro(String macroName, Map config) { } @Override - public Object process(ContentNode contentNode, String target, Map attributes) { + public PhraseNode process( + StructuralNode contentNode, String target, Map attributes) { var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version"); // see diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java index 119fd92698..2baa76a55e 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java @@ -2,7 +2,8 @@ import java.util.HashMap; import java.util.Map; -import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.PhraseNode; +import org.asciidoctor.ast.StructuralNode; import org.asciidoctor.extension.InlineMacroProcessor; /** @@ -21,7 +22,8 @@ public WebWolfMacro(String macroName, Map config) { } @Override - public Object process(ContentNode contentNode, String linkText, Map attributes) { + public PhraseNode process( + StructuralNode contentNode, String linkText, Map attributes) { var env = EnvironmentExposure.getEnv(); var hostname = env.getProperty("webwolf.url"); var target = (String) attributes.getOrDefault("target", "home"); @@ -36,7 +38,7 @@ public Object process(ContentNode contentNode, String linkText, Map attributes) { diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java index c48fb2f23b..9f81039fe5 100644 --- a/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentEndpoint.java @@ -25,68 +25,4 @@ package org.owasp.webgoat.container.assignments; -import lombok.Getter; -import org.owasp.webgoat.container.i18n.PluginMessages; -import org.owasp.webgoat.container.lessons.Initializeable; -import org.owasp.webgoat.container.session.UserSessionData; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.WebGoatUser; -import org.springframework.beans.factory.annotation.Autowired; - -public abstract class AssignmentEndpoint implements Initializeable { - - @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: - * - *

- Assignment is set to solved - Feedback message is set to 'assignment.solved' - * - *

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: - * - *

- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved' - * - *

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) {} -} +public interface AssignmentEndpoint {} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java b/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java deleted file mode 100644 index 0c19933936..0000000000 --- a/src/main/java/org/owasp/webgoat/container/assignments/AssignmentPath.java +++ /dev/null @@ -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 ""; -} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java b/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java index 3cf353c210..e9fcd1196a 100644 --- a/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/AttackResult.java @@ -30,82 +30,18 @@ import lombok.Getter; import org.owasp.webgoat.container.i18n.PluginMessages; +@Getter public class AttackResult { - public static class AttackResultBuilder { + private boolean lessonCompleted; + private String feedback; + private Object[] feedbackArgs; + private String output; + private Object[] outputArgs; + private final String assignment; + private boolean attemptWasMade; - private boolean lessonCompleted; - private PluginMessages messages; - private Object[] feedbackArgs; - private String feedbackResourceBundleKey; - private String output; - private Object[] outputArgs; - private AssignmentEndpoint assignment; - private boolean attemptWasMade = false; - - public AttackResultBuilder(PluginMessages messages) { - this.messages = messages; - } - - public AttackResultBuilder lessonCompleted(boolean lessonCompleted) { - this.lessonCompleted = lessonCompleted; - this.feedbackResourceBundleKey = "lesson.completed"; - return this; - } - - public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) { - this.lessonCompleted = lessonCompleted; - this.feedbackResourceBundleKey = resourceBundleKey; - return this; - } - - public AttackResultBuilder feedbackArgs(Object... args) { - this.feedbackArgs = args; - return this; - } - - public AttackResultBuilder feedback(String resourceBundleKey) { - this.feedbackResourceBundleKey = resourceBundleKey; - return this; - } - - public AttackResultBuilder output(String output) { - this.output = output; - return this; - } - - public AttackResultBuilder outputArgs(Object... args) { - this.outputArgs = args; - return this; - } - - public AttackResultBuilder attemptWasMade() { - this.attemptWasMade = true; - return this; - } - - public AttackResult build() { - return new AttackResult( - lessonCompleted, - messages.getMessage(feedbackResourceBundleKey, feedbackArgs), - messages.getMessage(output, output, outputArgs), - assignment.getClass().getSimpleName(), - attemptWasMade); - } - - public AttackResultBuilder assignment(AssignmentEndpoint assignment) { - this.assignment = assignment; - return this; - } - } - - @Getter private boolean lessonCompleted; - @Getter private String feedback; - @Getter private String output; - @Getter private final String assignment; - @Getter private boolean attemptWasMade; - - public AttackResult( + private AttackResult( boolean lessonCompleted, String feedback, String output, @@ -118,11 +54,33 @@ public AttackResult( this.attemptWasMade = attemptWasMade; } - public static AttackResultBuilder builder(PluginMessages messages) { - return new AttackResultBuilder(messages); + public AttackResult( + 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() { return lessonCompleted; } + + public AttackResult apply(PluginMessages pluginMessages) { + return new AttackResult( + lessonCompleted, + pluginMessages.getMessage(feedback, feedback, feedbackArgs), + pluginMessages.getMessage(output, output, outputArgs), + assignment, + attemptWasMade); + } } diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AttackResultBuilder.java b/src/main/java/org/owasp/webgoat/container/assignments/AttackResultBuilder.java new file mode 100644 index 0000000000..99e06a5cd4 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/assignments/AttackResultBuilder.java @@ -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: + * + *

- Assignment is set to solved - Feedback message is set to 'assignment.solved' + * + *

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: + * + *

- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved' + * + *

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); + } +} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/AttackResultMessageResponseBodyAdvice.java b/src/main/java/org/owasp/webgoat/container/assignments/AttackResultMessageResponseBodyAdvice.java new file mode 100644 index 0000000000..eea080c81a --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/assignments/AttackResultMessageResponseBodyAdvice.java @@ -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 { + + private final PluginMessages pluginMessages; + + public AttackResultMessageResponseBodyAdvice(PluginMessages pluginMessages) { + this.pluginMessages = pluginMessages; + } + + @Override + public boolean supports( + MethodParameter returnType, Class> converterType) { + return true; + } + + @Override + public Object beforeBodyWrite( + Object body, + MethodParameter returnType, + MediaType selectedContentType, + Class> selectedConverterType, + ServerHttpRequest request, + ServerHttpResponse response) { + if (body instanceof AttackResult a) { + return a.apply(pluginMessages); + } + return body; + } +} diff --git a/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java index aa3cd40cef..988755daf0 100644 --- a/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java @@ -22,27 +22,30 @@ package org.owasp.webgoat.container.assignments; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.users.UserProgress; +import org.owasp.webgoat.container.users.UserProgressRepository; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; @RestControllerAdvice public class LessonTrackerInterceptor implements ResponseBodyAdvice { - private UserTrackerRepository userTrackerRepository; - private WebSession webSession; + private final Course course; + private final UserProgressRepository userProgressRepository; - public LessonTrackerInterceptor( - UserTrackerRepository userTrackerRepository, WebSession webSession) { - this.userTrackerRepository = userTrackerRepository; - this.webSession = webSession; + public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) { + this.course = course; + this.userProgressRepository = userProgressRepository; } @Override @@ -65,18 +68,30 @@ public Object beforeBodyWrite( return o; } - protected AttackResult trackProgress(AttackResult attackResult) { - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); - if (userTracker == null) { - userTracker = new UserTracker(webSession.getUserName()); + private void trackProgress(AttackResult attackResult) { + var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + Assert.notNull(user, "User not found in SecurityContext"); + var username = realUsername(user); + + 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()) { - userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment()); + userProgress.assignmentSolved(lesson, attackResult.getAssignment()); } else { - userTracker.assignmentFailed(webSession.getCurrentLesson()); + userProgress.assignmentFailed(lesson); } - userTrackerRepository.save(userTracker); + userProgressRepository.save(userProgress); + } - return attackResult; + 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(); } } diff --git a/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java index 3cdd5e8d66..f5397c415c 100644 --- a/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java +++ b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java @@ -33,42 +33,20 @@ import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.session.Course; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class StartLesson { - private final WebSession ws; private final Course course; - public StartLesson(WebSession ws, Course course) { - this.ws = ws; + public StartLesson(Course course) { this.course = course; } - /** - * start. - * - * @return a {@link ModelAndView} object. - */ - @RequestMapping( - path = "startlesson.mvc", - method = {RequestMethod.GET, RequestMethod.POST}) - public ModelAndView start() { - var model = new ModelAndView(); - - model.addObject("course", course); - model.addObject("lesson", ws.getCurrentLesson()); - model.setViewName("lesson_content"); - - return model; - } - - @RequestMapping( + @GetMapping( value = {"*.lesson"}, produces = "text/html") public ModelAndView lessonPage(HttpServletRequest request) { @@ -81,8 +59,7 @@ public ModelAndView lessonPage(HttpServletRequest request) { .findFirst() .ifPresent( lesson -> { - ws.setCurrentLesson(lesson); - model.addObject("lesson", lesson); + request.setAttribute("lesson", lesson); }); return model; diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java index 3c3c89d6d1..f0e15b1714 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java @@ -51,10 +51,11 @@ public class Assignment { private String name; private String path; + private boolean solved = false; @Transient private List hints; - private Assignment() { + protected Assignment() { // Hibernate } @@ -74,4 +75,8 @@ public Assignment(String name, String path, List hints) { this.path = path; this.hints = hints; } + + public void solved() { + this.solved = true; + } } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Category.java b/src/main/java/org/owasp/webgoat/container/lessons/Category.java index 9fd8317da0..eca864d713 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/Category.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Category.java @@ -34,30 +34,28 @@ * @since October 28, 2003 */ public enum Category { - INTRODUCTION("Introduction", 5), - GENERAL("General", 100), + INTRODUCTION("Introduction"), + GENERAL("General"), - A1("(A1) Broken Access Control", 301), - A2("(A2) Cryptographic Failures", 302), - A3("(A3) Injection", 303), + A1("(A1) Broken Access Control"), + A2("(A2) Cryptographic Failures"), + A3("(A3) Injection"), - A5("(A5) Security Misconfiguration", 305), - A6("(A6) Vuln & Outdated Components", 306), - A7("(A7) Identity & Auth Failure", 307), - A8("(A8) Software & Data Integrity", 308), - A9("(A9) Security Logging Failures", 309), - A10("(A10) Server-side Request Forgery", 310), + A5("(A5) Security Misconfiguration"), + A6("(A6) Vuln & Outdated Components"), + A7("(A7) Identity & Auth Failure"), + A8("(A8) Software & Data Integrity"), + A9("(A9) Security Logging Failures"), + A10("(A10) Server-side Request Forgery"), - CLIENT_SIDE("Client side", 1700), + CLIENT_SIDE("Client side"), - CHALLENGE("Challenges", 3000); + CHALLENGE("Challenges"); @Getter private String name; - @Getter private Integer ranking; - Category(String name, Integer ranking) { + Category(String name) { this.name = name; - this.ranking = ranking; } @Override diff --git a/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java b/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java index c6be7cfad4..9b82e5437c 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/CourseConfiguration.java @@ -22,58 +22,107 @@ package org.owasp.webgoat.container.lessons; -import static java.util.stream.Collectors.groupingBy; - import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.*; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; 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.Configuration; -import org.springframework.util.CollectionUtils; +import org.springframework.util.Assert; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; -@Slf4j @Configuration public class CourseConfiguration { - private final List lessons; private final List assignments; - private final Map> assignmentsByPackage; + private final String contextPath; - public CourseConfiguration(List lessons, List assignments) { + public CourseConfiguration( + List lessons, + List assignments, + @Value("${server.servlet.context-path}") String contextPath) { this.lessons = lessons; this.assignments = assignments; - assignmentsByPackage = - this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName())); + this.contextPath = contextPath.equals("/") ? "" : contextPath; + } + + private void attachToLessonInParentPackage( + AssignmentEndpoint assignmentEndpoint, String packageName) { + if (packageName.equals("org.owasp.webgoat.lessons")) { + throw new IllegalStateException( + "No lesson found for assignment: '%s'" + .formatted(assignmentEndpoint.getClass().getSimpleName())); + } + lessons.stream() + .filter(l -> l.getClass().getPackageName().equals(packageName)) + .findFirst() + .ifPresentOrElse( + l -> l.addAssignment(toAssignment(assignmentEndpoint)), + () -> + attachToLessonInParentPackage( + assignmentEndpoint, packageName.substring(0, packageName.lastIndexOf(".")))); + } + + /** + * For each assignment endpoint, find the lesson in the same package or if not found, find the + * lesson in the parent package + */ + private void attachToLesson(AssignmentEndpoint assignmentEndpoint) { + lessons.stream() + .filter( + l -> + l.getClass() + .getPackageName() + .equals(assignmentEndpoint.getClass().getPackageName())) + .findFirst() + .ifPresentOrElse( + l -> l.addAssignment(toAssignment(assignmentEndpoint)), + () -> { + var assignmentPackageName = assignmentEndpoint.getClass().getPackageName(); + attachToLessonInParentPackage( + assignmentEndpoint, + assignmentPackageName.substring(0, assignmentPackageName.lastIndexOf("."))); + }); + } + + private Assignment toAssignment(AssignmentEndpoint endpoint) { + return new Assignment( + endpoint.getClass().getSimpleName(), + getPath(endpoint.getClass()), + getHints(endpoint.getClass())); } @Bean public Course course() { - lessons.stream().forEach(l -> l.setAssignments(createAssignment(l))); + assignments.stream().forEach(this::attachToLesson); + + // Check if all assignments are attached to a lesson + var assignmentsAttachedToLessons = + lessons.stream().mapToInt(l -> l.getAssignments().size()).sum(); + Assert.isTrue( + assignmentsAttachedToLessons == assignments.size(), + "Not all assignments are attached to a lesson, please check the configuration. The" + + " following assignments are not attached to any lesson: " + + findDiff()); return new Course(lessons); } - private List createAssignment(Lesson lesson) { - var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName()); - if (CollectionUtils.isEmpty(endpoints)) { - log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle()); - return new ArrayList<>(); - } - return endpoints.stream() - .map( - e -> - new Assignment( - e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass()))) - .toList(); + private List findDiff() { + var matchedToLessons = + lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList(); + var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList(); + + var diff = new ArrayList<>(allAssignments); + diff.removeAll(matchedToLessons); + return diff; } private String getPath(Class e) { @@ -81,7 +130,7 @@ private String getPath(Class e) { if (methodReturnTypeIsOfTypeAttackResult(m)) { var mapping = getMapping(m); if (mapping != null) { - return mapping; + return contextPath + mapping; } } } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java b/src/main/java/org/owasp/webgoat/container/lessons/Initializable.java similarity index 77% rename from src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java rename to src/main/java/org/owasp/webgoat/container/lessons/Initializable.java index 2a9726b6fb..1b2b4edd76 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/Initializeable.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Initializable.java @@ -6,7 +6,7 @@ * Interface for initialization of a lesson. It is called when a new user is added to WebGoat and * when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson. */ -public interface Initializeable { +public interface Initializable { - void initialize(WebGoatUser webGoatUser); + default void initialize(WebGoatUser webGoatUser) {} } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java b/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java index 18f031c933..4f49e241a7 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Lesson.java @@ -22,6 +22,7 @@ package org.owasp.webgoat.container.lessons; +import java.util.ArrayList; import java.util.List; import lombok.Getter; import lombok.Setter; @@ -30,13 +31,10 @@ @Setter public abstract class Lesson { - private static int count = 1; - private Integer id = null; - private List assignments; + private List assignments = new ArrayList<>(); - /** Constructor for the Lesson object */ - protected Lesson() { - id = ++count; + public void addAssignment(Assignment assignment) { + this.assignments.add(assignment); } /** @@ -44,9 +42,9 @@ protected Lesson() { * * @return a {@link java.lang.String} object. */ - public String getName() { + public LessonName getName() { String className = getClass().getName(); - return className.substring(className.lastIndexOf('.') + 1); + return new LessonName(className.substring(className.lastIndexOf('.') + 1)); } /** @@ -116,6 +114,10 @@ public final String getId() { return this.getClass().getSimpleName(); } + /** + * This is used in Thymeleaf to construct the HTML to load the lesson content from. See + * lesson_content.html + */ public final String getPackage() { var packageName = this.getClass().getPackageName(); // package name is the direct package name below lessons (any subpackage will be removed) diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java index 0ce6b660ac..7648752a7a 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonMenuItemType.java @@ -35,6 +35,5 @@ */ public enum LessonMenuItemType { CATEGORY, - LESSON, - STAGE + LESSON } diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonName.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonName.java new file mode 100644 index 0000000000..e68e849141 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonName.java @@ -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")); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/report/LessonStatistics.java b/src/main/java/org/owasp/webgoat/container/report/LessonStatistics.java new file mode 100644 index 0000000000..055aaaaae9 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/report/LessonStatistics.java @@ -0,0 +1,3 @@ +package org.owasp.webgoat.container.report; + +record LessonStatistics(String name, boolean solved, int numberOfAttempts) {} diff --git a/src/main/java/org/owasp/webgoat/container/report/ReportCardController.java b/src/main/java/org/owasp/webgoat/container/report/ReportCardController.java new file mode 100644 index 0000000000..60970281a0 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/report/ReportCardController.java @@ -0,0 +1,88 @@ +/** + * ************************************************************************************************* + * + *

+ * + *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, + * please see http://www.owasp.org/ + * + *

Copyright (c) 2002 - 2014 Bruce Mayhew + * + *

This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + *

You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *

Getting Source ============== + * + *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository + * for free software projects. + */ +package org.owasp.webgoat.container.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) {} + + private record LessonStatistics(String name, boolean solved, int numberOfAttempts) {} +} diff --git a/src/main/java/org/owasp/webgoat/container/service/HintService.java b/src/main/java/org/owasp/webgoat/container/service/HintService.java index d9ee5be259..d41752d792 100644 --- a/src/main/java/org/owasp/webgoat/container/service/HintService.java +++ b/src/main/java/org/owasp/webgoat/container/service/HintService.java @@ -10,26 +10,24 @@ import java.util.List; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Hint; -import org.owasp.webgoat.container.lessons.Lesson; -import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.session.Course; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * HintService class. - * - * @author rlawson - * @version $Id: $Id - */ @RestController public class HintService { public static final String URL_HINTS_MVC = "/service/hint.mvc"; - private final WebSession webSession; - - public HintService(WebSession webSession) { - this.webSession = webSession; + private final List allHints; + + public HintService(Course course) { + this.allHints = + course.getLessons().stream() + .flatMap(lesson -> lesson.getAssignments().stream()) + .map(this::createHint) + .flatMap(Collection::stream) + .toList(); } /** @@ -40,15 +38,7 @@ public HintService(WebSession webSession) { @GetMapping(path = URL_HINTS_MVC, produces = "application/json") @ResponseBody public List getHints() { - Lesson l = webSession.getCurrentLesson(); - return createAssignmentHints(l); - } - - private List createAssignmentHints(Lesson l) { - if (l != null) { - return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList(); - } - return List.of(); + return allHints; } private List createHint(Assignment a) { diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java b/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java index fcface4168..9161452b94 100644 --- a/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java +++ b/src/main/java/org/owasp/webgoat/container/service/LessonInfoService.java @@ -1,33 +1,24 @@ package org.owasp.webgoat.container.service; -import lombok.AllArgsConstructor; -import org.owasp.webgoat.container.lessons.Lesson; +import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.lessons.LessonInfoModel; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.web.bind.annotation.RequestMapping; +import org.owasp.webgoat.container.lessons.LessonName; +import org.owasp.webgoat.container.session.Course; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * LessonInfoService class. - * - * @author dm - * @version $Id: $Id - */ @RestController -@AllArgsConstructor +@RequiredArgsConstructor public class LessonInfoService { - private final WebSession webSession; + private final Course course; - /** - * getLessonInfo. - * - * @return a {@link LessonInfoModel} object. - */ - @RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json") - public @ResponseBody LessonInfoModel getLessonInfo() { - Lesson lesson = webSession.getCurrentLesson(); + @GetMapping(path = "/service/lessoninfo.mvc/{lesson}") + public @ResponseBody LessonInfoModel getLessonInfo( + @PathVariable("lesson") LessonName lessonName) { + var lesson = course.getLessonByName(lessonName); return new LessonInfoModel(lesson.getTitle(), false, false, false); } } diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java b/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java index 961e10d472..ac1902611f 100644 --- a/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java +++ b/src/main/java/org/owasp/webgoat/container/service/LessonMenuService.java @@ -30,18 +30,16 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; 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.Lesson; import org.owasp.webgoat.container.lessons.LessonMenuItem; import org.owasp.webgoat.container.lessons.LessonMenuItemType; import org.owasp.webgoat.container.session.Course; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.LessonTracker; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.owasp.webgoat.container.users.LessonProgress; +import org.owasp.webgoat.container.users.UserProgress; +import org.owasp.webgoat.container.users.UserProgressRepository; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -59,8 +57,7 @@ public class LessonMenuService { public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc"; private final Course course; - private final WebSession webSession; - private UserTrackerRepository userTrackerRepository; + private UserProgressRepository userTrackerRepository; @Value("#{'${exclude.categories}'.split(',')}") private List excludeCategories; @@ -74,10 +71,13 @@ public class LessonMenuService { * @return a {@link java.util.List} object. */ @RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json") - public @ResponseBody List showLeftNav() { + public @ResponseBody List 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 menu = new ArrayList<>(); List categories = course.getCategories(); - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + UserProgress userTracker = userTrackerRepository.findByUser(username); for (Category category : categories) { if (excludeCategories.contains(category.name())) { @@ -97,28 +97,14 @@ public class LessonMenuService { lessonItem.setName(lesson.getTitle()); lessonItem.setLink(lesson.getLink()); lessonItem.setType(LessonMenuItemType.LESSON); - LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); - boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson); + LessonProgress lessonTracker = userTracker.getLessonProgress(lesson); + boolean lessonSolved = lessonTracker.isLessonSolved(); lessonItem.setComplete(lessonSolved); categoryItem.addChild(lessonItem); } - categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking()); + categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking)); menu.add(categoryItem); } return menu; } - - private boolean lessonCompleted(Map map, Lesson currentLesson) { - boolean result = true; - for (Map.Entry 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; - } } diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java b/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java index 23fc38da50..1c279de49c 100644 --- a/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java +++ b/src/main/java/org/owasp/webgoat/container/service/LessonProgressService.java @@ -4,11 +4,15 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.lessons.Assignment; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.owasp.webgoat.container.lessons.LessonName; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.users.UserProgressRepository; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; /** @@ -20,8 +24,8 @@ @RequiredArgsConstructor public class LessonProgressService { - private final UserTrackerRepository userTrackerRepository; - private final WebSession webSession; + private final UserProgressRepository userProgressRepository; + private final Course course; /** * Endpoint for fetching the complete lesson overview which informs the user about whether all the @@ -29,19 +33,19 @@ public class LessonProgressService { * * @return list of assignments */ - @RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json") + @GetMapping(value = "/service/lessonoverview.mvc/{lesson}") @ResponseBody - public List lessonOverview() { - var userTracker = userTrackerRepository.findByUser(webSession.getUserName()); - var currentLesson = webSession.getCurrentLesson(); - - if (currentLesson != null) { - var lessonTracker = userTracker.getLessonTracker(currentLesson); - return lessonTracker.getLessonOverview().entrySet().stream() - .map(entry -> new LessonOverview(entry.getKey(), entry.getValue())) - .toList(); - } - return List.of(); + public List lessonOverview( + @PathVariable("lesson") LessonName lessonName, @CurrentUsername String username) { + var userProgress = userProgressRepository.findByUser(username); + var lesson = course.getLessonByName(lessonName); + + Assert.isTrue(lesson != null, "Lesson not found: " + lessonName); + + var lessonProgress = userProgress.getLessonProgress(lesson); + return lessonProgress.getLessonOverview().entrySet().stream() + .map(entry -> new LessonOverview(entry.getKey(), entry.getValue())) + .toList(); } @AllArgsConstructor diff --git a/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java b/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java deleted file mode 100644 index d1c9028801..0000000000 --- a/src/main/java/org/owasp/webgoat/container/service/LessonTitleService.java +++ /dev/null @@ -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() : ""; - } -} diff --git a/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java b/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java deleted file mode 100644 index a01bce5c11..0000000000 --- a/src/main/java/org/owasp/webgoat/container/service/ReportCardService.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * ************************************************************************************************* - * - *

- * - *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, - * please see http://www.owasp.org/ - * - *

Copyright (c) 2002 - 2014 Bruce Mayhew - * - *

This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - *

You should have received a copy of the GNU General Public License along with this program; if - * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - *

Getting Source ============== - * - *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository - * for free software projects. - */ -package org.owasp.webgoat.container.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 = new ArrayList<>(); - } - - @Setter - @Getter - private final class LessonStatistics { - private String name; - private boolean solved; - private int numberOfAttempts; - } -} diff --git a/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java b/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java index 2f0450d9eb..3d2a67875c 100644 --- a/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java +++ b/src/main/java/org/owasp/webgoat/container/service/RestartLessonService.java @@ -29,14 +29,17 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flywaydb.core.Flyway; -import org.owasp.webgoat.container.lessons.Initializeable; -import org.owasp.webgoat.container.lessons.Lesson; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.owasp.webgoat.container.CurrentUser; +import org.owasp.webgoat.container.lessons.Initializable; +import org.owasp.webgoat.container.lessons.LessonName; +import org.owasp.webgoat.container.session.Course; +import org.owasp.webgoat.container.users.UserProgress; +import org.owasp.webgoat.container.users.UserProgressRepository; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseStatus; @Controller @@ -44,25 +47,25 @@ @Slf4j public class RestartLessonService { - private final WebSession webSession; - private final UserTrackerRepository userTrackerRepository; + private final Course course; + private final UserProgressRepository userTrackerRepository; private final Function flywayLessons; - private final List lessonsToInitialize; + private final List lessonsToInitialize; - @RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text") + @GetMapping(path = "/service/restartlesson.mvc/{lesson}") @ResponseStatus(value = HttpStatus.OK) - public void restartLesson() { - Lesson al = webSession.getCurrentLesson(); - log.debug("Restarting lesson: " + al); + public void restartLesson( + @PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) { + var lesson = course.getLessonByName(lessonName); - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); - userTracker.reset(al); + UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername()); + userTracker.reset(lesson); userTrackerRepository.save(userTracker); - var flyway = flywayLessons.apply(webSession.getUserName()); + var flyway = flywayLessons.apply(user.getUsername()); flyway.clean(); flyway.migrate(); - lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser())); + lessonsToInitialize.forEach(i -> i.initialize(user)); } } diff --git a/src/main/java/org/owasp/webgoat/container/service/SessionService.java b/src/main/java/org/owasp/webgoat/container/service/SessionService.java index b1a14d2c2c..0217d06a12 100644 --- a/src/main/java/org/owasp/webgoat/container/service/SessionService.java +++ b/src/main/java/org/owasp/webgoat/container/service/SessionService.java @@ -7,8 +7,9 @@ package org.owasp.webgoat.container.service; import lombok.RequiredArgsConstructor; +import org.owasp.webgoat.container.CurrentUser; import org.owasp.webgoat.container.i18n.Messages; -import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -17,17 +18,17 @@ @RequiredArgsConstructor public class SessionService { - private final WebSession webSession; private final RestartLessonService restartLessonService; private final Messages messages; @RequestMapping(path = "/service/enable-security.mvc", produces = "application/json") @ResponseBody - public String applySecurity() { - webSession.toggleSecurity(); - restartLessonService.restartLesson(); + public String applySecurity(@CurrentUser WebGoatUser user) { + // webSession.toggleSecurity(); + // restartLessonService.restartLesson(user); - var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; - return messages.getMessage(msg); + // TODO disabled for now + // var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; + return messages.getMessage("Not working..."); } } diff --git a/src/main/java/org/owasp/webgoat/container/session/Course.java b/src/main/java/org/owasp/webgoat/container/session/Course.java index 225af40530..48908fa45d 100644 --- a/src/main/java/org/owasp/webgoat/container/session/Course.java +++ b/src/main/java/org/owasp/webgoat/container/session/Course.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; +import org.owasp.webgoat.container.lessons.LessonName; /** * ************************************************************************************************ @@ -96,4 +97,21 @@ public int getTotalOfAssignments() { return this.lessons.stream() .reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum); } + + public Lesson getLessonByName(LessonName lessonName) { + return lessons.stream() + .filter(lesson -> lesson.getName().equals(lessonName)) + .findFirst() + .orElse(null); + } + + public Lesson getLessonByAssignment(String assignmentName) { + return lessons.stream() + .filter( + lesson -> + lesson.getAssignments().stream() + .anyMatch(assignment -> assignment.getName().equals(assignmentName))) + .findFirst() + .orElse(null); + } } diff --git a/src/main/java/org/owasp/webgoat/container/session/LessonSession.java b/src/main/java/org/owasp/webgoat/container/session/LessonSession.java new file mode 100644 index 0000000000..70f844695b --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/session/LessonSession.java @@ -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 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); + } + } +} diff --git a/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java b/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java deleted file mode 100644 index be55c3971e..0000000000 --- a/src/main/java/org/owasp/webgoat/container/session/UserSessionData.java +++ /dev/null @@ -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 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); - } - } -} diff --git a/src/main/java/org/owasp/webgoat/container/session/WebSession.java b/src/main/java/org/owasp/webgoat/container/session/WebSession.java deleted file mode 100644 index 8650ba54ba..0000000000 --- a/src/main/java/org/owasp/webgoat/container/session/WebSession.java +++ /dev/null @@ -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; - -/** - * ************************************************************************************************* - * - *

- * - *

This file is part of WebGoat, an Open Web Application Security Project utility. For details, - * please see http://www.owasp.org/ - * - *

Copyright (c) 2002 - 2014 Bruce Mayhew - * - *

This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - *

You should have received a copy of the GNU General Public License along with this program; if - * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - *

Getting Source ============== - * - *

Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository - * for free software projects. - * - * @author Jeff Williams Aspect Security - * @author Bruce Mayhew WebGoat - * @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 currentScreen. - * - * @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; - } -} diff --git a/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java b/src/main/java/org/owasp/webgoat/container/users/LessonProgress.java similarity index 75% rename from src/main/java/org/owasp/webgoat/container/users/LessonTracker.java rename to src/main/java/org/owasp/webgoat/container/users/LessonProgress.java index fd9af4dcf5..6a0914f85a 100644 --- a/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java +++ b/src/main/java/org/owasp/webgoat/container/users/LessonProgress.java @@ -52,7 +52,7 @@ */ @Entity @EqualsAndHashCode -public class LessonTracker { +public class LessonProgress { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -61,25 +61,22 @@ public class LessonTracker { @Getter private String lessonName; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - private final Set solvedAssignments = new HashSet<>(); - - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - private final Set allAssignments = new HashSet<>(); + private final Set assignments = new HashSet<>(); @Getter private int numberOfAttempts = 0; @Version private Integer version; - private LessonTracker() { + protected LessonProgress() { // JPA } - public LessonTracker(Lesson lesson) { + public LessonProgress(Lesson lesson) { lessonName = lesson.getId(); - allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments()); + assignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments()); } public Optional 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 Optional getAssignment(String name) { * @param solvedAssignment the assignment which the user solved */ public void assignmentSolved(String solvedAssignment) { - getAssignment(solvedAssignment).ifPresent(solvedAssignments::add); + getAssignment(solvedAssignment).ifPresent(Assignment::solved); } /** * @return did they user solved all solvedAssignments for the lesson? */ public boolean isLessonSolved() { - return allAssignments.size() == solvedAssignments.size(); + return assignments.stream().allMatch(Assignment::isSolved); } /** Increase the number attempts to solve the lesson */ @@ -105,18 +102,17 @@ public void incrementAttempts() { /** Reset the tracker. We do not reset the number of attempts here! */ void reset() { - solvedAssignments.clear(); + assignments.clear(); } /** * @return list containing all the assignments solved or not */ public Map getLessonOverview() { - List notSolved = - allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList(); - Map overview = - notSolved.stream().collect(Collectors.toMap(a -> a, b -> false)); - overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true))); - return overview; + return assignments.stream().collect(Collectors.toMap(a -> a, Assignment::isSolved)); + } + + long numberOfSolvedAssignments() { + return assignments.size(); } } diff --git a/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java index 3d94b056b1..9a2a0d077a 100644 --- a/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java +++ b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java @@ -21,7 +21,7 @@ @AllArgsConstructor public class Scoreboard { - private final UserTrackerRepository userTrackerRepository; + private final UserProgressRepository userTrackerRepository; private final UserRepository userRepository; private final Course course; private final PluginMessages pluginMessages; @@ -46,7 +46,7 @@ public List getRankings() { .collect(Collectors.toList()); } - private List challengesSolved(UserTracker userTracker) { + private List challengesSolved(UserProgress userTracker) { List challenges = List.of( "Challenge1", @@ -59,10 +59,10 @@ private List challengesSolved(UserTracker userTracker) { "Challenge8", "Challenge9"); return challenges.stream() - .map(userTracker::getLessonTracker) + .map(userTracker::getLessonProgress) .flatMap(Optional::stream) - .filter(LessonTracker::isLessonSolved) - .map(LessonTracker::getLessonName) + .filter(LessonProgress::isLessonSolved) + .map(LessonProgress::getLessonName) .map(this::toLessonTitle) .toList(); } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserTracker.java b/src/main/java/org/owasp/webgoat/container/users/UserProgress.java similarity index 61% rename from src/main/java/org/owasp/webgoat/container/users/UserTracker.java rename to src/main/java/org/owasp/webgoat/container/users/UserProgress.java index 72450f69e8..f81d6d0425 100644 --- a/src/main/java/org/owasp/webgoat/container/users/UserTracker.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserProgress.java @@ -9,13 +9,10 @@ import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import java.util.HashSet; -import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Lesson; /** @@ -52,7 +49,7 @@ @Slf4j @Entity @EqualsAndHashCode -public class UserTracker { +public class UserProgress { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -62,11 +59,11 @@ public class UserTracker { private String user; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - private Set lessonTrackers = new HashSet<>(); + private Set lessonProgress = new HashSet<>(); - private UserTracker() {} + protected UserProgress() {} - public UserTracker(final String user) { + public UserProgress(final String user) { this.user = user; } @@ -76,15 +73,15 @@ public UserTracker(final String user) { * @param lesson the lesson * @return a lesson tracker created if not already present */ - public LessonTracker getLessonTracker(Lesson lesson) { - Optional lessonTracker = - lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); - if (!lessonTracker.isPresent()) { - LessonTracker newLessonTracker = new LessonTracker(lesson); - lessonTrackers.add(newLessonTracker); + public LessonProgress getLessonProgress(Lesson lesson) { + Optional progress = + lessonProgress.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); + if (!progress.isPresent()) { + LessonProgress newLessonTracker = new LessonProgress(lesson); + lessonProgress.add(newLessonTracker); return newLessonTracker; } else { - return lessonTracker.get(); + return progress.get(); } } @@ -94,43 +91,34 @@ public LessonTracker getLessonTracker(Lesson 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 */ - public Optional getLessonTracker(String id) { - return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); + public Optional getLessonProgress(String id) { + return lessonProgress.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); } public void assignmentSolved(Lesson lesson, String assignmentName) { - LessonTracker lessonTracker = getLessonTracker(lesson); - lessonTracker.incrementAttempts(); - lessonTracker.assignmentSolved(assignmentName); + LessonProgress progress = getLessonProgress(lesson); + progress.incrementAttempts(); + progress.assignmentSolved(assignmentName); } public void assignmentFailed(Lesson lesson) { - LessonTracker lessonTracker = getLessonTracker(lesson); - lessonTracker.incrementAttempts(); + LessonProgress progress = getLessonProgress(lesson); + progress.incrementAttempts(); } public void reset(Lesson al) { - LessonTracker lessonTracker = getLessonTracker(al); - lessonTracker.reset(); + LessonProgress progress = getLessonProgress(al); + progress.reset(); } - public int numberOfLessonsSolved() { - int numberOfLessonsSolved = 0; - for (LessonTracker lessonTracker : lessonTrackers) { - if (lessonTracker.isLessonSolved()) { - numberOfLessonsSolved = numberOfLessonsSolved + 1; - } - } - return numberOfLessonsSolved; + public long numberOfLessonsSolved() { + return lessonProgress.stream().filter(LessonProgress::isLessonSolved).count(); } - public int numberOfAssignmentsSolved() { - int numberOfAssignmentsSolved = 0; - for (LessonTracker lessonTracker : lessonTrackers) { - Map lessonOverview = lessonTracker.getLessonOverview(); - numberOfAssignmentsSolved = - lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue(); - } - return numberOfAssignmentsSolved; + public long numberOfAssignmentsSolved() { + return lessonProgress.stream() + .map(LessonProgress::numberOfSolvedAssignments) + .mapToLong(Long::valueOf) + .sum(); } } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserProgressRepository.java b/src/main/java/org/owasp/webgoat/container/users/UserProgressRepository.java new file mode 100644 index 0000000000..a6b9b7a44b --- /dev/null +++ b/src/main/java/org/owasp/webgoat/container/users/UserProgressRepository.java @@ -0,0 +1,9 @@ +package org.owasp.webgoat.container.users; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserProgressRepository extends JpaRepository { + + // TODO: make optional + UserProgress findByUser(String user); +} diff --git a/src/main/java/org/owasp/webgoat/container/users/UserService.java b/src/main/java/org/owasp/webgoat/container/users/UserService.java index e12668f002..af36a396fa 100644 --- a/src/main/java/org/owasp/webgoat/container/users/UserService.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserService.java @@ -4,7 +4,7 @@ import java.util.function.Function; import lombok.AllArgsConstructor; import org.flywaydb.core.Flyway; -import org.owasp.webgoat.container.lessons.Initializeable; +import org.owasp.webgoat.container.lessons.Initializable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -19,10 +19,10 @@ public class UserService implements UserDetailsService { private final UserRepository userRepository; - private final UserTrackerRepository userTrackerRepository; + private final UserProgressRepository userTrackerRepository; private final JdbcTemplate jdbcTemplate; private final Function flywayLessons; - private final List lessonInitializables; + private final List lessonInitializables; @Override public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException { @@ -43,7 +43,7 @@ public void addUser(String username, String password) { if (!userAlreadyExists) { userTrackerRepository.save( - new UserTracker(username)); // if user previously existed it will not get another tracker + new UserProgress(username)); // if user previously existed it will not get another tracker createLessonsForUser(webGoatUser); } } diff --git a/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java b/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java deleted file mode 100644 index 154360c3e6..0000000000 --- a/src/main/java/org/owasp/webgoat/container/users/UserTrackerRepository.java +++ /dev/null @@ -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 findByUser(String user); -} diff --git a/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java index 761d40aa0d..41e6e4e0c3 100644 --- a/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java +++ b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java @@ -22,6 +22,9 @@ 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.http.HttpServletRequest; import java.io.IOException; @@ -32,9 +35,7 @@ import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -48,11 +49,13 @@ "auth-bypass.hints.verify.3", "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( path = "/auth-bypass/verify-account", diff --git a/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java index 2ea8db965f..9f28f2305b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java +++ b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFieldRestrictions.java @@ -22,6 +22,9 @@ 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.AttackResult; import org.springframework.web.bind.annotation.PostMapping; @@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint { +public class BypassRestrictionsFieldRestrictions implements AssignmentEndpoint { @PostMapping("/BypassRestrictions/FieldRestrictions") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java index 9d2c048ebd..71f2002280 100644 --- a/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java +++ b/src/main/java/org/owasp/webgoat/lessons/bypassrestrictions/BypassRestrictionsFrontendValidation.java @@ -22,6 +22,9 @@ 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.AttackResult; import org.springframework.web.bind.annotation.PostMapping; @@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint { +public class BypassRestrictionsFrontendValidation implements AssignmentEndpoint { @PostMapping("/BypassRestrictions/frontendValidation") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java b/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java index 1c6ba4c374..88d05e1ace 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/ChallengeIntro.java @@ -2,11 +2,13 @@ import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; +import org.springframework.stereotype.Component; /** * @author nbaars * @since 3/21/17. */ +@Component public class ChallengeIntro extends Lesson { @Override diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java index 5e423cecd1..81bb924d68 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java @@ -22,27 +22,30 @@ 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.AttackResult; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -@AllArgsConstructor -public class FlagController extends AssignmentEndpoint { +public class FlagController implements AssignmentEndpoint { - private final WebSession webSession; 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 - public AttackResult postFlag(@RequestParam String flag) { - Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson()); + public AttackResult postFlag(@PathVariable int flagNumber, @RequestParam String flag) { + var expectedFlag = flags.getFlag(flagNumber); if (expectedFlag.isCorrect(flag)) { return success(this).feedback("challenge.flag.correct").build(); } else { diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java b/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java index d3b92b1493..ec63a7e98f 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java @@ -4,7 +4,6 @@ import java.util.Map; import java.util.UUID; import java.util.stream.IntStream; -import org.owasp.webgoat.container.lessons.Lesson; import org.springframework.context.annotation.Configuration; @Configuration @@ -15,12 +14,6 @@ public Flags() { IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString()))); } - public Flag getFlag(Lesson forLesson) { - String lessonName = forLesson.getName(); - int challengeNumber = Integer.valueOf(lessonName.substring(lessonName.length() - 1)); - return FLAGS.get(challengeNumber); - } - public Flag getFlag(int flagNumber) { return FLAGS.get(flagNumber); } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java index de99c44703..0b79c0b162 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java @@ -1,8 +1,9 @@ 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 lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Flags; @@ -42,11 +43,14 @@ * @since August 11, 2016 */ @RestController -@RequiredArgsConstructor -public class Assignment1 extends AssignmentEndpoint { +public class Assignment1 implements AssignmentEndpoint { private final Flags flags; + public Assignment1(Flags flags) { + this.flags = flags; + } + @PostMapping("/challenge/1") @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java index c8b3f3d10e..db52392bf0 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java @@ -22,6 +22,9 @@ 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.ResultSet; import lombok.RequiredArgsConstructor; @@ -39,7 +42,7 @@ @RestController @Slf4j @RequiredArgsConstructor -public class Assignment5 extends AssignmentEndpoint { +public class Assignment5 implements AssignmentEndpoint { private final LessonDataSource dataSource; private final Flags flags; diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java index a641bff281..fab9d74825 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java @@ -1,5 +1,7 @@ package org.owasp.webgoat.lessons.challenges.challenge7; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.net.URI; import java.net.URISyntaxException; @@ -29,7 +31,7 @@ */ @RestController @Slf4j -public class Assignment7 extends AssignmentEndpoint { +public class Assignment7 implements AssignmentEndpoint { public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java index 6623ea1a0a..0bf9edeb98 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java @@ -19,7 +19,7 @@ @RestController @Slf4j @RequiredArgsConstructor -public class Assignment8 extends AssignmentEndpoint { +public class Assignment8 implements AssignmentEndpoint { private static final Map votes = new HashMap<>(); diff --git a/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java index 97677e9a9f..cff6ab6474 100644 --- a/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java +++ b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkDummy.java @@ -22,28 +22,36 @@ 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.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** - * This is just a class used to make the the HTTP request. + * This is just a class used to make the HTTP request. * * @author TMelzer * @since 30.11.18 */ @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") @ResponseBody public AttackResult completed(@RequestParam String successMessage) { - UserSessionData userSessionData = getUserSessionData(); - String answer = (String) userSessionData.getValue("randValue"); + String answer = (String) lessonSession.getValue("randValue"); if (successMessage != null && successMessage.equals(answer)) { return success(this).feedback("xss-dom-message-success").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java index 7441ab4a56..106c03cebb 100644 --- a/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java +++ b/src/main/java/org/owasp/webgoat/lessons/chromedevtools/NetworkLesson.java @@ -22,6 +22,9 @@ 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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -40,7 +43,7 @@ */ @RestController @AssignmentHints({"networkHint1", "networkHint2"}) -public class NetworkLesson extends AssignmentEndpoint { +public class NetworkLesson implements AssignmentEndpoint { @PostMapping( value = "/ChromeDevTools/network", diff --git a/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java b/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java index fa01b43e5e..35d462d242 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/cia/CIAQuiz.java @@ -1,5 +1,8 @@ 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.AttackResult; import org.springframework.web.bind.annotation.GetMapping; @@ -9,9 +12,9 @@ import org.springframework.web.bind.annotation.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]; @PostMapping("/cia/quiz") diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java index fbe11da933..6dcb154c81 100644 --- a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.clientsidefiltering; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -37,7 +40,7 @@ "ClientSideFilteringHint3", "ClientSideFilteringHint4" }) -public class ClientSideFilteringAssignment extends AssignmentEndpoint { +public class ClientSideFilteringAssignment implements AssignmentEndpoint { @PostMapping("/clientSideFiltering/attack1") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java index 9db150279b..bc4a66ca16 100644 --- a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/ClientSideFilteringFreeAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.clientsidefiltering; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -40,8 +43,7 @@ "client.side.filtering.free.hint2", "client.side.filtering.free.hint3" }) -public class ClientSideFilteringFreeAssignment extends AssignmentEndpoint { - +public class ClientSideFilteringFreeAssignment implements AssignmentEndpoint { public static final String SUPER_COUPON_CODE = "get_it_for_free"; @PostMapping("/clientSideFiltering/getItForFree") diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java index 437e899593..4df2b97e26 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.util.Base64; import java.util.Random; @@ -35,7 +38,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class EncodingAssignment extends AssignmentEndpoint { +public class EncodingAssignment implements AssignmentEndpoint { public static String getBasicAuth(String username, String password) { return Base64.getEncoder().encodeToString(username.concat(":").concat(password).getBytes()); diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java index 266c53ffab..19f00b7483 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -39,8 +42,7 @@ @RestController @AssignmentHints({"crypto-hashing.hints.1", "crypto-hashing.hints.2"}) -public class HashingAssignment extends AssignmentEndpoint { - +public class HashingAssignment implements AssignmentEndpoint { public static final String[] SECRETS = {"secret", "admin", "password", "123456", "passw0rd"}; @RequestMapping(path = "/crypto/hashing/md5", produces = MediaType.TEXT_HTML_VALUE) diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java index bb28f4202d..d30708bbcc 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/SecureDefaultsAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.security.NoSuchAlgorithmException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; @@ -37,7 +40,7 @@ "crypto-secure-defaults.hints.2", "crypto-secure-defaults.hints.3" }) -public class SecureDefaultsAssignment extends AssignmentEndpoint { +public class SecureDefaultsAssignment implements AssignmentEndpoint { @PostMapping("/crypto/secure/defaults") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java index ffcb739a5b..7a27cae617 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; @@ -47,7 +50,7 @@ "crypto-signing.hints.4" }) @Slf4j -public class SigningAssignment extends AssignmentEndpoint { +public class SigningAssignment implements AssignmentEndpoint { @RequestMapping(path = "/crypto/signing/getprivate", produces = MediaType.TEXT_HTML_VALUE) @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java index d7e3ed94da..a2807b4e69 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/XOREncodingAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -32,7 +35,7 @@ @RestController @AssignmentHints({"crypto-encoding-xor.hints.1"}) -public class XOREncodingAssignment extends AssignmentEndpoint { +public class XOREncodingAssignment implements AssignmentEndpoint { @PostMapping("/crypto/encoding/xor") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java index 73fa55bdad..ca36da3e5c 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRF.java @@ -31,7 +31,7 @@ public class CSRF extends Lesson { @Override public Category getDefaultCategory() { - return Category.A10; + return Category.A5; } @Override diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java index e4f52eb09b..f46c238628 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFConfirmFlag1.java @@ -22,11 +22,13 @@ package org.owasp.webgoat.lessons.csrf; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -34,9 +36,13 @@ /** Created by jason on 9/29/17. */ @RestController @AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"}) -public class CSRFConfirmFlag1 extends AssignmentEndpoint { +public class CSRFConfirmFlag1 implements AssignmentEndpoint { + + private final LessonSession userSessionData; - @Autowired UserSessionData userSessionData; + public CSRFConfirmFlag1(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @PostMapping( path = "/csrf/confirm-flag-1", diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java index 4f4beb91ab..2154ed34da 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.csrf; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.Cookie; @@ -33,8 +36,7 @@ import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -42,16 +44,17 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 11/17/17. - */ @RestController @AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"}) -public class CSRFFeedback extends AssignmentEndpoint { +public class CSRFFeedback implements AssignmentEndpoint { - @Autowired private UserSessionData userSessionData; - @Autowired private ObjectMapper objectMapper; + private final LessonSession userSessionData; + private final ObjectMapper objectMapper; + + public CSRFFeedback(LessonSession userSessionData, ObjectMapper objectMapper) { + this.userSessionData = userSessionData; + this.objectMapper = objectMapper; + } @PostMapping( value = "/csrf/feedback/message", diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java index 2a929817ba..a0e3f5609b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java @@ -27,10 +27,9 @@ import java.util.Map; import java.util.Random; import org.owasp.webgoat.container.i18n.PluginMessages; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -38,13 +37,12 @@ @RestController public class CSRFGetFlag { - @Autowired UserSessionData userSessionData; + @Autowired LessonSession userSessionData; @Autowired private PluginMessages pluginMessages; - @RequestMapping( + @PostMapping( path = "/csrf/basic-get-flag", - produces = {"application/json"}, - method = RequestMethod.POST) + produces = {"application/json"}) @ResponseBody public Map invoke(HttpServletRequest req) { diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java index e414094573..78fb16a105 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java @@ -22,47 +22,29 @@ package org.owasp.webgoat.lessons.csrf; -import jakarta.servlet.http.HttpServletRequest; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 11/17/17. - */ @RestController @AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"}) -public class CSRFLogin extends AssignmentEndpoint { - - private final UserTrackerRepository userTrackerRepository; - - public CSRFLogin(UserTrackerRepository userTrackerRepository) { - this.userTrackerRepository = userTrackerRepository; - } +public class CSRFLogin implements AssignmentEndpoint { @PostMapping( path = "/csrf/login", produces = {"application/json"}) @ResponseBody - public AttackResult completed(HttpServletRequest request) { - String userName = request.getUserPrincipal().getName(); - if (userName.startsWith("csrf")) { - markAssignmentSolvedWithRealUser(userName.substring("csrf-".length())); + public AttackResult completed(@CurrentUsername String username) { + if (username.startsWith("csrf")) { return success(this).feedback("csrf-login-success").build(); } - return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build(); - } - - private void markAssignmentSolvedWithRealUser(String username) { - UserTracker userTracker = userTrackerRepository.findByUser(username); - userTracker.assignmentSolved( - getWebSession().getCurrentLesson(), this.getClass().getSimpleName()); - userTrackerRepository.save(userTracker); + return failed(this).feedback("csrf-login-failed").feedbackArgs(username).build(); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java index e82a46cc70..50dcb89158 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.csrf; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.ALL_VALUE; import com.google.common.collect.Lists; @@ -33,11 +35,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -46,9 +47,8 @@ @RestController @AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"}) -public class ForgedReviews extends AssignmentEndpoint { +public class ForgedReviews implements AssignmentEndpoint { - @Autowired private WebSession webSession; private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); private static final Map> userReviews = new HashMap<>(); @@ -73,9 +73,9 @@ public class ForgedReviews extends AssignmentEndpoint { produces = MediaType.APPLICATION_JSON_VALUE, consumes = ALL_VALUE) @ResponseBody - public Collection retrieveReviews() { + public Collection retrieveReviews(@CurrentUsername String username) { Collection allReviews = Lists.newArrayList(); - Collection newReviews = userReviews.get(webSession.getUserName()); + Collection newReviews = userReviews.get(username); if (newReviews != null) { allReviews.addAll(newReviews); } @@ -88,7 +88,11 @@ public Collection retrieveReviews() { @PostMapping("/csrf/review") @ResponseBody public AttackResult createNewReview( - String reviewText, Integer stars, String validateReq, HttpServletRequest request) { + String reviewText, + Integer stars, + String validateReq, + HttpServletRequest request, + @CurrentUsername String username) { final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host"); final String referer = (request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer"); @@ -97,11 +101,11 @@ public AttackResult createNewReview( Review review = new Review(); review.setText(reviewText); review.setDateTime(LocalDateTime.now().format(fmt)); - review.setUser(webSession.getUserName()); + review.setUser(username); review.setStars(stars); - var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>()); + var reviews = userReviews.getOrDefault(username, new ArrayList<>()); reviews.add(review); - userReviews.put(webSession.getUserName(), reviews); + userReviews.put(username, reviews); // short-circuit if (validateReq == null || !validateReq.equals(weakAntiCSRF)) { return failed(this).feedback("csrf-you-forgot-something").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java index d44823fdcf..22dd18a1f8 100644 --- a/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/deserialization/InsecureDeserializationTask.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.deserialization; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InvalidClassException; @@ -42,7 +45,7 @@ "insecure-deserialization.hints.2", "insecure-deserialization.hints.3" }) -public class InsecureDeserializationTask extends AssignmentEndpoint { +public class InsecureDeserializationTask implements AssignmentEndpoint { @PostMapping("/InsecureDeserialization/task") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java index 8fae4e89d4..7817022f05 100644 --- a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.hijacksession; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; @@ -30,7 +33,6 @@ import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.hijacksession.cas.Authentication; import org.owasp.webgoat.lessons.hijacksession.cas.HijackSessionAuthenticationProvider; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -51,11 +53,14 @@ "hijacksession.hints.4", "hijacksession.hints.5" }) -public class HijackSessionAssignment extends AssignmentEndpoint { - +public class HijackSessionAssignment implements AssignmentEndpoint { private static final String COOKIE_NAME = "hijack_cookie"; - @Autowired HijackSessionAuthenticationProvider provider; + private final HijackSessionAuthenticationProvider provider; + + public HijackSessionAssignment(HijackSessionAuthenticationProvider provider) { + this.provider = provider; + } @PostMapping(path = "/HijackSession/login") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java index 8a0ba7103f..2042ea0f62 100644 --- a/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/htmltampering/HtmlTamperingTask.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.htmltampering; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -32,7 +35,7 @@ @RestController @AssignmentHints({"hint1", "hint2", "hint3"}) -public class HtmlTamperingTask extends AssignmentEndpoint { +public class HtmlTamperingTask implements AssignmentEndpoint { @PostMapping("/HtmlTampering/task") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java index 883f14f313..f73e3ae06e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsLesson.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.httpbasics; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -32,7 +35,7 @@ @RestController @AssignmentHints({"http-basics.hints.http_basics_lesson.1"}) -public class HttpBasicsLesson extends AssignmentEndpoint { +public class HttpBasicsLesson implements AssignmentEndpoint { @PostMapping("/HttpBasics/attack1") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java index c6c14ad734..67ff783191 100644 --- a/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpbasics/HttpBasicsQuiz.java @@ -22,9 +22,11 @@ package org.owasp.webgoat.lessons.httpbasics; +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.AssignmentHints; -import org.owasp.webgoat.container.assignments.AssignmentPath; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -33,8 +35,7 @@ @RestController @AssignmentHints({"http-basics.hints.http_basic_quiz.1", "http-basics.hints.http_basic_quiz.2"}) -@AssignmentPath("HttpBasics/attack2") -public class HttpBasicsQuiz extends AssignmentEndpoint { +public class HttpBasicsQuiz implements AssignmentEndpoint { @PostMapping("/HttpBasics/attack2") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java index 7330c747bd..3731dec4eb 100644 --- a/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.httpproxies; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; @@ -34,7 +37,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class HttpBasicsInterceptRequest extends AssignmentEndpoint { +public class HttpBasicsInterceptRequest implements AssignmentEndpoint { @RequestMapping( path = "/HttpProxies/intercept-request", diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java index f910997424..7b641a228a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORDiffAttributes.java @@ -23,6 +23,9 @@ package org.owasp.webgoat.lessons.idor; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -37,7 +40,7 @@ "idor.hints.idorDiffAttributes2", "idor.hints.idorDiffAttributes3" }) -public class IDORDiffAttributes extends AssignmentEndpoint { +public class IDORDiffAttributes implements AssignmentEndpoint { @PostMapping("/IDOR/diff-attributes") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfile.java index 1e5bbd8bbb..e0b9debf8a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfile.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDOREditOtherProfile.java @@ -23,11 +23,13 @@ package org.owasp.webgoat.lessons.idor; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -46,9 +48,13 @@ "idor.hints.otherProfile8", "idor.hints.otherProfile9" }) -public class IDOREditOtherProfile extends AssignmentEndpoint { +public class IDOREditOtherProfile implements AssignmentEndpoint { + + private final LessonSession userSessionData; - @Autowired private UserSessionData userSessionData; + public IDOREditOtherProfile(LessonSession lessonSession) { + this.userSessionData = lessonSession; + } @PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java index 36a161c88f..febfb2f507 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORLogin.java @@ -23,12 +23,15 @@ package org.owasp.webgoat.lessons.idor; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -36,9 +39,14 @@ @RestController @AssignmentHints({"idor.hints.idor_login"}) -public class IDORLogin extends AssignmentEndpoint { +public class IDORLogin implements AssignmentEndpoint { + private final LessonSession lessonSession; + + public IDORLogin(LessonSession lessonSession) { + this.lessonSession = lessonSession; + } - private Map> idorUserInfo = new HashMap<>(); + private final Map> idorUserInfo = new HashMap<>(); public void initIDORInfo() { @@ -59,13 +67,11 @@ public void initIDORInfo() { @ResponseBody public AttackResult completed(@RequestParam String username, @RequestParam String password) { initIDORInfo(); - UserSessionData userSessionData = getUserSessionData(); if (idorUserInfo.containsKey(username)) { if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) { - userSessionData.setValue("idor-authenticated-as", username); - userSessionData.setValue( - "idor-authenticated-user-id", idorUserInfo.get(username).get("id")); + lessonSession.setValue("idor-authenticated-as", username); + lessonSession.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id")); return success(this).feedback("idor.login.success").feedbackArgs(username).build(); } else { return failed(this).feedback("idor.login.failure").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java index aa84614c26..2bd7838070 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java @@ -23,12 +23,13 @@ package org.owasp.webgoat.lessons.idor; -import jakarta.servlet.http.HttpServletResponse; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; @@ -46,15 +47,19 @@ "idor.hints.otherProfile8", "idor.hints.otherProfile9" }) -public class IDORViewOtherProfile extends AssignmentEndpoint { +public class IDORViewOtherProfile implements AssignmentEndpoint { + + private final LessonSession userSessionData; - @Autowired UserSessionData userSessionData; + public IDORViewOtherProfile(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @GetMapping( path = "/IDOR/profile/{userId}", produces = {"application/json"}) @ResponseBody - public AttackResult completed(@PathVariable("userId") String userId, HttpServletResponse resp) { + public AttackResult completed(@PathVariable("userId") String userId) { Object obj = userSessionData.getValue("idor-authenticated-as"); if (obj != null && obj.equals("tom")) { diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java index b58fe69ca2..5897fa8682 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfile.java @@ -26,8 +26,7 @@ import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @@ -36,7 +35,11 @@ @Slf4j public class IDORViewOwnProfile { - @Autowired UserSessionData userSessionData; + private final LessonSession userSessionData; + + public IDORViewOwnProfile(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @GetMapping( path = {"/IDOR/own", "/IDOR/profile"}, @@ -60,7 +63,7 @@ public Map invoke() { "You do not have privileges to view the profile. Authenticate as tom first please."); } } catch (Exception ex) { - log.error("something went wrong", ex.getMessage()); + log.error("something went wrong: {}", ex.getMessage()); } return details; } diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java index c4f99a6b32..970b33932e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOwnProfileAltUrl.java @@ -23,11 +23,13 @@ package org.owasp.webgoat.lessons.idor; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -39,9 +41,12 @@ "idor.hints.ownProfileAltUrl2", "idor.hints.ownProfileAltUrl3" }) -public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint { +public class IDORViewOwnProfileAltUrl implements AssignmentEndpoint { + private final LessonSession userSessionData; - @Autowired UserSessionData userSessionData; + public IDORViewOwnProfileAltUrl(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @PostMapping("/IDOR/profile/alt-path") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java index 8d39a594d7..24f5ac7f67 100644 --- a/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/insecurelogin/InsecureLoginTask.java @@ -22,13 +22,16 @@ package org.owasp.webgoat.lessons.insecurelogin; +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.AttackResult; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @RestController -public class InsecureLoginTask extends AssignmentEndpoint { +public class InsecureLoginTask implements AssignmentEndpoint { @PostMapping("/InsecureLogin/task") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java index 9b27236cb7..75bfd61716 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTDecodeEndpoint.java @@ -1,5 +1,8 @@ package org.owasp.webgoat.lessons.jwt; +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.AttackResult; import org.springframework.web.bind.annotation.PostMapping; @@ -8,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class JWTDecodeEndpoint extends AssignmentEndpoint { +public class JWTDecodeEndpoint implements AssignmentEndpoint { @PostMapping("/JWT/decode") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java index abcd08edd4..d73765cb4f 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTQuiz.java @@ -1,5 +1,8 @@ package org.owasp.webgoat.lessons.jwt; +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.AttackResult; import org.springframework.web.bind.annotation.GetMapping; @@ -9,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class JWTQuiz extends AssignmentEndpoint { +public class JWTQuiz implements AssignmentEndpoint { private final String[] solutions = {"Solution 1", "Solution 2"}; private final boolean[] guesses = new boolean[solutions.length]; diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java index 4efc9db095..03691dee1e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.jwt; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.ResponseEntity.ok; import io.jsonwebtoken.Claims; @@ -56,7 +58,7 @@ "jwt-refresh-hint3", "jwt-refresh-hint4" }) -public class JWTRefreshEndpoint extends AssignmentEndpoint { +public class JWTRefreshEndpoint implements AssignmentEndpoint { public static final String PASSWORD = "bm5nhSkxCXZkKRy4"; private static final String JWT_PASSWORD = "bm5n3SkxCX4kKRy4"; diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java index 0e688c0497..4eb46d6e79 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.jwt; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; @@ -44,7 +47,7 @@ @RestController @AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"}) -public class JWTSecretKeyEndpoint extends AssignmentEndpoint { +public class JWTSecretKeyEndpoint implements AssignmentEndpoint { public static final String[] SECRETS = { "victory", "business", "available", "shipping", "washington" diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java index e1ac1a0d2d..4b4d6486e8 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java @@ -25,6 +25,8 @@ import static java.util.Comparator.comparingLong; import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwt; @@ -66,13 +68,13 @@ "jwt-change-token-hint4", "jwt-change-token-hint5" }) -public class JWTVotesEndpoint extends AssignmentEndpoint { +public class JWTVotesEndpoint implements AssignmentEndpoint { public static final String JWT_PASSWORD = TextCodec.BASE64.encode("victory"); private static String validUsers = "TomJerrySylvester"; private static int totalVotes = 38929; - private Map votes = new HashMap<>(); + private final Map votes = new HashMap<>(); @PostConstruct public void initVotes() { diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderJKUEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderJKUEndpoint.java index 7927e76bcc..4ff2e13cad 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderJKUEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderJKUEndpoint.java @@ -1,7 +1,9 @@ package org.owasp.webgoat.lessons.jwt.claimmisuse; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import com.auth0.jwk.JwkException; -import com.auth0.jwk.JwkProvider; import com.auth0.jwk.JwkProviderBuilder; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; @@ -20,7 +22,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -@RequestMapping("/JWT/jku") +@RequestMapping("/JWT/") @RestController @AssignmentHints({ "jwt-jku-hint1", @@ -29,9 +31,9 @@ "jwt-jku-hint4", "jwt-jku-hint5" }) -public class JWTHeaderJKUEndpoint extends AssignmentEndpoint { +public class JWTHeaderJKUEndpoint implements AssignmentEndpoint { - @PostMapping("/follow/{user}") + @PostMapping("jku/follow/{user}") public @ResponseBody String follow(@PathVariable("user") String user) { if ("Jerry".equals(user)) { return "Following yourself seems redundant"; @@ -40,7 +42,7 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint { } } - @PostMapping("/delete") + @PostMapping("jku/delete") public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { if (StringUtils.isEmpty(token)) { return failed(this).feedback("jwt-invalid-token").build(); @@ -48,12 +50,12 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint { try { var decodedJWT = JWT.decode(token); var jku = decodedJWT.getHeaderClaim("jku"); - JwkProvider jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build(); + var jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build(); var jwk = jwkProvider.get(decodedJWT.getKeyId()); var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey()); JWT.require(algorithm).build().verify(decodedJWT); - String username = decodedJWT.getClaims().get("username").asString(); + var username = decodedJWT.getClaims().get("username").asString(); if ("Jerry".equals(username)) { return failed(this).feedback("jwt-final-jerry-account").build(); } diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderKIDEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderKIDEndpoint.java index 237c0195d5..904f2656f4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderKIDEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/claimmisuse/JWTHeaderKIDEndpoint.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.jwt.claimmisuse; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwt; @@ -52,16 +55,15 @@ "jwt-kid-hint5", "jwt-kid-hint6" }) -@RequestMapping("/JWT/kid") -public class JWTHeaderKIDEndpoint extends AssignmentEndpoint { - +@RequestMapping("/JWT/") +public class JWTHeaderKIDEndpoint implements AssignmentEndpoint { private final LessonDataSource dataSource; private JWTHeaderKIDEndpoint(LessonDataSource dataSource) { this.dataSource = dataSource; } - @PostMapping("/follow/{user}") + @PostMapping("kid/follow/{user}") public @ResponseBody String follow(@PathVariable("user") String user) { if ("Jerry".equals(user)) { return "Following yourself seems redundant"; @@ -70,7 +72,7 @@ private JWTHeaderKIDEndpoint(LessonDataSource dataSource) { } } - @PostMapping("/delete") + @PostMapping("kid/delete") public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { if (StringUtils.isEmpty(token)) { return failed(this).feedback("jwt-invalid-token").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java index 22a028490a..d4f0c1b86e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java +++ b/src/main/java/org/owasp/webgoat/lessons/lessontemplate/SampleAttack.java @@ -22,13 +22,15 @@ package org.owasp.webgoat.lessons.lessontemplate; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.util.List; import lombok.AllArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -39,12 +41,14 @@ /** Created by jason on 1/5/17. */ @RestController @AssignmentHints({"lesson-template.hints.1", "lesson-template.hints.2", "lesson-template.hints.3"}) -public class SampleAttack extends AssignmentEndpoint { +public class SampleAttack implements AssignmentEndpoint { + private static final String secretValue = "secr37Value"; - String secretValue = "secr37Value"; + private final LessonSession userSessionData; - // UserSessionData is bound to session and can be used to persist data across multiple assignments - @Autowired UserSessionData userSessionData; + public SampleAttack(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @PostMapping("/lesson-template/sample-attack") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java index a338407bf8..7b1f68937f 100644 --- a/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java @@ -22,7 +22,9 @@ package org.owasp.webgoat.lessons.logging; -import jakarta.annotation.PostConstruct; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; @@ -37,14 +39,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class LogBleedingTask extends AssignmentEndpoint { +public class LogBleedingTask implements AssignmentEndpoint { - Logger log = LoggerFactory.getLogger(this.getClass().getName()); - private String password; + private static final Logger log = LoggerFactory.getLogger(LogBleedingTask.class); + private final String password; - @PostConstruct - public void generatePassword() { - password = UUID.randomUUID().toString(); + public LogBleedingTask() { + this.password = UUID.randomUUID().toString(); log.info( "Password for admin: {}", Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8))); diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java index 0fe3b35599..bcce8a57ad 100644 --- a/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogSpoofingTask.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.logging; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import org.apache.logging.log4j.util.Strings; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; @@ -31,7 +34,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class LogSpoofingTask extends AssignmentEndpoint { +public class LogSpoofingTask implements AssignmentEndpoint { @PostMapping("/LogSpoofing/log-spoofing") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java index 8cf11a6fbd..2bbb066874 100644 --- a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACHiddenMenus.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.missingac; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -36,7 +39,7 @@ "access-control.hidden-menus.hint2", "access-control.hidden-menus.hint3" }) -public class MissingFunctionACHiddenMenus extends AssignmentEndpoint { +public class MissingFunctionACHiddenMenus implements AssignmentEndpoint { @PostMapping( path = "/access-control/hidden-menu", diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java index 0bbf9d68d6..f771d45a31 100644 --- a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACUsers.java @@ -30,7 +30,7 @@ import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.CurrentUsername; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -47,7 +47,6 @@ public class MissingFunctionACUsers { private final MissingAccessControlUserRepository userRepository; - private final WebSession webSession; @GetMapping(path = {"access-control/users"}) public ModelAndView listUsers() { @@ -81,8 +80,8 @@ public ResponseEntity> usersService() { path = {"access-control/users-admin-fix"}, consumes = "application/json") @ResponseBody - public ResponseEntity> usersFixed() { - var currentUser = userRepository.findByUsername(webSession.getUserName()); + public ResponseEntity> usersFixed(@CurrentUsername String username) { + var currentUser = userRepository.findByUsername(username); if (currentUser != null && currentUser.isAdmin()) { return ResponseEntity.ok( userRepository.findAllUsers().stream() diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java index 8417ae059b..722e376f91 100644 --- a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHash.java @@ -22,9 +22,10 @@ package org.owasp.webgoat.lessons.missingac; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_SIMPLE; -import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -40,11 +41,14 @@ "access-control.hash.hint4", "access-control.hash.hint5" }) -@RequiredArgsConstructor -public class MissingFunctionACYourHash extends AssignmentEndpoint { +public class MissingFunctionACYourHash implements AssignmentEndpoint { private final MissingAccessControlUserRepository userRepository; + public MissingFunctionACYourHash(MissingAccessControlUserRepository userRepository) { + this.userRepository = userRepository; + } + @PostMapping( path = "/access-control/user-hash", produces = {"application/json"}) diff --git a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java index 8db5c5b7c8..c36442a2a4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java +++ b/src/main/java/org/owasp/webgoat/lessons/missingac/MissingFunctionACYourHashAdmin.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.missingac; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.owasp.webgoat.lessons.missingac.MissingFunctionAC.PASSWORD_SALT_ADMIN; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; @@ -42,7 +44,7 @@ "access-control.hash.hint12", "access-control.hash.hint13" }) -public class MissingFunctionACYourHashAdmin extends AssignmentEndpoint { +public class MissingFunctionACYourHashAdmin implements AssignmentEndpoint { private final MissingAccessControlUserRepository userRepository; diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java index 8568b97ec5..02a9475da9 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/QuestionsAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.passwordreset; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; @@ -37,7 +40,7 @@ * @since 8/20/17. */ @RestController -public class QuestionsAssignment extends AssignmentEndpoint { +public class QuestionsAssignment implements AssignmentEndpoint { private static final Map COLORS = new HashMap<>(); diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java index 4dacce2098..3fe8af5347 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java @@ -22,11 +22,16 @@ package org.owasp.webgoat.lessons.passwordreset; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; +import static org.springframework.util.StringUtils.hasText; + import com.google.common.collect.Maps; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -55,7 +60,7 @@ "password-reset-hint5", "password-reset-hint6" }) -public class ResetLinkAssignment extends AssignmentEndpoint { +public class ResetLinkAssignment implements AssignmentEndpoint { private static final String VIEW_FORMATTER = "lessons/passwordreset/templates/%s.html"; static final String PASSWORD_TOM_9 = @@ -81,10 +86,10 @@ public class ResetLinkAssignment extends AssignmentEndpoint { @PostMapping("/PasswordReset/reset/login") @ResponseBody - public AttackResult login(@RequestParam String password, @RequestParam String email) { + public AttackResult login( + @RequestParam String password, @RequestParam String email, @CurrentUsername String username) { if (TOM_EMAIL.equals(email)) { - String passwordTom = - usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9); + String passwordTom = usersToTomPassword.getOrDefault(username, PASSWORD_TOM_9); if (passwordTom.equals(PASSWORD_TOM_9)) { return failed(this).feedback("login_failed").build(); } else if (passwordTom.equals(password)) { @@ -112,9 +117,11 @@ public ModelAndView resetPassword(@PathVariable(value = "link") String link, Mod @PostMapping("/PasswordReset/reset/change-password") public ModelAndView changePassword( - @ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) { + @ModelAttribute("form") PasswordChangeForm form, + BindingResult bindingResult, + @CurrentUsername String username) { ModelAndView modelAndView = new ModelAndView(); - if (!org.springframework.util.StringUtils.hasText(form.getPassword())) { + if (!hasText(form.getPassword())) { bindingResult.rejectValue("password", "not.empty"); } if (bindingResult.hasErrors()) { @@ -125,15 +132,15 @@ public ModelAndView changePassword( modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); return modelAndView; } - if (checkIfLinkIsFromTom(form.getResetLink())) { - usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); + if (checkIfLinkIsFromTom(form.getResetLink(), username)) { + usersToTomPassword.put(username, form.getPassword()); } modelAndView.setViewName(VIEW_FORMATTER.formatted("success")); return modelAndView; } - private boolean checkIfLinkIsFromTom(String resetLinkFromForm) { - String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown"); + private boolean checkIfLinkIsFromTom(String resetLinkFromForm, String username) { + String resetLink = userToTomResetLink.getOrDefault(username, "unknown"); return resetLink.equals(resetLinkFromForm); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java index 26bdb2e0e6..1a2780467e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java @@ -22,8 +22,12 @@ package org.owasp.webgoat.lessons.passwordreset; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.util.UUID; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; @@ -43,12 +47,12 @@ * @since 8/20/17. */ @RestController -public class ResetLinkAssignmentForgotPassword extends AssignmentEndpoint { +public class ResetLinkAssignmentForgotPassword implements AssignmentEndpoint { private final RestTemplate restTemplate; - private String webWolfHost; - private String webWolfPort; - private String webWolfURL; + private final String webWolfHost; + private final String webWolfPort; + private final String webWolfURL; private final String webWolfMailURL; public ResetLinkAssignmentForgotPassword( @@ -67,14 +71,14 @@ public ResetLinkAssignmentForgotPassword( @PostMapping("/PasswordReset/ForgotPassword/create-password-reset-link") @ResponseBody public AttackResult sendPasswordResetLink( - @RequestParam String email, HttpServletRequest request) { + @RequestParam String email, HttpServletRequest request, @CurrentUsername String username) { String resetLink = UUID.randomUUID().toString(); ResetLinkAssignment.resetLinks.add(resetLink); String host = request.getHeader(HttpHeaders.HOST); if (ResetLinkAssignment.TOM_EMAIL.equals(email) && (host.contains(webWolfPort) && host.contains(webWolfHost))) { // User indeed changed the host header. - ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink); + ResetLinkAssignment.userToTomResetLink.put(username, resetLink); fakeClickingLinkEmail(webWolfURL, resetLink); } else { try { diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java index 0446897174..f08bc78901 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SecurityQuestionAssignment.java @@ -23,12 +23,13 @@ package org.owasp.webgoat.lessons.passwordreset; import static java.util.Optional.of; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.util.HashMap; import java.util.Map; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -41,9 +42,9 @@ * @since 11.12.18 */ @RestController -public class SecurityQuestionAssignment extends AssignmentEndpoint { +public class SecurityQuestionAssignment implements AssignmentEndpoint { - @Autowired private TriedQuestions triedQuestions; + private final TriedQuestions triedQuestions; private static Map questions; @@ -90,6 +91,10 @@ public class SecurityQuestionAssignment extends AssignmentEndpoint { questions.put("What is your favorite color?", "Can easily be guessed."); } + public SecurityQuestionAssignment(TriedQuestions triedQuestions) { + this.triedQuestions = triedQuestions; + } + @PostMapping("/PasswordReset/SecurityQuestions") @ResponseBody public AttackResult completed(@RequestParam String question) { diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java index 6567321836..cd862d49be 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/SimpleMailAssignment.java @@ -23,9 +23,13 @@ package org.owasp.webgoat.lessons.passwordreset; import static java.util.Optional.ofNullable; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.time.LocalDateTime; import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; @@ -42,8 +46,7 @@ * @since 8/20/17. */ @RestController -public class SimpleMailAssignment extends AssignmentEndpoint { - +public class SimpleMailAssignment implements AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; @@ -57,12 +60,14 @@ public SimpleMailAssignment( path = "/PasswordReset/simple-mail", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @ResponseBody - public AttackResult login(@RequestParam String email, @RequestParam String password) { + public AttackResult login( + @RequestParam String email, + @RequestParam String password, + @CurrentUsername String webGoatUsername) { String emailAddress = ofNullable(email).orElse("unknown@webgoat.org"); String username = extractUsername(emailAddress); - if (username.equals(getWebSession().getUserName()) - && StringUtils.reverse(username).equals(password)) { + if (username.equals(webGoatUsername) && StringUtils.reverse(username).equals(password)) { return success(this).build(); } else { return failed(this).feedbackArgs("password-reset-simple.password_incorrect").build(); @@ -73,9 +78,10 @@ public AttackResult login(@RequestParam String email, @RequestParam String passw consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, value = "/PasswordReset/simple-mail/reset") @ResponseBody - public AttackResult resetPassword(@RequestParam String emailReset) { + public AttackResult resetPassword( + @RequestParam String emailReset, @CurrentUsername String username) { String email = ofNullable(emailReset).orElse("unknown@webgoat.org"); - return sendEmail(extractUsername(email), email); + return sendEmail(extractUsername(email), email, username); } private String extractUsername(String email) { @@ -83,8 +89,8 @@ private String extractUsername(String email) { return email.substring(0, index == -1 ? email.length() : index); } - private AttackResult sendEmail(String username, String email) { - if (username.equals(getWebSession().getUserName())) { + private AttackResult sendEmail(String username, String email, String webGoatUsername) { + if (username.equals(webGoatUsername)) { PasswordResetEmail mailEvent = PasswordResetEmail.builder() .recipient(username) diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java index 6c76cede71..c6e07a048a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUpload.java @@ -3,9 +3,9 @@ import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -23,9 +23,8 @@ }) public class ProfileUpload extends ProfileUploadBase { - public ProfileUpload( - @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { - super(webGoatHomeDirectory, webSession); + public ProfileUpload(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { + super(webGoatHomeDirectory); } @PostMapping( @@ -35,13 +34,14 @@ public ProfileUpload( @ResponseBody public AttackResult uploadFileHandler( @RequestParam("uploadedFile") MultipartFile file, - @RequestParam(value = "fullName", required = false) String fullName) { - return super.execute(file, fullName); + @RequestParam(value = "fullName", required = false) String fullName, + @CurrentUsername String username) { + return super.execute(file, fullName, username); } @GetMapping("/PathTraversal/profile-picture") @ResponseBody - public ResponseEntity getProfilePicture() { - return super.getProfilePicture(); + public ResponseEntity getProfilePicture(@CurrentUsername String username) { + return super.getProfilePicture(username); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java index 131f1674ae..f212b170e0 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadBase.java @@ -1,5 +1,9 @@ package org.owasp.webgoat.lessons.pathtraversal; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -7,13 +11,12 @@ import java.util.Arrays; import java.util.Base64; import java.util.List; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.SneakyThrows; import org.apache.commons.io.FilenameUtils; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.util.FileCopyUtils; @@ -21,14 +24,16 @@ import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; -@AllArgsConstructor @Getter -public class ProfileUploadBase extends AssignmentEndpoint { +public class ProfileUploadBase implements AssignmentEndpoint { + + private final String webGoatHomeDirectory; - private String webGoatHomeDirectory; - private WebSession webSession; + public ProfileUploadBase(String webGoatHomeDirectory) { + this.webGoatHomeDirectory = webGoatHomeDirectory; + } - protected AttackResult execute(MultipartFile file, String fullName) { + protected AttackResult execute(MultipartFile file, String fullName, String username) { if (file.isEmpty()) { return failed(this).feedback("path-traversal-profile-empty-file").build(); } @@ -36,7 +41,7 @@ protected AttackResult execute(MultipartFile file, String fullName) { return failed(this).feedback("path-traversal-profile-empty-name").build(); } - File uploadDirectory = cleanupAndCreateDirectoryForUser(); + File uploadDirectory = cleanupAndCreateDirectoryForUser(username); try { var uploadedFile = new File(uploadDirectory, fullName); @@ -57,9 +62,8 @@ protected AttackResult execute(MultipartFile file, String fullName) { } @SneakyThrows - protected File cleanupAndCreateDirectoryForUser() { - var uploadDirectory = - new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); + protected File cleanupAndCreateDirectoryForUser(String username) { + var uploadDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + username); if (uploadDirectory.exists()) { FileSystemUtils.deleteRecursively(uploadDirectory); } @@ -85,15 +89,14 @@ private AttackResult solvedIt(File uploadedFile) throws IOException { .build(); } - public ResponseEntity getProfilePicture() { + public ResponseEntity getProfilePicture(@CurrentUsername String username) { return ResponseEntity.ok() .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE)) - .body(getProfilePictureAsBase64()); + .body(getProfilePictureAsBase64(username)); } - protected byte[] getProfilePictureAsBase64() { - var profilePictureDirectory = - new File(this.webGoatHomeDirectory, "/PathTraversal/" + webSession.getUserName()); + protected byte[] getProfilePictureAsBase64(String username) { + var profilePictureDirectory = new File(this.webGoatHomeDirectory, "/PathTraversal/" + username); var profileDirectoryFiles = profilePictureDirectory.listFiles(); if (profileDirectoryFiles != null && profileDirectoryFiles.length > 0) { diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java index 90c0589b9e..09087fde11 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadFix.java @@ -3,9 +3,9 @@ import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -23,9 +23,8 @@ }) public class ProfileUploadFix extends ProfileUploadBase { - public ProfileUploadFix( - @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { - super(webGoatHomeDirectory, webSession); + public ProfileUploadFix(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { + super(webGoatHomeDirectory); } @PostMapping( @@ -35,13 +34,14 @@ public ProfileUploadFix( @ResponseBody public AttackResult uploadFileHandler( @RequestParam("uploadedFileFix") MultipartFile file, - @RequestParam(value = "fullNameFix", required = false) String fullName) { - return super.execute(file, fullName != null ? fullName.replace("../", "") : ""); + @RequestParam(value = "fullNameFix", required = false) String fullName, + @CurrentUsername String username) { + return super.execute(file, fullName != null ? fullName.replace("../", "") : "", username); } @GetMapping("/PathTraversal/profile-picture-fix") @ResponseBody - public ResponseEntity getProfilePicture() { - return super.getProfilePicture(); + public ResponseEntity getProfilePicture(@CurrentUsername String username) { + return super.getProfilePicture(username); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java index 95971df266..032c79fb9d 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRemoveUserInput.java @@ -3,9 +3,9 @@ import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -22,8 +22,8 @@ public class ProfileUploadRemoveUserInput extends ProfileUploadBase { public ProfileUploadRemoveUserInput( - @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { - super(webGoatHomeDirectory, webSession); + @Value("${webgoat.server.directory}") String webGoatHomeDirectory) { + super(webGoatHomeDirectory); } @PostMapping( @@ -32,7 +32,8 @@ public ProfileUploadRemoveUserInput( produces = APPLICATION_JSON_VALUE) @ResponseBody public AttackResult uploadFileHandler( - @RequestParam("uploadedFileRemoveUserInput") MultipartFile file) { - return super.execute(file, file.getOriginalFilename()); + @RequestParam("uploadedFileRemoveUserInput") MultipartFile file, + @CurrentUsername String username) { + return super.execute(file, file.getOriginalFilename(), username); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java index 402945f122..2225c4d509 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java @@ -1,5 +1,8 @@ package org.owasp.webgoat.lessons.pathtraversal; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; import java.io.File; @@ -12,6 +15,7 @@ import java.util.Base64; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -39,8 +43,7 @@ "path-traversal-profile-retrieve.hint6" }) @Slf4j -public class ProfileUploadRetrieval extends AssignmentEndpoint { - +public class ProfileUploadRetrieval implements AssignmentEndpoint { private final File catPicturesDirectory; public ProfileUploadRetrieval(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { @@ -71,8 +74,10 @@ public void initAssignment() { @PostMapping("/PathTraversal/random") @ResponseBody - public AttackResult execute(@RequestParam(value = "secret", required = false) String secret) { - if (Sha512DigestUtils.shaHex(getWebSession().getUserName()).equalsIgnoreCase(secret)) { + public AttackResult execute( + @RequestParam(value = "secret", required = false) String secret, + @CurrentUsername String username) { + if (Sha512DigestUtils.shaHex(username).equalsIgnoreCase(secret)) { return success(this).build(); } return failed(this).build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java index 49c7b15c36..891d6bafd4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileZipSlip.java @@ -1,5 +1,7 @@ package org.owasp.webgoat.lessons.pathtraversal; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -14,9 +16,9 @@ import java.util.zip.ZipFile; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.util.FileCopyUtils; @@ -38,9 +40,8 @@ @Slf4j public class ProfileZipSlip extends ProfileUploadBase { - public ProfileZipSlip( - @Value("${webgoat.server.directory}") String webGoatHomeDirectory, WebSession webSession) { - super(webGoatHomeDirectory, webSession); + public ProfileZipSlip(@Value("${webgoat.server.directory}") String webGoatHomeDirectory) { + super(webGoatHomeDirectory); } @PostMapping( @@ -48,19 +49,20 @@ public ProfileZipSlip( consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult uploadFileHandler(@RequestParam("uploadedFileZipSlip") MultipartFile file) { + public AttackResult uploadFileHandler( + @RequestParam("uploadedFileZipSlip") MultipartFile file, @CurrentUsername String username) { if (!file.getOriginalFilename().toLowerCase().endsWith(".zip")) { return failed(this).feedback("path-traversal-zip-slip.no-zip").build(); } else { - return processZipUpload(file); + return processZipUpload(file, username); } } @SneakyThrows - private AttackResult processZipUpload(MultipartFile file) { - var tmpZipDirectory = Files.createTempDirectory(getWebSession().getUserName()); - cleanupAndCreateDirectoryForUser(); - var currentImage = getProfilePictureAsBase64(); + private AttackResult processZipUpload(MultipartFile file, String username) { + var tmpZipDirectory = Files.createTempDirectory(username); + cleanupAndCreateDirectoryForUser(username); + var currentImage = getProfilePictureAsBase64(username); try { var uploadedZipFile = tmpZipDirectory.resolve(file.getOriginalFilename()); @@ -75,7 +77,7 @@ private AttackResult processZipUpload(MultipartFile file) { Files.copy(is, f.toPath(), StandardCopyOption.REPLACE_EXISTING); } - return isSolved(currentImage, getProfilePictureAsBase64()); + return isSolved(currentImage, getProfilePictureAsBase64(username)); } catch (IOException e) { return failed(this).output(e.getMessage()).build(); } @@ -90,13 +92,13 @@ private AttackResult isSolved(byte[] currentImage, byte[] newImage) { @GetMapping("/PathTraversal/zip-slip/") @ResponseBody - public ResponseEntity getProfilePicture() { - return super.getProfilePicture(); + public ResponseEntity getProfilePicture(@CurrentUsername String username) { + return super.getProfilePicture(username); } @GetMapping("/PathTraversal/zip-slip/profile-image/{username}") @ResponseBody - public ResponseEntity getProfilePicture(@PathVariable("username") String username) { + public ResponseEntity getProfileImage(@PathVariable String username) { return ResponseEntity.notFound().build(); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java index 5b9932d36a..b90adf4373 100644 --- a/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/securepasswords/SecurePasswordsAssignment.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.securepasswords; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import com.nulabinc.zxcvbn.Strength; import com.nulabinc.zxcvbn.Zxcvbn; import java.text.DecimalFormat; @@ -35,7 +38,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class SecurePasswordsAssignment extends AssignmentEndpoint { +public class SecurePasswordsAssignment implements AssignmentEndpoint { @PostMapping("SecurePasswords/assignment") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java index d8bda90079..6d68423b82 100644 --- a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java @@ -23,6 +23,10 @@ package org.owasp.webgoat.lessons.spoofcookie; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; import java.util.Map; @@ -48,7 +52,7 @@ @AssignmentHints({"spoofcookie.hint1", "spoofcookie.hint2", "spoofcookie.hint3"}) @RestController -public class SpoofCookieAssignment extends AssignmentEndpoint { +public class SpoofCookieAssignment implements AssignmentEndpoint { private static final String COOKIE_NAME = "spoof_auth"; private static final String COOKIE_INFO = diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java index 95f86ca023..f5b0a88ba8 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallenge.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.advanced; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.sql.*; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; @@ -42,7 +45,7 @@ @AssignmentHints( value = {"SqlInjectionChallenge1", "SqlInjectionChallenge2", "SqlInjectionChallenge3"}) @Slf4j -public class SqlInjectionChallenge extends AssignmentEndpoint { +public class SqlInjectionChallenge implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java index bdfcc88f20..4a63748510 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionChallengeLogin.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.advanced; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; @@ -39,8 +42,7 @@ "SqlInjectionChallengeHint3", "SqlInjectionChallengeHint4" }) -public class SqlInjectionChallengeLogin extends AssignmentEndpoint { - +public class SqlInjectionChallengeLogin implements AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionChallengeLogin(LessonDataSource dataSource) { diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java index 313c739100..96f090ff7d 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6a.java @@ -22,7 +22,14 @@ package org.owasp.webgoat.lessons.sqlinjection.advanced; -import java.sql.*; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; @@ -42,8 +49,7 @@ "SqlStringInjectionHint-advanced-6a-4", "SqlStringInjectionHint-advanced-6a-5" }) -public class SqlInjectionLesson6a extends AssignmentEndpoint { - +public class SqlInjectionLesson6a implements AssignmentEndpoint { private final LessonDataSource dataSource; private static final String YOUR_QUERY_WAS = "
Your query was: "; @@ -62,55 +68,66 @@ public AttackResult completed(@RequestParam(value = "userid_6a") String userId) public AttackResult injectableQuery(String accountName) { String query = ""; try (Connection connection = dataSource.getConnection()) { - boolean usedUnion = true; + boolean usedUnion = this.unionQueryChecker(accountName); query = "SELECT * FROM user_data WHERE last_name = '" + accountName + "'"; - // Check if Union is used - if (!accountName.matches("(?i)(^[^-/*;)]*)(\\s*)UNION(.*$)")) { - usedUnion = false; - } - try (Statement statement = - connection.createStatement( - ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { - ResultSet results = statement.executeQuery(query); - - if ((results != null) && results.first()) { - ResultSetMetaData resultsMetaData = results.getMetaData(); - StringBuilder output = new StringBuilder(); - - output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); - - String appendingWhenSucceded; - if (usedUnion) - appendingWhenSucceded = - "Well done! Can you also figure out a solution, by appending a new SQL Statement?"; - else - appendingWhenSucceded = - "Well done! Can you also figure out a solution, by using a UNION?"; - results.last(); - - if (output.toString().contains("dave") && output.toString().contains("passW0rD")) { - output.append(appendingWhenSucceded); - return success(this) - .feedback("sql-injection.advanced.6a.success") - .feedbackArgs(output.toString()) - .output(" Your query was: " + query) - .build(); - } else { - return failed(this).output(output.toString() + YOUR_QUERY_WAS + query).build(); - } - } else { - return failed(this) - .feedback("sql-injection.advanced.6a.no.results") - .output(YOUR_QUERY_WAS + query) - .build(); - } - } catch (SQLException sqle) { - return failed(this).output(sqle.getMessage() + YOUR_QUERY_WAS + query).build(); - } + + return executeSqlInjection(connection, query, usedUnion); } catch (Exception e) { return failed(this) .output(this.getClass().getName() + " : " + e.getMessage() + YOUR_QUERY_WAS + query) .build(); } } + + private boolean unionQueryChecker(String accountName) { + return accountName.matches("(?i)(^[^-/*;)]*)(\\s*)UNION(.*$)"); + } + + private AttackResult executeSqlInjection(Connection connection, String query, boolean usedUnion) { + try (Statement statement = + connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { + + ResultSet results = statement.executeQuery(query); + + if (!((results != null) && results.first())) { + return failed(this) + .feedback("sql-injection.advanced.6a.no.results") + .output(YOUR_QUERY_WAS + query) + .build(); + } + + ResultSetMetaData resultsMetaData = results.getMetaData(); + StringBuilder output = new StringBuilder(); + String appendingWhenSucceded = this.appendSuccededMessage(usedUnion); + + output.append(SqlInjectionLesson5a.writeTable(results, resultsMetaData)); + results.last(); + + return verifySqlInjection(output, appendingWhenSucceded, query); + } catch (SQLException sqle) { + return failed(this).output(sqle.getMessage() + YOUR_QUERY_WAS + query).build(); + } + } + + private String appendSuccededMessage(boolean isUsedUnion) { + String appendingWhenSucceded = "Well done! Can you also figure out a solution, by "; + + appendingWhenSucceded += isUsedUnion ? "appending a new SQL Statement?" : "using a UNION?"; + + return appendingWhenSucceded; + } + + private AttackResult verifySqlInjection( + StringBuilder output, String appendingWhenSucceded, String query) { + if (!(output.toString().contains("dave") && output.toString().contains("passW0rD"))) { + return failed(this).output(output.toString() + YOUR_QUERY_WAS + query).build(); + } + + output.append(appendingWhenSucceded); + return success(this) + .feedback("sql-injection.advanced.6a.success") + .feedbackArgs(output.toString()) + .output(" Your query was: " + query) + .build(); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java index 5cf42437fd..36e8530abf 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionLesson6b.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.advanced; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import java.sql.Connection; import java.sql.ResultSet; @@ -36,8 +39,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class SqlInjectionLesson6b extends AssignmentEndpoint { - +public class SqlInjectionLesson6b implements AssignmentEndpoint { private final LessonDataSource dataSource; public SqlInjectionLesson6b(LessonDataSource dataSource) { diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java index e7c03139ac..841bafbbf9 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/advanced/SqlInjectionQuiz.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.advanced; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; @@ -37,7 +40,7 @@ * implement the quiz go to the quiz.js file in webgoat-container -> js */ @RestController -public class SqlInjectionQuiz extends AssignmentEndpoint { +public class SqlInjectionQuiz implements AssignmentEndpoint { String[] solutions = {"Solution 4", "Solution 3", "Solution 2", "Solution 3", "Solution 4"}; boolean[] guesses = new boolean[solutions.length]; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java index 55f8021164..215a00bf3f 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson10.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; @@ -45,7 +48,7 @@ "SqlStringInjectionHint.10.5", "SqlStringInjectionHint.10.6" }) -public class SqlInjectionLesson10 extends AssignmentEndpoint { +public class SqlInjectionLesson10 implements AssignmentEndpoint { private final LessonDataSource dataSource; @@ -120,8 +123,7 @@ private boolean tableExists(Connection connection) { if (errorMsg.contains("object not found: ACCESS_LOG")) { return false; } else { - System.err.println(e.getMessage()); - return false; + return true; } } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java index 5540f31a4f..15f595960b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson2.java @@ -24,6 +24,8 @@ import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.sql.ResultSet; import java.sql.SQLException; @@ -45,7 +47,7 @@ "SqlStringInjectionHint2-3", "SqlStringInjectionHint2-4" }) -public class SqlInjectionLesson2 extends AssignmentEndpoint { +public class SqlInjectionLesson2 implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java index f34c9302d6..352fa5e2ce 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson3.java @@ -24,6 +24,8 @@ import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.sql.Connection; import java.sql.ResultSet; @@ -40,7 +42,7 @@ @RestController @AssignmentHints(value = {"SqlStringInjectionHint3-1", "SqlStringInjectionHint3-2"}) -public class SqlInjectionLesson3 extends AssignmentEndpoint { +public class SqlInjectionLesson3 implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java index 2299becc49..24a64d2877 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson4.java @@ -24,6 +24,8 @@ import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.sql.Connection; import java.sql.ResultSet; @@ -41,7 +43,7 @@ @RestController @AssignmentHints( value = {"SqlStringInjectionHint4-1", "SqlStringInjectionHint4-2", "SqlStringInjectionHint4-3"}) -public class SqlInjectionLesson4 extends AssignmentEndpoint { +public class SqlInjectionLesson4 implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java index 9678a2f9d2..aa6e292005 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.annotation.PostConstruct; import java.sql.Connection; import java.sql.ResultSet; @@ -43,7 +46,7 @@ "SqlStringInjectionHint5-3", "SqlStringInjectionHint5-4" }) -public class SqlInjectionLesson5 extends AssignmentEndpoint { +public class SqlInjectionLesson5 implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java index 59a29ff103..74431a9bf4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.sql.*; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; @@ -34,15 +37,15 @@ @RestController @AssignmentHints(value = {"SqlStringInjectionHint5a1"}) -public class SqlInjectionLesson5a extends AssignmentEndpoint { +public class SqlInjectionLesson5a implements AssignmentEndpoint { private static final String EXPLANATION = "
Explanation: This injection works, because or '1' =" + " '1' always evaluates to true (The string ending literal for '1 is closed by" + " the query itself, so you should not inject it). So the injected query basically looks" + " like this: SELECT * FROM user_data WHERE" - + " first_name = 'John' and last_name = '' or TRUE, which will always evaluate to" - + " true, no matter what came before it."; + + " (first_name = 'John' and last_name = '') or (TRUE), which will always evaluate" + + " to true, no matter what came before it."; private final LessonDataSource dataSource; public SqlInjectionLesson5a(LessonDataSource dataSource) { diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java index d8cecf291e..4e4d3c41b4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java @@ -22,7 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; -import jakarta.servlet.http.HttpServletRequest; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import java.sql.*; import org.owasp.webgoat.container.LessonDataSource; @@ -42,7 +44,7 @@ "SqlStringInjectionHint5b3", "SqlStringInjectionHint5b4" }) -public class SqlInjectionLesson5b extends AssignmentEndpoint { +public class SqlInjectionLesson5b implements AssignmentEndpoint { private final LessonDataSource dataSource; @@ -52,8 +54,7 @@ public SqlInjectionLesson5b(LessonDataSource dataSource) { @PostMapping("/SqlInjection/assignment5b") @ResponseBody - public AttackResult completed( - @RequestParam String userid, @RequestParam String login_count, HttpServletRequest request) + public AttackResult completed(@RequestParam String userid, @RequestParam String login_count) throws IOException { return injectableQuery(login_count, userid); } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java index ae7fbb9f44..56f81ff565 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson8.java @@ -24,6 +24,8 @@ import static java.sql.ResultSet.CONCUR_UPDATABLE; import static java.sql.ResultSet.TYPE_SCROLL_SENSITIVE; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.sql.*; import java.text.SimpleDateFormat; @@ -46,7 +48,7 @@ "SqlStringInjectionHint.8.4", "SqlStringInjectionHint.8.5" }) -public class SqlInjectionLesson8 extends AssignmentEndpoint { +public class SqlInjectionLesson8 implements AssignmentEndpoint { private final LessonDataSource dataSource; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java index 3df08175a8..79ec3bb0a7 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson9.java @@ -24,6 +24,8 @@ import static org.hsqldb.jdbc.JDBCResultSet.CONCUR_UPDATABLE; import static org.hsqldb.jdbc.JDBCResultSet.TYPE_SCROLL_SENSITIVE; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import java.sql.Connection; import java.sql.ResultSet; @@ -47,7 +49,7 @@ "SqlStringInjectionHint.9.4", "SqlStringInjectionHint.9.5" }) -public class SqlInjectionLesson9 extends AssignmentEndpoint { +public class SqlInjectionLesson9 implements AssignmentEndpoint { private final LessonDataSource dataSource; @@ -63,66 +65,73 @@ public AttackResult completed(@RequestParam String name, @RequestParam String au protected AttackResult injectableQueryIntegrity(String name, String auth_tan) { StringBuilder output = new StringBuilder(); - String query = + String queryInjection = "SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'"; try (Connection connection = dataSource.getConnection()) { - try { - Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); - SqlInjectionLesson8.log(connection, query); - ResultSet results = statement.executeQuery(query); - var test = results.getRow() != 0; - if (results.getStatement() != null) { - if (results.first()) { - output.append(SqlInjectionLesson8.generateTable(results)); - } else { - // no results - return failed(this).feedback("sql-injection.8.no.results").build(); - } - } - } catch (SQLException e) { - System.err.println(e.getMessage()); - return failed(this) - .output("
") - .build(); + // V2019_09_26_7__employees.sql + int oldMaxSalary = this.getMaxSalary(connection); + int oldSumSalariesOfOtherEmployees = this.getSumSalariesOfOtherEmployees(connection); + // begin transaction + connection.setAutoCommit(false); + // do injectable query + Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); + SqlInjectionLesson8.log(connection, queryInjection); + statement.execute(queryInjection); + // check new sum of salaries other employees and new salaries of John + int newJohnSalary = this.getJohnSalary(connection); + int newSumSalariesOfOtherEmployees = this.getSumSalariesOfOtherEmployees(connection); + if (newJohnSalary > oldMaxSalary + && newSumSalariesOfOtherEmployees == oldSumSalariesOfOtherEmployees) { + // success commit + connection.commit(); // need execute not executeQuery + connection.setAutoCommit(true); + output.append( + SqlInjectionLesson8.generateTable(this.getEmployeesDataOrderBySalaryDesc(connection))); + return success(this).feedback("sql-injection.9.success").output(output.toString()).build(); } - - return checkSalaryRanking(connection, output); - - } catch (Exception e) { - System.err.println(e.getMessage()); + // failed roolback + connection.rollback(); return failed(this) - .output("
") + .feedback("sql-injection.9.one") + .output( + SqlInjectionLesson8.generateTable(this.getEmployeesDataOrderBySalaryDesc(connection))) .build(); - } - } - - private AttackResult checkSalaryRanking(Connection connection, StringBuilder output) { - try { - String query = "SELECT * FROM employees ORDER BY salary DESC"; - try (Statement statement = - connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); ) { - ResultSet results = statement.executeQuery(query); - - results.first(); - // user completes lesson if John Smith is the first in the list - if ((results.getString(2).equals("John")) && (results.getString(3).equals("Smith"))) { - output.append(SqlInjectionLesson8.generateTable(results)); - return success(this) - .feedback("sql-injection.9.success") - .output(output.toString()) - .build(); - } else { - return failed(this).feedback("sql-injection.9.one").output(output.toString()).build(); - } - } } catch (SQLException e) { return failed(this) .output("
") .build(); } } + + private int getSqlInt(Connection connection, String query) throws SQLException { + Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); + ResultSet results = statement.executeQuery(query); + results.first(); + return results.getInt(1); + } + + private int getMaxSalary(Connection connection) throws SQLException { + String query = "SELECT max(salary) FROM employees"; + return this.getSqlInt(connection, query); + } + + private int getSumSalariesOfOtherEmployees(Connection connection) throws SQLException { + String query = "SELECT sum(salary) FROM employees WHERE auth_tan != '3SL99A'"; + return this.getSqlInt(connection, query); + } + + private int getJohnSalary(Connection connection) throws SQLException { + String query = "SELECT salary FROM employees WHERE auth_tan = '3SL99A'"; + return this.getSqlInt(connection, query); + } + + private ResultSet getEmployeesDataOrderBySalaryDesc(Connection connection) throws SQLException { + String query = "SELECT * FROM employees ORDER BY salary DESC"; + Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE); + return statement.executeQuery(query); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java index fbe5514278..d4dc18d2c3 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10a.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.mitigation; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; @@ -35,9 +38,9 @@ @Slf4j @AssignmentHints( value = {"SqlStringInjectionHint-mitigation-10a-1", "SqlStringInjectionHint-mitigation-10a-2"}) -public class SqlInjectionLesson10a extends AssignmentEndpoint { +public class SqlInjectionLesson10a implements AssignmentEndpoint { - private String[] results = { + private static final String[] results = { "getConnection", "PreparedStatement", "prepareStatement", "?", "?", "setString", "setString" }; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java index 325d376bb2..2037313f69 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson10b.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.mitigation; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import java.net.URI; import java.util.Arrays; @@ -52,7 +55,7 @@ "SqlStringInjectionHint-mitigation-10b-4", "SqlStringInjectionHint-mitigation-10b-5" }) -public class SqlInjectionLesson10b extends AssignmentEndpoint { +public class SqlInjectionLesson10b implements AssignmentEndpoint { @PostMapping("/SqlInjectionMitigations/attack10b") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java index 453f0e3e1b..f2ac154d0e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlInjectionLesson13.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.mitigation; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -45,7 +48,7 @@ "SqlStringInjectionHint-mitigation-13-4" }) @Slf4j -public class SqlInjectionLesson13 extends AssignmentEndpoint { +public class SqlInjectionLesson13 implements AssignmentEndpoint { private final LessonDataSource dataSource; @@ -68,7 +71,7 @@ public AttackResult completed(@RequestParam String ip) { return failed(this).build(); } catch (SQLException e) { log.error("Failed", e); - return (failed(this).build()); + return failed(this).build(); } } } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java index 4cfec63370..f0df96711c 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidation.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.sqlinjection.mitigation; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; + import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -34,7 +36,7 @@ @RestController @AssignmentHints( value = {"SqlOnlyInputValidation-1", "SqlOnlyInputValidation-2", "SqlOnlyInputValidation-3"}) -public class SqlOnlyInputValidation extends AssignmentEndpoint { +public class SqlOnlyInputValidation implements AssignmentEndpoint { private final SqlInjectionLesson6a lesson6a; @@ -52,7 +54,9 @@ public AttackResult attack(@RequestParam("userid_sql_only_input_validation") Str return new AttackResult( attackResult.isLessonCompleted(), attackResult.getFeedback(), + attackResult.getFeedbackArgs(), attackResult.getOutput(), + attackResult.getOutputArgs(), getClass().getSimpleName(), true); } diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java index 3a324bc65b..0ca75999a5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/mitigation/SqlOnlyInputValidationOnKeywords.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.sqlinjection.mitigation; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; + import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -38,7 +40,7 @@ "SqlOnlyInputValidationOnKeywords-2", "SqlOnlyInputValidationOnKeywords-3" }) -public class SqlOnlyInputValidationOnKeywords extends AssignmentEndpoint { +public class SqlOnlyInputValidationOnKeywords implements AssignmentEndpoint { private final SqlInjectionLesson6a lesson6a; @@ -58,7 +60,9 @@ public AttackResult attack( return new AttackResult( attackResult.isLessonCompleted(), attackResult.getFeedback(), + attackResult.getFeedbackArgs(), attackResult.getOutput(), + attackResult.getOutputArgs(), getClass().getSimpleName(), true); } diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java index 3a07664f3e..986602731e 100644 --- a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.ssrf; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; @@ -32,7 +35,7 @@ @RestController @AssignmentHints({"ssrf.hint1", "ssrf.hint2"}) -public class SSRFTask1 extends AssignmentEndpoint { +public class SSRFTask1 implements AssignmentEndpoint { @PostMapping("/SSRF/task1") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java index 35f9491f77..a48b42278b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.ssrf; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -37,7 +40,7 @@ @RestController @AssignmentHints({"ssrf.hint3"}) -public class SSRFTask2 extends AssignmentEndpoint { +public class SSRFTask2 implements AssignmentEndpoint { @PostMapping("/SSRF/task2") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java index ad1a91cc47..e2edd5667b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java +++ b/src/main/java/org/owasp/webgoat/lessons/vulnerablecomponents/VulnerableComponentsLesson.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.vulnerablecomponents; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import com.thoughtworks.xstream.XStream; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; @@ -34,7 +37,7 @@ @RestController @AssignmentHints({"vulnerable.hint"}) -public class VulnerableComponentsLesson extends AssignmentEndpoint { +public class VulnerableComponentsLesson implements AssignmentEndpoint { @PostMapping("/VulnerableComponents/attack1") public @ResponseBody AttackResult completed(@RequestParam String payload) { diff --git a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java index 26f8439e87..152bae920a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java @@ -22,10 +22,11 @@ package org.owasp.webgoat.lessons.webwolfintroduction; -import jakarta.servlet.http.HttpServletRequest; -import java.net.URI; -import java.net.URISyntaxException; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; @@ -40,27 +41,28 @@ * @since 8/20/17. */ @RestController -public class LandingAssignment extends AssignmentEndpoint { +public class LandingAssignment implements AssignmentEndpoint { + private final String landingPageUrl; - @Value("${webwolf.landingpage.url}") - private String landingPageUrl; + public LandingAssignment(@Value("${webwolf.landingpage.url}") String landingPageUrl) { + this.landingPageUrl = landingPageUrl; + } @PostMapping("/WebWolf/landing") @ResponseBody - public AttackResult click(String uniqueCode) { - if (StringUtils.reverse(getWebSession().getUserName()).equals(uniqueCode)) { + public AttackResult click(String uniqueCode, @CurrentUsername String username) { + if (StringUtils.reverse(username).equals(uniqueCode)) { return success(this).build(); } return failed(this).feedback("webwolf.landing_wrong").build(); } @GetMapping("/WebWolf/landing/password-reset") - public ModelAndView openPasswordReset(HttpServletRequest request) throws URISyntaxException { - URI uri = new URI(request.getRequestURL().toString()); + public ModelAndView openPasswordReset(@CurrentUsername String username) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject( "webwolfLandingPageUrl", landingPageUrl.replace("//landing", "/landing")); - modelAndView.addObject("uniqueCode", StringUtils.reverse(getWebSession().getUserName())); + modelAndView.addObject("uniqueCode", StringUtils.reverse(username)); modelAndView.setViewName("lessons/webwolfintroduction/templates/webwolfPasswordReset.html"); return modelAndView; diff --git a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java index 8dd168d6e8..2748876402 100644 --- a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/MailAssignment.java @@ -22,7 +22,12 @@ package org.owasp.webgoat.lessons.webwolfintroduction; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.informationMessage; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import org.apache.commons.lang3.StringUtils; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; @@ -38,7 +43,7 @@ * @since 8/20/17. */ @RestController -public class MailAssignment extends AssignmentEndpoint { +public class MailAssignment implements AssignmentEndpoint { private final String webWolfURL; private RestTemplate restTemplate; @@ -51,9 +56,10 @@ public MailAssignment( @PostMapping("/WebWolf/mail/send") @ResponseBody - public AttackResult sendEmail(@RequestParam String email) { + public AttackResult sendEmail( + @RequestParam String email, @CurrentUsername String webGoatUsername) { String username = email.substring(0, email.indexOf("@")); - if (username.equalsIgnoreCase(getWebSession().getUserName())) { + if (username.equalsIgnoreCase(webGoatUsername)) { Email mailEvent = Email.builder() .recipient(username) @@ -82,8 +88,8 @@ public AttackResult sendEmail(@RequestParam String email) { @PostMapping("/WebWolf/mail") @ResponseBody - public AttackResult completed(@RequestParam String uniqueCode) { - if (uniqueCode.equals(StringUtils.reverse(getWebSession().getUserName()))) { + public AttackResult completed(@RequestParam String uniqueCode, @CurrentUsername String username) { + if (uniqueCode.equals(StringUtils.reverse(username))) { return success(this).build(); } else { return failed(this).feedbackArgs("webwolf.code_incorrect").feedbackArgs(uniqueCode).build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java index 114632ef54..79ee3469e4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson1.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.xss; +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.AttackResult; import org.springframework.web.bind.annotation.PostMapping; @@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class CrossSiteScriptingLesson1 extends AssignmentEndpoint { +public class CrossSiteScriptingLesson1 implements AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack1") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java index 9807d8d4e5..dcc1b59034 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson5a.java @@ -22,13 +22,15 @@ package org.owasp.webgoat.lessons.xss; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.util.function.Predicate; import java.util.regex.Pattern; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -42,13 +44,18 @@ "xss-reflected-5a-hint-3", "xss-reflected-5a-hint-4" }) -public class CrossSiteScriptingLesson5a extends AssignmentEndpoint { +public class CrossSiteScriptingLesson5a implements AssignmentEndpoint { public static final Predicate XSS_PATTERN = Pattern.compile( ".*.*", Pattern.CASE_INSENSITIVE) .asMatchPredicate(); - @Autowired UserSessionData userSessionData; + + private final LessonSession userSessionData; + + public CrossSiteScriptingLesson5a(LessonSession lessonSession) { + this.userSessionData = lessonSession; + } @GetMapping("/CrossSiteScripting/attack5a") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java index d0252280c2..a6fb245c35 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson6a.java @@ -22,11 +22,13 @@ package org.owasp.webgoat.lessons.xss; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -40,8 +42,12 @@ "xss-reflected-6a-hint-3", "xss-reflected-6a-hint-4" }) -public class CrossSiteScriptingLesson6a extends AssignmentEndpoint { - @Autowired UserSessionData userSessionData; +public class CrossSiteScriptingLesson6a implements AssignmentEndpoint { + private final LessonSession userSessionData; + + public CrossSiteScriptingLesson6a(LessonSession userSessionData) { + this.userSessionData = userSessionData; + } @PostMapping("/CrossSiteScripting/attack6a") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java index e193d262a3..ab2a0a3108 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingQuiz.java @@ -22,6 +22,9 @@ package org.owasp.webgoat.lessons.xss; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import java.io.IOException; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; @@ -32,9 +35,11 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class CrossSiteScriptingQuiz extends AssignmentEndpoint { +public class CrossSiteScriptingQuiz implements AssignmentEndpoint { - String[] solutions = {"Solution 4", "Solution 3", "Solution 1", "Solution 2", "Solution 4"}; + private static final String[] solutions = { + "Solution 4", "Solution 3", "Solution 1", "Solution 2", "Solution 4" + }; boolean[] guesses = new boolean[solutions.length]; @PostMapping("/CrossSiteScripting/quiz") diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java index e7df0a4ed1..83b9276499 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java @@ -22,32 +22,40 @@ package org.owasp.webgoat.lessons.xss; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; + import jakarta.servlet.http.HttpServletRequest; import java.security.SecureRandom; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -public class DOMCrossSiteScripting extends AssignmentEndpoint { +public class DOMCrossSiteScripting implements AssignmentEndpoint { + + private final LessonSession lessonSession; + + public DOMCrossSiteScripting(LessonSession lessonSession) { + this.lessonSession = lessonSession; + } @PostMapping("/CrossSiteScripting/phone-home-xss") @ResponseBody public AttackResult completed( @RequestParam Integer param1, @RequestParam Integer param2, HttpServletRequest request) { - UserSessionData userSessionData = getUserSessionData(); SecureRandom number = new SecureRandom(); - userSessionData.setValue("randValue", String.valueOf(number.nextInt())); + lessonSession.setValue("randValue", String.valueOf(number.nextInt())); if (param1 == 42 && param2 == 24 && request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) { return success(this) - .output("phoneHome Response is " + userSessionData.getValue("randValue").toString()) + .output("phoneHome Response is " + lessonSession.getValue("randValue").toString()) .build(); } else { return failed(this).build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScriptingVerifier.java b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScriptingVerifier.java index 10e471c80f..87a4e74f6f 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScriptingVerifier.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScriptingVerifier.java @@ -22,10 +22,13 @@ package org.owasp.webgoat.lessons.xss; +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.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -42,13 +45,18 @@ "xss-dom-message-hint-5", "xss-dom-message-hint-6" }) -public class DOMCrossSiteScriptingVerifier extends AssignmentEndpoint { +public class DOMCrossSiteScriptingVerifier implements AssignmentEndpoint { + + private final LessonSession lessonSession; + + public DOMCrossSiteScriptingVerifier(LessonSession lessonSession) { + this.lessonSession = lessonSession; + } @PostMapping("/CrossSiteScripting/dom-follow-up") @ResponseBody public AttackResult completed(@RequestParam String successMessage) { - UserSessionData userSessionData = getUserSessionData(); - String answer = (String) userSessionData.getValue("randValue"); + String answer = (String) lessonSession.getValue("randValue"); if (successMessage.equals(answer)) { return success(this).feedback("xss-dom-message-success").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson3.java similarity index 92% rename from src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java rename to src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson3.java index fa3a0a3c27..dc59d0cb30 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson3.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson3.java @@ -21,7 +21,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.lessons.xss; +package org.owasp.webgoat.lessons.xss.mitigation; + +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -41,7 +44,7 @@ "xss-mitigation-3-hint3", "xss-mitigation-3-hint4" }) -public class CrossSiteScriptingLesson3 extends AssignmentEndpoint { +public class CrossSiteScriptingLesson3 implements AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack3") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson4.java similarity index 89% rename from src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java rename to src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson4.java index 1bf5fbe016..7afcc5d276 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingLesson4.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingLesson4.java @@ -20,7 +20,10 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.lessons.xss; +package org.owasp.webgoat.lessons.xss.mitigation; + +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.AssignmentHints; @@ -32,7 +35,7 @@ @RestController @AssignmentHints(value = {"xss-mitigation-4-hint1"}) -public class CrossSiteScriptingLesson4 extends AssignmentEndpoint { +public class CrossSiteScriptingLesson4 implements AssignmentEndpoint { @PostMapping("/CrossSiteScripting/attack4") @ResponseBody diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingMitigation.java similarity index 96% rename from src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java rename to src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingMitigation.java index bf5b777638..b61c03a012 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/CrossSiteScriptingMitigation.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/mitigation/CrossSiteScriptingMitigation.java @@ -21,7 +21,7 @@ * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. */ -package org.owasp.webgoat.lessons.xss; +package org.owasp.webgoat.lessons.xss.mitigation; import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Lesson; diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredCrossSiteScriptingVerifier.java b/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredCrossSiteScriptingVerifier.java index 9502c5f77e..6a51ab0798 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredCrossSiteScriptingVerifier.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredCrossSiteScriptingVerifier.java @@ -22,9 +22,12 @@ package org.owasp.webgoat.lessons.xss.stored; +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.AttackResult; -import org.owasp.webgoat.container.session.UserSessionData; +import org.owasp.webgoat.container.session.LessonSession; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -32,14 +35,18 @@ /** Created by jason on 11/23/16. */ @RestController -public class StoredCrossSiteScriptingVerifier extends AssignmentEndpoint { +public class StoredCrossSiteScriptingVerifier implements AssignmentEndpoint { + + private final LessonSession lessonSession; + + public StoredCrossSiteScriptingVerifier(LessonSession lessonSession) { + this.lessonSession = lessonSession; + } @PostMapping("/CrossSiteScriptingStored/stored-xss-follow-up") @ResponseBody public AttackResult completed(@RequestParam String successMessage) { - UserSessionData userSessionData = getUserSessionData(); - - if (successMessage.equals(userSessionData.getValue("randValue"))) { + if (successMessage.equals(lessonSession.getValue("randValue"))) { return success(this).feedback("xss-stored-callback-success").build(); } else { return failed(this).feedback("xss-stored-callback-failure").build(); diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredXssComments.java b/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredXssComments.java index 196e2e3e1f..7d9c28fbcf 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredXssComments.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/stored/StoredXssComments.java @@ -22,6 +22,8 @@ package org.owasp.webgoat.lessons.xss.stored; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.ALL_VALUE; import com.fasterxml.jackson.databind.ObjectMapper; @@ -35,11 +37,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.owasp.webgoat.container.CurrentUsername; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.lessons.xss.Comment; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -48,10 +49,9 @@ import org.springframework.web.bind.annotation.RestController; @RestController -public class StoredXssComments extends AssignmentEndpoint { +public class StoredXssComments implements AssignmentEndpoint { - @Autowired private WebSession webSession; - private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); + private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); private static final Map> userComments = new HashMap<>(); private static final List comments = new ArrayList<>(); @@ -77,9 +77,9 @@ public class StoredXssComments extends AssignmentEndpoint { produces = MediaType.APPLICATION_JSON_VALUE, consumes = ALL_VALUE) @ResponseBody - public Collection retrieveComments() { + public Collection retrieveComments(@CurrentUsername String username) { List allComments = Lists.newArrayList(); - Collection newComments = userComments.get(webSession.getUserName()); + Collection newComments = userComments.get(username); allComments.addAll(comments); if (newComments != null) { allComments.addAll(newComments); @@ -90,15 +90,16 @@ public Collection retrieveComments() { @PostMapping("/CrossSiteScriptingStored/stored-xss") @ResponseBody - public AttackResult createNewComment(@RequestBody String commentStr) { + public AttackResult createNewComment( + @RequestBody String commentStr, @CurrentUsername String username) { Comment comment = parseJson(commentStr); - List comments = userComments.getOrDefault(webSession.getUserName(), new ArrayList<>()); + List comments = userComments.getOrDefault(username, new ArrayList<>()); comment.setDateTime(LocalDateTime.now().format(fmt)); - comment.setUser(webSession.getUserName()); + comment.setUser(username); comments.add(comment); - userComments.put(webSession.getUserName(), comments); + userComments.put(username, comments); if (comment.getText().contains(phoneHomeString)) { return (success(this).feedback("xss-stored-comment-success").build()); diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java index 317ef948e3..55b5772591 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/BlindSendFileAssignment.java @@ -2,6 +2,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -14,8 +16,10 @@ import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.lessons.Initializable; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.ResponseBody; @@ -56,7 +60,7 @@ "xxe.blind.hints.4", "xxe.blind.hints.5" }) -public class BlindSendFileAssignment extends AssignmentEndpoint { +public class BlindSendFileAssignment implements AssignmentEndpoint, Initializable { private final String webGoatHomeDirectory; private final CommentsCache comments; @@ -84,8 +88,9 @@ private void createSecretFileWithRandomContents(WebGoatUser user) { @PostMapping(path = "xxe/blind", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult addComment(@RequestBody String commentStr) { - var fileContentsForUser = userToFileContents.getOrDefault(getWebSession().getUser(), ""); + public AttackResult addComment( + @RequestBody String commentStr, @AuthenticationPrincipal WebGoatUser user) { + var fileContentsForUser = userToFileContents.getOrDefault(user, ""); // Solution is posted by the user as a separate comment if (commentStr.contains(fileContentsForUser)) { @@ -93,11 +98,11 @@ public AttackResult addComment(@RequestBody String commentStr) { } try { - Comment comment = comments.parseXml(commentStr); + Comment comment = comments.parseXml(commentStr, false); if (fileContentsForUser.contains(comment.getText())) { comment.setText("Nice try, you need to send the file to WebWolf"); } - comments.addComment(comment, false); + comments.addComment(comment, user, false); } catch (Exception e) { return failed(this).output(e.toString()).build(); } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java index e8abf3bd3d..c78ad59ddb 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java @@ -22,13 +22,8 @@ package org.owasp.webgoat.lessons.xxe; -import static java.util.Optional.empty; -import static java.util.Optional.of; - -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; -import java.io.IOException; import java.io.StringReader; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -36,11 +31,9 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import javax.xml.XMLConstants; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; -import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -59,10 +52,7 @@ void sort() { private static final Map userComments = new HashMap<>(); private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); - private final WebSession webSession; - - public CommentsCache(WebSession webSession) { - this.webSession = webSession; + public CommentsCache() { initDefaultComments(); } @@ -76,9 +66,9 @@ void initDefaultComments() { comments.add(new Comment("guest", LocalDateTime.now().format(fmt), "Lol!! :-).")); } - protected Comments getComments() { + protected Comments getComments(WebGoatUser user) { Comments allComments = new Comments(); - Comments commentsByUser = userComments.get(webSession.getUser()); + Comments commentsByUser = userComments.get(user); if (commentsByUser != null) { allComments.addAll(commentsByUser); } @@ -93,11 +83,13 @@ protected Comments getComments() { * progress etc). In real life the XmlMapper bean defined above will be used automatically and the * Comment class can be directly used in the controller method (instead of a String) */ - protected Comment parseXml(String xml) throws XMLStreamException, JAXBException { + protected Comment parseXml(String xml, boolean securityEnabled) + throws XMLStreamException, JAXBException { var jc = JAXBContext.newInstance(Comment.class); var xif = XMLInputFactory.newInstance(); - if (webSession.isSecurityEnabled()) { + // TODO fix me disabled for now. + if (securityEnabled) { xif.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant xif.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant } @@ -108,24 +100,15 @@ protected Comment parseXml(String xml) throws XMLStreamException, JAXBException return (Comment) unmarshaller.unmarshal(xsr); } - protected Optional parseJson(String comment) { - ObjectMapper mapper = new ObjectMapper(); - try { - return of(mapper.readValue(comment, Comment.class)); - } catch (IOException e) { - return empty(); - } - } - - public void addComment(Comment comment, boolean visibleForAllUsers) { + public void addComment(Comment comment, WebGoatUser user, boolean visibleForAllUsers) { comment.setDateTime(LocalDateTime.now().format(fmt)); - comment.setUser(webSession.getUserName()); + comment.setUser(user.getUsername()); if (visibleForAllUsers) { comments.add(comment); } else { - var comments = userComments.getOrDefault(webSession.getUserName(), new Comments()); + var comments = userComments.getOrDefault(user.getUsername(), new Comments()); comments.add(comment); - userComments.put(webSession.getUser(), comments); + userComments.put(user, comments); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java index a6e97f8d4e..12ede306b5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java @@ -24,6 +24,8 @@ import java.util.Collection; import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.CurrentUser; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -43,7 +45,7 @@ public class CommentsEndpoint { @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - public Collection retrieveComments() { - return comments.getComments(); + public Collection retrieveComments(@CurrentUser WebGoatUser user) { + return comments.getComments(user); } } diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java index 4555fcd72a..4f5ed172e5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java @@ -22,17 +22,22 @@ package org.owasp.webgoat.lessons.xxe; +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import jakarta.servlet.http.HttpServletRequest; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.Optional; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.owasp.webgoat.container.CurrentUser; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -42,42 +47,41 @@ @RestController @AssignmentHints({"xxe.hints.content.type.xxe.1", "xxe.hints.content.type.xxe.2"}) -public class ContentTypeAssignment extends AssignmentEndpoint { +public class ContentTypeAssignment implements AssignmentEndpoint { private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" }; - @Value("${webgoat.server.directory}") - private String webGoatHomeDirectory; + private final CommentsCache comments; - @Autowired private WebSession webSession; - @Autowired private CommentsCache comments; + public ContentTypeAssignment(CommentsCache comments) { + this.comments = comments; + } @PostMapping(path = "xxe/content-type") @ResponseBody public AttackResult createNewUser( - HttpServletRequest request, @RequestBody String commentStr, - @RequestHeader("Content-Type") String contentType) { + @RequestHeader("Content-Type") String contentType, + @CurrentUser WebGoatUser user) { AttackResult attackResult = failed(this).build(); if (APPLICATION_JSON_VALUE.equals(contentType)) { - comments.parseJson(commentStr).ifPresent(c -> comments.addComment(c, true)); + parseJson(commentStr).ifPresent(c -> comments.addComment(c, user, true)); attackResult = failed(this).feedback("xxe.content.type.feedback.json").build(); } if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) { - String error = ""; try { - Comment comment = comments.parseXml(commentStr); - comments.addComment(comment, false); + Comment comment = comments.parseXml(commentStr, false); + comments.addComment(comment, user, false); if (checkSolution(comment)) { attackResult = success(this).build(); } } catch (Exception e) { - error = ExceptionUtils.getStackTrace(e); + String error = ExceptionUtils.getStackTrace(e); attackResult = failed(this).feedback("xxe.content.type.feedback.xml").output(error).build(); } } @@ -85,6 +89,15 @@ public AttackResult createNewUser( return attackResult; } + protected Optional parseJson(String comment) { + ObjectMapper mapper = new ObjectMapper(); + try { + return of(mapper.readValue(comment, Comment.class)); + } catch (IOException e) { + return empty(); + } + } + private boolean checkSolution(Comment comment) { String[] directoriesToCheck = OS.isFamilyMac() || OS.isFamilyUnix() diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java b/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java index f71dbd7dd5..2f31c99d14 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/Ping.java @@ -26,12 +26,10 @@ import java.io.FileNotFoundException; import java.io.PrintWriter; import lombok.extern.slf4j.Slf4j; -import org.owasp.webgoat.container.session.WebSession; -import org.springframework.beans.factory.annotation.Autowired; +import org.owasp.webgoat.container.CurrentUsername; import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -41,15 +39,15 @@ public class Ping { @Value("${webgoat.user.directory}") private String webGoatHomeDirectory; - @Autowired private WebSession webSession; - - @RequestMapping(method = RequestMethod.GET) + @GetMapping @ResponseBody public String logRequest( - @RequestHeader("User-Agent") String userAgent, @RequestParam(required = false) String text) { + @RequestHeader("User-Agent") String userAgent, + @RequestParam(required = false) String text, + @CurrentUsername String username) { String logLine = String.format("%s %s %s", "GET", userAgent, text); log.debug(logLine); - File logFile = new File(webGoatHomeDirectory, "/XXE/log" + webSession.getUserName() + ".txt"); + File logFile = new File(webGoatHomeDirectory, "/XXE/log" + username + ".txt"); try { try (PrintWriter pw = new PrintWriter(logFile)) { pw.println(logLine); diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java index 53638e0d89..2547cbcd41 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java @@ -22,17 +22,18 @@ package org.owasp.webgoat.lessons.xxe; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed; +import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.owasp.webgoat.container.CurrentUser; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; +import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -40,10 +41,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 4/8/17. - */ @RestController @AssignmentHints({ "xxe.hints.simple.xxe.1", @@ -53,28 +50,27 @@ "xxe.hints.simple.xxe.5", "xxe.hints.simple.xxe.6" }) -public class SimpleXXE extends AssignmentEndpoint { +public class SimpleXXE implements AssignmentEndpoint { private static final String[] DEFAULT_LINUX_DIRECTORIES = {"usr", "etc", "var"}; private static final String[] DEFAULT_WINDOWS_DIRECTORIES = { "Windows", "Program Files (x86)", "Program Files", "pagefile.sys" }; - @Value("${webgoat.server.directory}") - private String webGoatHomeDirectory; - - @Value("${webwolf.landingpage.url}") - private String webWolfURL; + private final CommentsCache comments; - @Autowired private CommentsCache comments; + public SimpleXXE(CommentsCache comments) { + this.comments = comments; + } @PostMapping(path = "xxe/simple", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE) @ResponseBody - public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) { + public AttackResult createNewComment( + @RequestBody String commentStr, @CurrentUser WebGoatUser user) { String error = ""; try { - var comment = comments.parseXml(commentStr); - comments.addComment(comment, false); + var comment = comments.parseXml(commentStr, false); + comments.addComment(comment, user, false); if (checkSolution(comment)) { return success(this).build(); } diff --git a/src/main/java/org/owasp/webgoat/server/ParentConfig.java b/src/main/java/org/owasp/webgoat/server/ParentConfig.java index b4a62db4dc..b089df1ccb 100644 --- a/src/main/java/org/owasp/webgoat/server/ParentConfig.java +++ b/src/main/java/org/owasp/webgoat/server/ParentConfig.java @@ -1,8 +1,6 @@ package org.owasp.webgoat.server; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration -@ComponentScan("org.owasp.webgoat.server") public class ParentConfig {} diff --git a/src/main/java/org/owasp/webgoat/server/StartWebGoat.java b/src/main/java/org/owasp/webgoat/server/StartWebGoat.java index 2e87ef20ec..57e088d0a7 100644 --- a/src/main/java/org/owasp/webgoat/server/StartWebGoat.java +++ b/src/main/java/org/owasp/webgoat/server/StartWebGoat.java @@ -28,23 +28,30 @@ import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.WebGoat; import org.owasp.webgoat.webwolf.WebWolf; -import org.springframework.boot.Banner; +import org.springframework.boot.ResourceBanner; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ClassPathResource; @Slf4j public class StartWebGoat { public static void main(String[] args) { var parentBuilder = - new SpringApplicationBuilder() - .parent(ParentConfig.class) - .web(WebApplicationType.NONE) - .bannerMode(Banner.Mode.OFF); - parentBuilder.child(WebWolf.class).web(WebApplicationType.SERVLET).run(args); + new SpringApplicationBuilder().parent(ParentConfig.class).web(WebApplicationType.NONE); + parentBuilder + .child(WebWolf.class) + .banner(new ResourceBanner(new ClassPathResource("banner-webwolf.txt"))) + .web(WebApplicationType.SERVLET) + .run(args); + ApplicationContext webGoatContext = - parentBuilder.child(WebGoat.class).web(WebApplicationType.SERVLET).run(args); + parentBuilder + .child(WebGoat.class) + .banner(new ResourceBanner(new ClassPathResource("banner-webgoat.txt"))) + .web(WebApplicationType.SERVLET) + .run(args); printStartUpMessage(webGoatContext); } diff --git a/src/main/java/org/owasp/webgoat/webwolf/FileServer.java b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java index 63c173da20..58924e5d60 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/FileServer.java +++ b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java @@ -28,6 +28,7 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.attribute.FileTime; import java.time.ZonedDateTime; @@ -81,13 +82,19 @@ public String getFileLocation() { @PostMapping(value = "/fileupload") public ModelAndView importFile( - @RequestParam("file") MultipartFile myFile, Authentication authentication) + @RequestParam("file") MultipartFile multipartFile, Authentication authentication) throws IOException { - String username = authentication.getName(); + var username = authentication.getName(); var destinationDir = new File(fileLocation, username); destinationDir.mkdirs(); - myFile.transferTo(new File(destinationDir, myFile.getOriginalFilename())); - log.debug("File saved to {}", new File(destinationDir, myFile.getOriginalFilename())); + // DO NOT use multipartFile.transferTo(), see + // https://stackoverflow.com/questions/60336929/java-nio-file-nosuchfileexception-when-file-transferto-is-called + try (InputStream is = multipartFile.getInputStream()) { + var destinationFile = destinationDir.toPath().resolve(multipartFile.getOriginalFilename()); + Files.deleteIfExists(destinationFile); + Files.copy(is, destinationFile); + } + log.debug("File saved to {}", new File(destinationDir, multipartFile.getOriginalFilename())); return new ModelAndView( new RedirectView("files", true), diff --git a/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java index 395f69d360..5c49a6326e 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java +++ b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java @@ -25,15 +25,19 @@ import org.owasp.webgoat.webwolf.requests.WebWolfTraceRepository; import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @Configuration @ComponentScan("org.owasp.webgoat.webwolf") @PropertySource("classpath:application-webwolf.properties") @EnableAutoConfiguration +@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.webwolf"}) +@EntityScan(basePackages = "org.owasp.webgoat.webwolf") public class WebWolf { @Bean diff --git a/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java b/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java index c7e87b559e..7d6f7ad8a8 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/UserRepository.java @@ -23,12 +23,14 @@ package org.owasp.webgoat.webwolf.user; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; /** * @author nbaars * @since 3/19/17. */ -public interface UserRepository extends JpaRepository { +@Repository("webWolfUserRepository") +public interface UserRepository extends JpaRepository { - WebGoatUser findByUsername(String username); + WebWolfUser findByUsername(String username); } diff --git a/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java b/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java index a57980e9ca..dd7a2ee891 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/UserService.java @@ -40,8 +40,8 @@ public UserService(UserRepository userRepository) { } @Override - public WebGoatUser loadUserByUsername(final String username) throws UsernameNotFoundException { - WebGoatUser webGoatUser = userRepository.findByUsername(username); + public WebWolfUser loadUserByUsername(final String username) throws UsernameNotFoundException { + WebWolfUser webGoatUser = userRepository.findByUsername(username); if (webGoatUser == null) { throw new UsernameNotFoundException("User not found"); } @@ -50,6 +50,6 @@ public WebGoatUser loadUserByUsername(final String username) throws UsernameNotF } public void addUser(final String username, final String password) { - userRepository.save(new WebGoatUser(username, password)); + userRepository.save(new WebWolfUser(username, password)); } } diff --git a/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java b/src/main/java/org/owasp/webgoat/webwolf/user/WebWolfUser.java similarity index 91% rename from src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java rename to src/main/java/org/owasp/webgoat/webwolf/user/WebWolfUser.java index 35f7dd92fe..f02351a609 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/WebWolfUser.java @@ -24,6 +24,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; +import jakarta.persistence.Table; import jakarta.persistence.Transient; import java.util.Collection; import java.util.Collections; @@ -38,15 +39,16 @@ */ @Getter @Entity -public class WebGoatUser implements UserDetails { +@Table(name = "WEB_GOAT_USER") +public class WebWolfUser implements UserDetails { @Id private String username; private String password; @Transient private User user; - protected WebGoatUser() {} + protected WebWolfUser() {} - public WebGoatUser(String username, String password) { + public WebWolfUser(String username, String password) { this.username = username; this.password = password; createUser(); diff --git a/src/main/resources/application-webgoat.properties b/src/main/resources/application-webgoat.properties index b394e77c06..a2283b4885 100644 --- a/src/main/resources/application-webgoat.properties +++ b/src/main/resources/application-webgoat.properties @@ -2,7 +2,7 @@ server.error.include-stacktrace=always server.error.path=/error.html server.servlet.context-path=${WEBGOAT_CONTEXT:/WebGoat} server.servlet.session.persistent=false -server.port=${WEBGOAT_PORT:8080} +server.port=${webgoat.port} server.address=${WEBGOAT_HOST:127.0.0.1} webgoat.host=${WEBGOAT_HOST:127.0.0.1} webgoat.port=${WEBGOAT_PORT:8080} @@ -15,7 +15,6 @@ server.ssl.key-store-password=${WEBGOAT_KEYSTORE_PASSWORD:password} server.ssl.key-alias=${WEBGOAT_KEY_ALIAS:goat} server.ssl.enabled=${WEBGOAT_SSLENABLED:false} -spring.banner.location=classpath:banner.txt spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.open-in-view=false @@ -45,13 +44,17 @@ webgoat.database.connection.string=jdbc:hsqldb:mem:{USER} webgoat.default.language=en webgoat.url=http://${server.address}:${server.port}${server.servlet.context-path} -webwolf.host=${WEBWOLF_HOST:127.0.0.1} -webwolf.port=${WEBWOLF_PORT:9090} -webwolf.context=${WEBWOLF_CONTEXT:/WebWolf} -webwolf.url=http://${WEBWOLF_HOST:127.0.0.1}:${WEBWOLF_PORT:9090}${WEBWOLF_CONTEXT:/WebWolf} +webwolf.host=127.0.0.1 +webwolf.port=9090 +webwolf.context=/WebWolf +webwolf.url=http://${webwolf.host}:${webwolf.port}${webwolf.context} webwolf.landingpage.url=${webwolf.url}/landing webwolf.mail.url=${webwolf.url}/mail +spring.jpa.properties.jakarta.persistence.schema-generation.scripts.action=create +spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-target=create.sql +spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-source=metadata + spring.jackson.serialization.indent_output=true spring.jackson.serialization.write-dates-as-timestamps=false @@ -71,3 +74,4 @@ management.endpoints.web.exposure.include=env, health,configprops spring.security.oauth2.client.registration.github.client-id=${WEBGOAT_OAUTH_CLIENTID:dummy} spring.security.oauth2.client.registration.github.client-secret=${WEBGOAT_OAUTH_CLIENTSECRET:dummy} +spring.application.admin.jmx-name=org.springframework.boot:type=Admin,name=WebGoat diff --git a/src/main/resources/application-webwolf.properties b/src/main/resources/application-webwolf.properties index 4d450fc908..a66f8dd2e8 100644 --- a/src/main/resources/application-webwolf.properties +++ b/src/main/resources/application-webwolf.properties @@ -13,7 +13,6 @@ management.server.port=-1 server.servlet.session.cookie.name=WEBWOLFSESSION server.servlet.session.timeout=6000 spring.flyway.enabled=false - spring.thymeleaf.prefix=classpath:/webwolf/templates/ @@ -53,3 +52,5 @@ spring.devtools.restart.additional-paths=webwolf/src/main/resources/static/ spring.security.oauth2.client.registration.github.client-id=${WEBWOLF_OAUTH_CLIENTID:dummy} spring.security.oauth2.client.registration.github.client-secret=${WEBWOLF_OAUTH_CLIENTSECRET:dummy} + +spring.application.admin.jmx-name=org.springframework.boot:type=Admin,name=WebWolf diff --git a/src/main/resources/banner-webgoat.txt b/src/main/resources/banner-webgoat.txt new file mode 100644 index 0000000000..f9bf062ccc --- /dev/null +++ b/src/main/resources/banner-webgoat.txt @@ -0,0 +1,5 @@ + __ __ _ ____ _ + \ \ / /__| |__ / ___| ___ __ _| |_ + \ \ /\ / / _ \ '_ \| | _ / _ \ / _` | __| + \ V V / __/ |_) | |_| | (_) | (_| | |_ + \_/\_/ \___|_.__/ \____|\___/ \__,_|\__| diff --git a/src/main/resources/banner-webwolf.txt b/src/main/resources/banner-webwolf.txt new file mode 100644 index 0000000000..52dbf768c9 --- /dev/null +++ b/src/main/resources/banner-webwolf.txt @@ -0,0 +1,5 @@ + __ __ _ __ __ _ __ + \ \ / /__| |_\ \ / /__ | |/ _| + \ \ /\ / / _ \ '_ \ \ /\ / / _ \| | |_ + \ V V / __/ |_) \ V V / (_) | | _| + \_/\_/ \___|_.__/ \_/\_/ \___/|_|_| diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt deleted file mode 100644 index 4afc67dea9..0000000000 --- a/src/main/resources/banner.txt +++ /dev/null @@ -1,6 +0,0 @@ - __ __ _ _____ _ - \ \ / / | | / ____| | | - \ \ /\ / / ___ | |__ | | __ ___ __ _ | |_ - \ \/ \/ / / _ \ | '_ \ | | |_ | / _ \ / _' | | __| - \ /\ / | __/ | |_) | | |__| | | (_) | | (_| | | |_ - \/ \/ \___| |_.__/ \_____| \___/ \__,_| \__| diff --git a/src/main/resources/db/container/V1__init.sql b/src/main/resources/db/container/V1__init.sql index b4c3727f7e..76c72ae95c 100644 --- a/src/main/resources/db/container/V1__init.sql +++ b/src/main/resources/db/container/V1__init.sql @@ -2,65 +2,64 @@ -- For the normal WebGoat server there is a bean which already provided the schema (and creates it see DatabaseInitialization) CREATE SCHEMA IF NOT EXISTS CONTAINER; -CREATE SEQUENCE CONTAINER.HIBERNATE_SEQUENCE; - -CREATE TABLE CONTAINER.ASSIGNMENT ( - ID BIGINT NOT NULL PRIMARY KEY, - NAME VARCHAR(255), - PATH VARCHAR(255) -); - -CREATE TABLE CONTAINER.LESSON_TRACKER( - ID BIGINT NOT NULL PRIMARY KEY, - LESSON_NAME VARCHAR(255), - NUMBER_OF_ATTEMPTS INTEGER NOT NULL +create + table CONTAINER.assignment +( + solved boolean not null, + id bigint generated by default as identity (start with 1), + name varchar(255), + path varchar(255), + primary key (id) ); - -CREATE TABLE CONTAINER.LESSON_TRACKER_ALL_ASSIGNMENTS( - LESSON_TRACKER_ID BIGINT NOT NULL, - ALL_ASSIGNMENTS_ID BIGINT NOT NULL, - PRIMARY KEY(LESSON_TRACKER_ID,ALL_ASSIGNMENTS_ID), - CONSTRAINT FKNHIDKE27BCJHI8C7WJ9QW6Y3Q FOREIGN KEY(ALL_ASSIGNMENTS_ID) REFERENCES CONTAINER.ASSIGNMENT(ID), - CONSTRAINT FKBM51QSDJ7N17O2DNATGAMW7D FOREIGN KEY(LESSON_TRACKER_ID) REFERENCES CONTAINER.LESSON_TRACKER(ID), - CONSTRAINT UK_SYGJY2S8O8DDGA2K5YHBMUVEA UNIQUE(ALL_ASSIGNMENTS_ID) +create table CONTAINER.lesson_progress +( + number_of_attempts integer not null, + version integer, + id bigint generated by default as identity (start with 1), + lesson_name varchar(255), + primary key (id) ); - -CREATE TABLE CONTAINER.LESSON_TRACKER_SOLVED_ASSIGNMENTS( - LESSON_TRACKER_ID BIGINT NOT NULL, - SOLVED_ASSIGNMENTS_ID BIGINT NOT NULL, - PRIMARY KEY(LESSON_TRACKER_ID,SOLVED_ASSIGNMENTS_ID), - CONSTRAINT FKPP850U1MG09YKKL2EQGM0TRJK FOREIGN KEY(SOLVED_ASSIGNMENTS_ID) REFERENCES CONTAINER.ASSIGNMENT(ID), - CONSTRAINT FKNKRWGA1UHLOQ6732SQXHXXSCR FOREIGN KEY(LESSON_TRACKER_ID) REFERENCES CONTAINER.LESSON_TRACKER(ID), - CONSTRAINT UK_9WFYDUY3TVE1XD05LWOUEG0C1 UNIQUE(SOLVED_ASSIGNMENTS_ID) +create table CONTAINER.lesson_progress_assignments +( + assignments_id bigint not null unique, + lesson_progress_id bigint not null, + primary key (assignments_id, lesson_progress_id) ); - -CREATE TABLE CONTAINER.USER_TRACKER( - ID BIGINT NOT NULL PRIMARY KEY, - USERNAME VARCHAR(255) +create table CONTAINER.user_progress +( + id bigint generated by default as identity (start with 1), + username varchar(255), + primary key (id) ); - -CREATE TABLE CONTAINER.USER_TRACKER_LESSON_TRACKERS( - USER_TRACKER_ID BIGINT NOT NULL, - LESSON_TRACKERS_ID BIGINT NOT NULL, - PRIMARY KEY(USER_TRACKER_ID,LESSON_TRACKERS_ID), - CONSTRAINT FKQJSTCA3YND3OHP35D50PNUH3H FOREIGN KEY(LESSON_TRACKERS_ID) REFERENCES CONTAINER.LESSON_TRACKER(ID), - CONSTRAINT FKC9GX8INK7LRC79XC77O2MN9KE FOREIGN KEY(USER_TRACKER_ID) REFERENCES CONTAINER.USER_TRACKER(ID), - CONSTRAINT UK_5D8N5I3IC26CVF7DF7N95DOJB UNIQUE(LESSON_TRACKERS_ID) +create table CONTAINER.user_progress_lesson_progress +( + lesson_progress_id bigint not null unique, + user_progress_id bigint not null, + primary key (lesson_progress_id, user_progress_id) ); - -CREATE TABLE CONTAINER.WEB_GOAT_USER( - USERNAME VARCHAR(255) NOT NULL PRIMARY KEY, - PASSWORD VARCHAR(255), - ROLE VARCHAR(255) +create table CONTAINER.web_goat_user +( + password varchar(255), + role varchar(255), + username varchar(255) not null, + primary key (username) ); -CREATE TABLE CONTAINER.EMAIL( - ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY, - CONTENTS VARCHAR(1024), - RECIPIENT VARCHAR(255), - SENDER VARCHAR(255), - TIME TIMESTAMP, - TITLE VARCHAR(255) +create table CONTAINER.email +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL PRIMARY KEY, + contents VARCHAR(1024), + recipient VARCHAR(255), + sender VARCHAR(255), + time TIMESTAMP, + title VARCHAR(255) ); -ALTER TABLE CONTAINER.EMAIL ALTER COLUMN ID RESTART WITH 2; \ No newline at end of file +alter table CONTAINER.lesson_progress_assignments + add constraint FKbd9xavuwr1rxbcqhcu3jckyro foreign key (assignments_id) references CONTAINER.assignment; +alter table CONTAINER.lesson_progress_assignments + add constraint FKl8vg2qfqhmsnt18qqcyydq7iu foreign key (lesson_progress_id) references CONTAINER.lesson_progress; +alter table CONTAINER.user_progress_lesson_progress + add constraint FKkk5vk79v4q48xb5apeq0g5t2q foreign key (lesson_progress_id) references CONTAINER.lesson_progress; +alter table CONTAINER.user_progress_lesson_progress + add constraint FKkw1rtg14shtginbfflbglbf4m foreign key (user_progress_id) references CONTAINER.user_progress; diff --git a/src/main/resources/db/container/V2__version.sql b/src/main/resources/db/container/V2__version.sql deleted file mode 100644 index 3d7a8908ae..0000000000 --- a/src/main/resources/db/container/V2__version.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE CONTAINER.LESSON_TRACKER ADD VERSION INTEGER; diff --git a/src/main/resources/db/container/V3__id.sql b/src/main/resources/db/container/V3__id.sql deleted file mode 100644 index 2787eed56e..0000000000 --- a/src/main/resources/db/container/V3__id.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE CONTAINER.ASSIGNMENT ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); -ALTER TABLE CONTAINER.LESSON_TRACKER ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); -ALTER TABLE CONTAINER.USER_TRACKER ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); - diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 5e86bade23..08f691a9f1 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -48,12 +48,13 @@ report.card=Report card about=About WebGoat contact=Contact Us show.hints=Show hints +hide.hints=Hide hints lesson.overview=Lesson overview reset.lesson=Reset lesson sign.in=Sign in register.new=or register yourself as a new user sign.up=Sign up -register.title=Register +register.title=Register searchmenu=Search lesson @@ -66,7 +67,7 @@ security.enabled=Security enabled, you can try the previous challenges and see t security.disabled=Security enabled, you can try the previous challenges and see the effect! termsofuse=Terms of use register.condition.1=While running this program your machine will be extremely vulnerable to attack.\ - You should disconnect from the Internet while using this program. WebGoat's default configuration binds to localhost to minimize the exposure. + You should disconnect from the Internet while using this program. WebGoat's default configuration binds to localhost to minimize the exposure. register.condition.2=This program is for educational purposes only. If you attempt \ these techniques without authorization, you are very likely to get caught. If \ you are caught engaging in unauthorized hacking, most companies will fire you. \ diff --git a/src/main/resources/i18n/messages_de.properties b/src/main/resources/i18n/messages_de.properties index 13a8b1f2f9..21b33cad59 100644 --- a/src/main/resources/i18n/messages_de.properties +++ b/src/main/resources/i18n/messages_de.properties @@ -37,5 +37,4 @@ username=Benutzername password=Passwort password.confirm=Wiederhohl Passwort sign.up=Anmelden -register.title=Registrieren - +register.title=Registrieren diff --git a/src/main/resources/i18n/messages_nl.properties b/src/main/resources/i18n/messages_nl.properties index 2f4dff687f..85c78181b1 100644 --- a/src/main/resources/i18n/messages_nl.properties +++ b/src/main/resources/i18n/messages_nl.properties @@ -59,4 +59,4 @@ register.condition.1=Wanneer u WebGoat runt op uw computer, bent u kwetsbaar voo Zorg dat u geen verbinding heeft met internet en dat toegang tot WebGoat alleen lokaal mogelijk is om het aanvalsoppervlak te verkleinen. register.condition.2=WebGoat is bedoeld als educatieve applicatie op het gebied van secure software development. \ Gebruik wat u leert om applicaties beter te maken en niet om zonder toestemming applicaties te schaden. \ - In dat laatste geval loopt u risico op rechtsvervoling en ontslag. + In dat laatste geval loopt u risico op rechtsvervoling en ontslag. diff --git a/src/main/resources/i18n/messages_ru.properties b/src/main/resources/i18n/messages_ru.properties deleted file mode 100644 index e24c2b8f4b..0000000000 --- a/src/main/resources/i18n/messages_ru.properties +++ /dev/null @@ -1,32 +0,0 @@ -# -# This file is part of WebGoat, an Open Web Application Security Project utility. For details, -# please see http://www.owasp.org/ -#

-# Copyright (c) 2002 - 2017 Bruce Mayhew -#

-# This program is free software; you can redistribute it and/or modify it under the terms of the -# GNU General Public License as published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -#

-# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -#

-# You should have received a copy of the GNU General Public License along with this program; if -# not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -# 02111-1307, USA. -#

-# Getting Source ============== -#

-# Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software -# projects. -#

-# - -#General -lesson.completed=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a. -RestartLesson=\u041d\u0430\u0447\u0430\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 -SolutionVideos=\u0412\u0438\u0434\u0435\u043e \u0441 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c -ErrorGenerating=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 -InvalidData=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 -Go!=\u0412\u043f\u0435\u0440\u0451\u0434! diff --git a/src/main/resources/lessons/authbypass/html/AuthBypass.html b/src/main/resources/lessons/authbypass/html/AuthBypass.html index 2fdeeb8268..3fe3326194 100644 --- a/src/main/resources/lessons/authbypass/html/AuthBypass.html +++ b/src/main/resources/lessons/authbypass/html/AuthBypass.html @@ -23,7 +23,7 @@

+ th:action="@{/auth-bypass/verify-account}">

Verify Your Account by answering the questions below:

What is the name of your favorite teacher?

@@ -43,7 +43,7 @@

Please provide a new password for your account

diff --git a/src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties b/src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties index 31c590b3b1..0fe57505da 100644 --- a/src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/authbypass/i18n/WebGoatLabels.properties @@ -8,4 +8,3 @@ auth-bypass.hints.verify.1=The attack on this is similar to the story referenced auth-bypass.hints.verify.2=You do want to tamper the security question parameters, but not delete them auth-bypass.hints.verify.3=The logic to verify the account does expect 2 security questions to be answered, but there is a flaw in the implementation auth-bypass.hints.verify.4=Have you tried renaming the secQuestion0 and secQuestion1 parameters? - diff --git a/src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css b/src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css index 4572fa24aa..231eb5bbf3 100644 --- a/src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css +++ b/src/main/resources/lessons/bypassrestrictions/css/bypass-restrictions.css @@ -2,4 +2,4 @@ position: relative; padding: 7px; margin-top: 7px; -} \ No newline at end of file +} diff --git a/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc index edc411eda8..dc72a0aba3 100755 --- a/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc +++ b/src/main/resources/lessons/bypassrestrictions/documentation/BypassRestrictions_FieldRestrictions.adoc @@ -3,4 +3,4 @@ In most browsers, the client has complete or almost complete control over the HT of the webpage. They can alter values or restrictions to fit their preference. === Task -Send a request that bypasses restrictions of all four of these fields. +Send a request that bypasses restrictions of all five of these fields. diff --git a/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html index d1c8d30016..e947734e2e 100755 --- a/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html +++ b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html @@ -18,7 +18,7 @@
+ th:action="@{/BypassRestrictions/FieldRestrictions}">
Select field with two possible value
diff --git a/src/main/resources/lessons/challenges/css/challenge8.css b/src/main/resources/lessons/challenges/css/challenge8.css index b3e74d70d2..435d4d2099 100644 --- a/src/main/resources/lessons/challenges/css/challenge8.css +++ b/src/main/resources/lessons/challenges/css/challenge8.css @@ -40,4 +40,4 @@ } .review-block-description{ font-size:13px; -} \ No newline at end of file +} diff --git a/src/main/resources/lessons/challenges/documentation/Challenge_1.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_1.adoc index 36e25fe5a7..a1e23fd1af 100644 --- a/src/main/resources/lessons/challenges/documentation/Challenge_1.adoc +++ b/src/main/resources/lessons/challenges/documentation/Challenge_1.adoc @@ -1 +1 @@ -The admin forgot where the password is stashed, can you help? \ No newline at end of file +The admin forgot where the password is stashed, can you help? diff --git a/src/main/resources/lessons/challenges/documentation/Challenge_5.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_5.adoc index 0102ca98f2..424334318b 100644 --- a/src/main/resources/lessons/challenges/documentation/Challenge_5.adoc +++ b/src/main/resources/lessons/challenges/documentation/Challenge_5.adoc @@ -1 +1 @@ -Can you login as Larry? \ No newline at end of file +Can you login as Larry? diff --git a/src/main/resources/lessons/challenges/documentation/Challenge_6.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_6.adoc index 1e1e61ac24..2ac09a7471 100644 --- a/src/main/resources/lessons/challenges/documentation/Challenge_6.adoc +++ b/src/main/resources/lessons/challenges/documentation/Challenge_6.adoc @@ -1 +1 @@ -Can you login as Tom? It may be a little harder than it was for Larry. \ No newline at end of file +Can you login as Tom? It may be a little harder than it was for Larry. diff --git a/src/main/resources/lessons/challenges/documentation/Challenge_8.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_8.adoc index 1d51b75c02..0f133cd695 100644 --- a/src/main/resources/lessons/challenges/documentation/Challenge_8.adoc +++ b/src/main/resources/lessons/challenges/documentation/Challenge_8.adoc @@ -1 +1 @@ -Can you still vote? \ No newline at end of file +Can you still vote? diff --git a/src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc b/src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc index d28b46a061..fd2cd92f4a 100644 --- a/src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc +++ b/src/main/resources/lessons/challenges/documentation/Challenge_introduction.adoc @@ -26,4 +26,4 @@ an e-mail. Team WebGoat -image::images/boss.jpg[] \ No newline at end of file +image::images/boss.jpg[] diff --git a/src/main/resources/lessons/challenges/html/Challenge1.html b/src/main/resources/lessons/challenges/html/Challenge1.html index 544dc4f778..03f5f05cfa 100644 --- a/src/main/resources/lessons/challenges/html/Challenge1.html +++ b/src/main/resources/lessons/challenges/html/Challenge1.html @@ -17,7 +17,7 @@
@@ -37,7 +37,7 @@
- +