Compare commits

...

88 Commits

Author SHA1 Message Date
a4f7059051 Merge branch 'release/v8.1.0' 2020-05-23 14:19:03 +02:00
61720b29ea Fix Travis script 2020-05-23 13:40:28 +02:00
75700597e6 Merge branch 'release/v8.1.0' into develop 2020-05-22 15:09:11 +02:00
bd16fedb33 Merge branch 'release/v8.1.0' 2020-05-22 15:08:50 +02:00
39740e069e New release 2020-05-22 14:10:31 +02:00
5739705d8a Process review comments 2020-05-22 10:10:42 +02:00
9b72610510 Extend XXE lesson with more content and add solution description
Remove obsolete images
Add stylesheet items specific for asciidoctor so we can for icons and source numbering
2020-05-22 10:10:42 +02:00
c4a046bd12 Ch1 less default (#814)
* random pincode in challenge1

* unit test fix
2020-05-12 08:49:48 +02:00
f520c3589c flag submission fixed (#812) 2020-05-07 11:04:00 +02:00
832d6432fc fix for JWT green button and WebWolf intro green button and added jwt int tests (#808) 2020-05-07 08:28:45 +02:00
f4838e1233 add int test for acl 2020-05-01 09:15:29 +02:00
70771ee854 added a webwolf template error page with some explanation and updated 2017 to 2020 2020-04-30 10:21:53 +02:00
9dea696c4c added int test for IDOR and fixed green button issue (#801) 2020-04-29 12:12:11 +02:00
2398949396 added ace js for java 2020-04-28 09:33:54 +02:00
57c008a697 Fix reading file, added try/catch and added tests 2020-04-28 09:25:39 +02:00
2614044918 Fix copying of pictures to WebGoat home directory 2020-04-27 13:07:23 +02:00
1aad57ba55 Fix the syntax differences between HSQL and Postgres 2020-04-27 11:45:41 +02:00
54610868fe Fix the syntax differences between HSQL and Postgres 2020-04-27 11:45:41 +02:00
4831338649 Remove explicit HSQLDB property from WebGoat and use the Spring Boot version 2020-04-27 11:45:41 +02:00
3bb7ee46bd Upgrade to Postgres 10 2020-04-27 11:45:41 +02:00
1a9ce15e99 fix typo (hint3 will not be shown) 2020-04-27 10:44:39 +02:00
9063b4137f fix 404 links 2020-04-27 10:44:39 +02:00
d7ae3a4391 fix typo 2020-04-27 10:44:39 +02:00
db66c1dd02 fix number of steps 2020-04-27 10:44:39 +02:00
608728b135 fix asciidoc italic format 2020-04-27 10:44:39 +02:00
88eb4d7b26 ace editor added without all the nonsense around it 2020-04-26 16:45:56 +02:00
58bc94d1f6 fix green buttons 2020-04-22 16:37:00 +02:00
6f532683a1 lessonplan character updates so it also works on Windows Cp125 2020-04-20 12:54:18 +02:00
6b68a12449 Set more conditions for releasing 2020-04-19 15:42:50 +02:00
27bf08ad5c Deploy and release on Java 11 2020-04-19 15:42:50 +02:00
52b66ed506 Java 12 is EOL so no need to support it 2020-04-19 15:42:50 +02:00
a5350060e1 Add dummy extra method with return type AttackResult because every assignment needs at least one such mapping (in the challenges case this is optional but since the challenges are an extra thing and this is the only assignment which has no such method adding a dummy method makes sense) 2020-04-19 15:42:50 +02:00
4f649234a9 Fix Java 11 issue where the order of methods returned in AssignmentEndpoint subclasses returned wrong method for determining the mapping of an assignment. Now we walk over all methods until we find one which has for example a @GetMapping with AttackResult or ResponseEntity<AttackResult as return type. If no such method is found an exception is thrown 2020-04-19 15:42:50 +02:00
96412da04e Remove unused imports and parameters 2020-04-19 15:42:50 +02:00
0015394582 Fix typo 2020-04-19 15:42:50 +02:00
9cb63a7c43 Update to latest surefire plugin otherwise new JUnit 5 test fails 2020-04-19 15:42:50 +02:00
561fb1f7f4 Build matrix for building 2020-04-19 15:42:50 +02:00
3b7481c2a7 Update method signature 2020-04-19 15:42:50 +02:00
f1768bd9a5 small update 2020-04-19 15:42:50 +02:00
407e19638f Add two more assignments for SQL injection where only filtering is applied. 2020-04-19 15:42:50 +02:00
122cc323f2 Changed the order of explanation of setting up ZAP/Burp a bit (feedback from workshop). This makes the necessary steps more explicit by moving all extra configuration for https etc to the back. So when you follow the lesson you will only setup the minimal and not get confused about things which are only necessary in certain cases 2020-04-19 15:42:50 +02:00
9509993a8f all tests complete for Password Reset (#785) 2020-04-17 15:54:24 +02:00
25e66ae412 use of script console in stead of browser address bar 2020-04-17 15:33:26 +02:00
089952e9ad quiz fix for CIA, SQL Injection Advanced and XSS + XSS description
change in alert(document.cookie)
2020-04-17 15:33:26 +02:00
efc5a870a0 Path traversal windows unittest fix (#780)
* fixes to support windows and linux/unix/mac

* fix in matcher
2020-04-14 16:13:43 +02:00
0638cae6e5 corrected hints and improved error handling base64 (#781) 2020-04-14 16:13:25 +02:00
b8abc99faf fix for scoreboard after js refactoring 2020-04-08 12:05:01 +02:00
e921fb66a9 actual working version of vulnerable components part 5 2020-04-08 12:05:01 +02:00
e25f7a7560 clean up and update js 2020-04-08 12:05:01 +02:00
c4ae9ae2ab migrate to JUnit 5 code 2020-04-06 16:02:15 +02:00
c4153ecbfb Maven owasp dep update (#776)
* add pmd and owasp dependency check through -P owasp profile

* suppress full stack trace in log

* revert to spring 2.2.0 as 2.2.4 failed in travis

* added owasp dependency check maven configuration details to vulenerable
lesson page 7
2020-04-06 16:01:09 +02:00
bb6d06713f Fix failing test 2020-03-10 08:03:48 +01:00
14022d88c9 Last assignment now filters out .. and / so encoding plays a role now 2020-03-10 08:03:48 +01:00
d4966b5e71 Fix test cases 2020-03-10 08:03:48 +01:00
b3840e60e3 Fix lessons 2020-03-10 08:03:48 +01:00
3ece45b3d4 Fix for not passing the content-type 2020-03-10 08:03:48 +01:00
6b7678fb1d Remove old files 2020-03-10 08:03:48 +01:00
6c25cf8e43 Add path traversal lesson 2020-03-10 08:03:48 +01:00
c4c28f544f Fixed CSRF broken links. 2020-03-06 17:15:10 +01:00
3b050a856a tested solution with unit test and verfied with lesson 5 on ie 2020-02-28 23:11:29 +01:00
71d9c4b61a first steps 2020-02-28 23:11:29 +01:00
a8118a14cd add support for status 403 feedback from e.g. ModSecurity/CRS 2020-02-28 23:06:42 +01:00
5f3dff4921 added notes on salted hash (#758) 2020-02-27 07:20:58 +01:00
208aa42fdb relax detection regex (#757)
Allow for content before and after the script; Allow optional semicolon
2020-02-20 20:00:07 +01:00
cd3fb8040f Typo and grammar corrections for the crypto lessons (#756)
* Correct typos and grammar errors.

* Revert one grammar change
2020-02-09 08:00:08 +01:00
9d5fa6f4ef Correct typos and clarify language in signing.adoc (#754)
Some of the changes correct simple misspellings. Some are intended to clarify or simplify the language.
2020-01-30 14:01:42 +01:00
6797033a09 restored pom removal (#753) 2020-01-25 18:18:06 +01:00
9eee726eb5 All in one docker (#749)
* all-in-one Dockerfile preparations

* some cleanup

* add to main pom and add links in index.html

* updated deploy script from build pipeline

* additional line feed just in case
2020-01-25 17:54:24 +01:00
4e371b63d0 suppressing some useless log messages and banners in unit tests (#752)
* suppressing some useless log messages and banners in unit tests

* some more log suppressed
2020-01-25 12:11:45 +01:00
edd6b7d7cf Reset lesson bug (#741)
* Remove old code from UI

* Remove old code

* Remove old functions

* Remove unnecessary divs

* Remove logging to console

* Clear lesson messages (checkmark, output text etc) when lesson resets
2020-01-05 20:22:50 +01:00
5de82c0a06 Fix link to XStream blog which no longer exists (#740) 2020-01-05 19:48:40 +01:00
71f2d2968f Fix NPE when request does not contain parameter (#739) 2020-01-05 15:14:53 +01:00
0d7daf60d9 Fix broken e-mail link (#738) 2020-01-05 15:05:51 +01:00
bb80e11665 dockerfile and compose changes (#737)
* dockerfile and compose changes

* adjusted link
2019-12-27 20:32:35 +01:00
8088465652 Move and remove unneccessary pom dependencies (#736) 2019-12-24 16:14:36 +01:00
035c8662d4 Revert "Bump xstream from 1.4.5 to 1.4.6 in /webgoat-lessons"
This reverts commit a831d949b2.
2019-12-23 17:14:20 +01:00
a831d949b2 Bump xstream from 1.4.5 to 1.4.6 in /webgoat-lessons
Bumps xstream from 1.4.5 to 1.4.6.

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-23 17:12:31 +01:00
4c45a1e68c This lesson is intended to show the dangers of outdated software. However in version 1.4.7 the vulnerability is fixed! In 1.4.5 it is still present, so I suggest this downgrade. It is tested and works as intended, just as 1.4.7 does not. 2019-12-23 17:09:46 +01:00
f79ad452d2 password reset support for using www.webwolf.local 2019-12-23 17:08:33 +01:00
59076fc9ef adjusted WebWolfMacro 2019-12-23 17:08:33 +01:00
b6aa677594 Zap 8 update for proxy lesson (#718)
* additional steps in proxy setup added

* lessons checked

* added page on https proxy and burp proxy
2019-12-10 12:14:21 +01:00
681a20a7c3 In the migration to Spring 2, this method lost its get mapping to the IDOR/profile url,breaking the javascript call to that address. (#720)
thanks!
2019-12-04 12:21:19 +01:00
c5ec2d40a1 updates docker image name (#717) 2019-11-26 18:12:06 +01:00
b5e5dd1d13 Crypto lesson (#712)
* crypto lesson added

* signing assignment

* integration test added for signing assignment

* added more hints

* corrections after rebase

* added some explanation

* added security defaults assignment
2019-11-23 21:52:14 +01:00
9c0b7f8233 Fix version substitution so WebGot home directory contains version number instead of @project.version@ in the name (#710) 2019-11-17 14:33:24 +01:00
5dd6b31905 Adjust lesson template (#704)
* Remove method `getId()` from all lessons as it defaults to the class name

* remove clean up endpoint

* remove unused class `RequestParameter`

* remove unused class `PluginLoadingFailure`

* Move `CourseConfiguration` to lesson package

* Add more content around the lesson template lesson and make it visible as a lesson in WebGoat

* Remove explicit invocation `trackProgress()` inside WebGoat framework so assignments only need to return an `AttackResult`

* Put original solution back as well for SQL string injection

* review comments

* Add
2019-11-17 13:39:56 +01:00
f40b6ffd31 Moving back to snapshot 2019-11-13 12:27:26 +01:00
7313fc6c08 Merge branch 'release/v8.0.0.M26' into develop 2019-11-12 09:33:05 +01:00
657 changed files with 10924 additions and 76118 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@
/.externalToolBuilders/
.project
*/target/*
*.pmd
mongo-data/*
.classpath
.idea/

View File

@ -3,11 +3,14 @@ services:
language: java
jdk:
- openjdk11
- openjdk13
install: "/bin/true"
script:
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
- export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH;
else echo $TRAVIS_PULL_REQUEST_BRANCH; fi)
- echo "TRAVIS_BRANCH=$TRAVIS_BRANCH, PR=$PR, BRANCH=$BRANCH"
- if [ ! -z "${TRAVIS_TAG}" ]; then mvn versions:set -DnewVersion=${TRAVIS_TAG:1}; fi
- if [ ! -z "${TRAVIS_TAG}" ]; then mvn versions:set -DnewVersion=${TRAVIS_TAG:1};
fi
- mvn clean install -q
cache:
directories:
@ -23,21 +26,20 @@ before_deploy:
- ls $WEBGOAT_ARTIFACTS_FOLDER
deploy:
- provider: script
jdk: openjdk11
skip_cleanup: true
script: bash scripts/deploy-webgoat.sh
on:
repo: WebGoat/WebGoat
tags: true
- provider: releases
jdk: openjdk11
skip_cleanup: true
overwrite: true
api_key:
#api-key from webgoat-github user
secure: pJOLBnl6427PcVg/tVy/qB18JC7b8cKpffau+IP0pjdSt7KUfBdBY3QuJ7mrM65zRoVILzggLckaew2PlRmYQRdumyWlyRn44XiJ9KO4n6Bsufbz+ictB4ggtozpp9+I9IIUh1TmqypL9lhkX2ONM9dSHmyblYpAAgMuYSK8FYc=
file_glob: true
file: $WEBGOAT_ARTIFACTS_FOLDER/*
file: "$WEBGOAT_ARTIFACTS_FOLDER/*"
on:
repo: WebGoat/WebGoat
tags: true
env:
global:
@ -45,3 +47,7 @@ env:
- secure: XgPc0UKRTUI70I4YWNQpThPPWeQIxkmzh1GNoR/SSDC2GPIBq3EfkkbSQewqil8stTy+S1/xSzc0JXG8NTn7UOxHVHA/2nhI6jX9E+DKtXQ89YwmaDNQjkbMjziAtDCIex+5TRykxNfkxj6VPYbDssrzI7iJXOIZVj/HoyO3O5E=
#Docker password
- secure: aly5TKBUK9sIiqtMbytNNPZHQhC0a7Yond5tEtuJ8fO+j/KZB4Uro3I6BhzYjGWFb5Kndd0j2TXHPFvtOl402J1CmFsY3v0BhilQd0g6zOssp5T0A73m8Jgq4ItV8wQJJy2bQsXqL1B+uFYieYPiMchj7JxWW0vBn7TV5b68l6U=
notifications:
slack:
rooms:
secure: cDG2URRy7SEipMLyhodwjRBtsPBmfngFB4FyNaIhhr+2/SGyKvGhfW75YA9V+eC7J40KllxQhiIvrxngKDRABb3L1O72Sdj8mZSi8TVsUNLOdamJXHKGUwNSPWXv/1s2m+uC20cgxl66o31vxdV33uvxLdvGOd5e5qOKTsKP7UE=

19
COPYRIGHT.txt Normal file
View File

@ -0,0 +1,19 @@
This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
Copyright (c) 2002 - $today.year Bruce Mayhew
This program is free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if
not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
Getting Source ==============
Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.

View File

@ -5,7 +5,11 @@
For WebGoat we use milestone releases first before we release the official version, we use `v8.0.0.M3` while tagging
and 8.0.0.M3 in the `pom.xml`. When we create the final release we remove the milestone release and use
`v8.0.0` and 8.0.0 in the `pom.xml`
`v8.0.0` in the `pom.xml`
### Release notes:
Update the release notes with the correct version. Use `git shortlog -s -n --since "SEP 31 2019"` for the list of
committers.
At the moment we use Gitflow, for a release you create a new release branch and take the following steps:

View File

@ -37,7 +37,7 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel
java -jar webgoat-server-8.0.0.VERSION.jar [--server.port=8080] [--server.address=localhost]
```
The latest version of WebGoat needs Java 11. By default WebGoat starts on port 8080 with `--server.port` you can specify a different port. With `server.address` you
The latest version of WebGoat needs Java 11 or above. By default WebGoat starts on port 8080 with `--server.port` you can specify a different port. With `server.address` you
can bind it to a different address (default localhost)
@ -45,19 +45,45 @@ can bind it to a different address (default localhost)
Every release is also published on [DockerHub]((https://hub.docker.com/r/webgoat/webgoat-8.0/)).
### Using docker-compose
### Using docker run
The easiest way to start WebGoat as a Docker container is to use the `docker-compose.yml` [file](https://raw.githubusercontent.com/WebGoat/WebGoat/develop/docker-compose.yml)
from our Github repository. This will start both containers and it also takes care of setting up the
connection between WebGoat and WebWolf.
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
curl https://raw.githubusercontent.com/WebGoat/WebGoat/develop/docker-compose.yml | docker-compose -f - up
docker run -d -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf
```
**Important**: the current directory on your host will be mapped into the container for keeping state.
WebGoat will be located at: http://127.0.0.1:8080/WebGoat
WebWolf will be located at: http://127.0.0.1:9090/WebWolf
Using the `docker-compose` file will simplify getting WebGoat and WebWolf up and running.
**Important**: Choose the correct timezone, so that the docker container and your host are in the same timezone. As it important for the validity of JWT tokens used in certain exercises.
### Using docker stack deploy
Another way to deply WebGoat and WebWolf in a more advanced way is to use a compose-file in a docker stack deploy.
You can define which containers should run in which combinations and define all of this in a yaml file.
An example of such a file is: [goat-with-reverseproxy.yaml](goat-with-reverseproxy.yaml)
This sets up an nginx webserver as reverse proxy to WebGoat and WebWolf. You can change the timezone by adjusting the value in the yaml file.
```shell
docker stack init
docker stack deploy --compose-file goat-with-reverseproxy.yaml webgoatdemo
```
Add the following entries in your local hosts file:
```shell
127.0.0.1 www.webgoat.local www.webwolf.localhost
```
You can use the overall start page: http://www.webgoat.local or:
WebGoat will be located at: http://www.webgoat.local/WebGoat
WebWolf will be located at: http://www.webwolf.local/WebWolf
**Important**: the current directory on your host will be mapped into the container for keeping state.
## 3. Run from the sources

50
RELEASE_NOTES.md Normal file
View File

@ -0,0 +1,50 @@
# WebGoat release notes
## Version 8.1.0
### New functionality
- Added new lessons for cryptography and path-traversal
- Extra content added to the XXE lesson
- Explanation of the assignments will be part of WebGoat, in this release we added detailed descriptions on how to solve the XXE lesson. In the upcoming releases new explanations will be added. If you want to contribute please create a pull request on Github.
- Docker improvements + docker stack for complete container with nginx
- Included JWT token decoding and generation, since jwt.io does not support None anymore
### Bug fixes
- [#743 - Character encoding errors](https://github.com/WebGoat/WebGoat/issues/743)
- [#811 - Flag submission fails](https://github.com/WebGoat/WebGoat/issues/811)
- [#810 - Scoreboard for challenges shows csrf users](https://github.com/WebGoat/WebGoat/issues/810)
- [#788 - strange copy in constructor](https://github.com/WebGoat/WebGoat/issues/788)
- [#760 - Execution of standalone jar fails (Flyway migration step](https://github.com/WebGoat/WebGoat/issues/760)
- [#766 - Unclear objective of vulnerable components practical assignment](https://github.com/WebGoat/WebGoat/issues/766)
- [#708 - Seems like the home directory of WebGoat always use @project.version@](https://github.com/WebGoat/WebGoat/issues/708)
- [#719 - WebGoat: 'Contact Us' email link in header is not correctly set](https://github.com/WebGoat/WebGoat/issues/719)
- [#715 - Reset lesson doesn't reset the "HTML lesson" => forms stay succesful](https://github.com/WebGoat/WebGoat/issues/715)
- [#725 - Vulnerable Components lesson 12 broken due to too new dependency](https://github.com/WebGoat/WebGoat/issues/725)
- [#716 - On M26 @project.version@ is not "interpreted" #7](https://github.com/WebGoat/WebGoat/issues/716)
- [#721 couldn't be able to run CSRF lesson 3: Receive Whitelabel Error Page](https://github.com/WebGoat/WebGoat/issues/721)
- [#724 - Dead link in VulnerableComponents lesson 11](https://github.com/WebGoat/WebGoat/issues/724)
## Contributors
Special thanks to the following contributors providing us with a pull request:
- Satoshi SAKAO
- Philippe Lafoucrière
- Cotonne
- Tiago Mussi
- thegoodcrumpets
- Atharva Vaidya
- torleif
- August Detlefsen
- Choe Hyeong Jin
And everyone who provided feedback through Github.
Team WebGoat

View File

@ -11,7 +11,7 @@ services:
- spring.datasource.username=webgoat
- spring.datasource.password=webgoat
- spring.datasource.driver-class-name=org.postgresql.Driver
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect
- webgoat.server.directory=/home/webgoat/.webgoat/
- webgoat.user.directory=/home/webgoat/.webgoat/
ports:
@ -23,11 +23,11 @@ services:
- spring.datasource.username=webgoat
- spring.datasource.password=webgoat
- spring.datasource.driver-class-name=org.postgresql.Driver
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL94Dialect
- spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect
ports:
- "9090:9090"
webgoat_db:
image: postgres:9.4
image: postgres:10.12
# Uncomment to store the state of the database on the host.
# volumes:
# - ./database:/var/lib/postgresql

View File

@ -1,4 +1,4 @@
version: '2.1'
version: '3'
services:
webgoat:
@ -6,13 +6,17 @@ services:
environment:
- WEBWOLF_HOST=webwolf
- WEBWOLF_PORT=9090
- TZ=Europe/Amsterdam
ports:
- "8080:8080"
- "9001:9001"
volumes:
- .:/home/webgoat/.webgoat
working_dir: /home/webgoat
webwolf:
image: webgoat/webwolf
ports:
- "9090:9090"
command: --spring.datasource.url=jdbc:hsqldb:hsql://webgoat:9001/webgoat --server.address=0.0.0.0
depends_on:
- webgoat

1
docker/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.jar

30
docker/Dockerfile Normal file
View File

@ -0,0 +1,30 @@
FROM openjdk:11.0.1-jre-slim-stretch
ARG webgoat_version=v8.0.0-SNAPSHOT
ENV webgoat_version_env=${webgoat_version}
RUN apt-get update && apt-get install
RUN useradd --home-dir /home/webgoat --create-home -U webgoat
RUN apt-get -y install apt-utils nginx
USER webgoat
RUN cd /home/webgoat/; mkdir -p .webgoat-${webgoat_version}
COPY nginx.conf /etc/nginx/nginx.conf
COPY index.html /usr/share/nginx/html/
COPY webgoat-server-${webgoat_version}.jar /home/webgoat/webgoat.jar
COPY webwolf-${webgoat_version}.jar /home/webgoat/webwolf.jar
COPY start.sh /home/webgoat
EXPOSE 8080
EXPOSE 9090
ENV WEBGOAT_PORT 8080
ENV WEBGOAT_SSLENABLED false
ENV GOATURL https://127.0.0.1:$WEBGOAT_PORT
ENV WOLFURL http://127.0.0.1:9090
WORKDIR /home/webgoat
ENTRYPOINT /bin/bash /home/webgoat/start.sh $webgoat_version_env

9
docker/Readme.md Normal file
View File

@ -0,0 +1,9 @@
# Docker all-in-one image
## Docker build
docker build --no-cache --build-arg webgoat_version=v8.0.0-SNAPSHOT -t webgoat/goatandwolf:latest .
## Docker run
docker run -d -p 80:8888 -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam webgoat/goatandwolf:latest

43
docker/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<body>
<h1>OWASP WebGoat Training tools</h1>
<p>
Use the following links to access the WebGoat and WebWolf applications.
Register a user using WebGoat. The same user can access WebWolf.
</p>
<h2>Use without special host name entries</h2>
<table>
<tr>
<td>WebGoat URL</td>
<td><a href="http://127.0.0.1:8080/WebGoat" target="_blank">http://127.0.0.1:8080/WebGoat</a></td>
</tr>
<tr>
<td>WebWolf URL</td>
<td><a href="http://127.0.0.1:9090/WebWolf" target="_blank">http://127.0.0.1:9090/WebWolf</a></td>
</tr>
<table>
<h2>Use with www.webgoat.local and www.webwolf.local</h2>
<p>
Add the following entries to your local <b><i>hosts</i></b> file on Windows (c:\Windows\System32\drivers\etc\hosts) or Linux (/etc/hosts)
<pre>
127.0.0.1 www.webgoat.local www.webwolf.local
</pre>
Then use the following URL's:
</p>
<table>
<tr>
<td>WebGoat URL</td>
<td><a href="http://www.webgoat.local/WebGoat" target="_blank">http://www.webgoat.local/WebGoat</a></td>
</tr>
<tr>
<td>WebWolf URL</td>
<td><a href="http://www.webwolf.local/WebWolf" target="_blank">http://www.webwolf.local/WebWolf</a></td>
</tr>
<table>
</body>
</html>

140
docker/nginx.conf Normal file
View File

@ -0,0 +1,140 @@
error_log /tmp/error.log;
pid /tmp/nginx.pid;
worker_processes 1;
events { worker_connections 1024; }
http {
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
sendfile on;
upstream docker-webgoat {
server 127.0.0.1:8080;
}
upstream docker-webwolf {
server 127.0.0.1:9090;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
server {
listen 8888;
server_name www.webgoat.local;
root /var/www;
access_log /tmp/goataccess.log;
error_log /tmp/goaterror.log;
location ~* \.(png|jpg|jpeg|gif|ico|woff|otf|ttf|mvc|svg|txt|pdf|docx?|xlsx?)$ {
access_log off;
proxy_pass http://docker-webgoat;
proxy_redirect off;
}
location / {
root /usr/share/nginx/html;
index index.html;
add_header Cache-Control no-cache;
expires 0;
}
location /WebGoat {
proxy_pass http://docker-webgoat;
proxy_redirect off;
}
}
server {
listen 8888;
server_name www.webwolf.local;
root /var/www;
access_log /tmp/wolfaccess.log;
error_log /tmp/wolferror.log;
location /WebGoat/PasswordReset/ForgotPassword/create-password-reset-link {
proxy_pass http://docker-webgoat;
proxy_redirect off;
}
location /PasswordReset/reset/reset-password {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /files {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /tmpdir {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /webjars {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /css {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /login {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /images {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /mail {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /upload {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /js {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /landing {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /logout {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
location /WebWolf {
proxy_pass http://docker-webwolf;
proxy_redirect off;
}
}
}

40
docker/pom.xml Normal file
View File

@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>webgoat-all-in-one-docker</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.1.0</version>
</parent>
<dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>install</phase>
<configuration>
<target>
<copy file="../webgoat-server/target/webgoat-server-${project.version}.jar" tofile="webgoat-server-${project.version}.jar"/>
<copy file="../webwolf/target/webwolf-${project.version}.jar" tofile="webwolf-${project.version}.jar"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

12
docker/start.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/bash
cd /home/webgoat
service nginx start
sleep 1
java -Dfile.encoding=UTF-8 -jar webgoat.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webgoat.log &
sleep 10
java -Dfile.encoding=UTF-8 -jar webwolf.jar --webgoat.build.version=$1 --server.address=0.0.0.0 > webwolf.log &
tail -300f webgoat.log

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -278,10 +278,6 @@
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
<script src="vendor/magnific-popup/jquery.magnific-popup.min.js"></script>
<!-- Contact Form JavaScript -->
<script src="js/jqBootstrapValidation.js"></script>
<script src="js/contact_me.js"></script>
<!-- Custom scripts for this template -->
<script src="js/freelancer.min.js"></script>

View File

@ -1,75 +0,0 @@
$(function() {
$("#contactForm input,#contactForm textarea").jqBootstrapValidation({
preventSubmit: true,
submitError: function($form, event, errors) {
// additional error messages or events
},
submitSuccess: function($form, event) {
event.preventDefault(); // prevent default submit behaviour
// get values from FORM
var name = $("input#name").val();
var email = $("input#email").val();
var phone = $("input#phone").val();
var message = $("textarea#message").val();
var firstName = name; // For Success/Failure Message
// Check for white space in name for Success/Fail message
if (firstName.indexOf(' ') >= 0) {
firstName = name.split(' ').slice(0, -1).join(' ');
}
$this = $("#sendMessageButton");
$this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages
$.ajax({
url: "././mail/contact_me.php",
type: "POST",
data: {
name: name,
phone: phone,
email: email,
message: message
},
cache: false,
success: function() {
// Success message
$('#success').html("<div class='alert alert-success'>");
$('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
.append("</button>");
$('#success > .alert-success')
.append("<strong>Your message has been sent. </strong>");
$('#success > .alert-success')
.append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
error: function() {
// Fail message
$('#success').html("<div class='alert alert-danger'>");
$('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>&times;")
.append("</button>");
$('#success > .alert-danger').append($("<strong>").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
$('#success > .alert-danger').append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
complete: function() {
setTimeout(function() {
$this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
}, 1000);
}
});
},
filter: function() {
return $(this).is(":visible");
},
});
$("a[data-toggle=\"tab\"]").click(function(e) {
e.preventDefault();
$(this).tab("show");
});
});
/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
$('#success').html('');
});

View File

@ -1,912 +0,0 @@
/* jqBootstrapValidation
* A plugin for automating validation on Twitter Bootstrap formatted forms.
*
* v1.3.6
*
* License: MIT <http://opensource.org/licenses/mit-license.php> - see LICENSE file
*
* http://ReactiveRaven.github.com/jqBootstrapValidation/
*/
(function( $ ){
var createdElements = [];
var defaults = {
options: {
prependExistingHelpBlock: false,
sniffHtml: true, // sniff for 'required', 'maxlength', etc
preventSubmit: true, // stop the form submit event from firing if validation fails
submitError: false, // function called if there is an error when trying to submit
submitSuccess: false, // function called just before a successful submit event is sent to the server
semanticallyStrict: false, // set to true to tidy up generated HTML output
autoAdd: {
helpBlocks: true
},
filter: function () {
// return $(this).is(":visible"); // only validate elements you can see
return true; // validate everything
}
},
methods: {
init : function( options ) {
var settings = $.extend(true, {}, defaults);
settings.options = $.extend(true, settings.options, options);
var $siblingElements = this;
var uniqueForms = $.unique(
$siblingElements.map( function () {
return $(this).parents("form")[0];
}).toArray()
);
$(uniqueForms).bind("submit", function (e) {
var $form = $(this);
var warningsFound = 0;
var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter);
$inputs.trigger("submit.validation").trigger("validationLostFocus.validation");
$inputs.each(function (i, el) {
var $this = $(el),
$controlGroup = $this.parents(".control-group").first();
if (
$controlGroup.hasClass("warning")
) {
$controlGroup.removeClass("warning").addClass("error");
warningsFound++;
}
});
$inputs.trigger("validationLostFocus.validation");
if (warningsFound) {
if (settings.options.preventSubmit) {
e.preventDefault();
}
$form.addClass("error");
if ($.isFunction(settings.options.submitError)) {
settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true));
}
} else {
$form.removeClass("error");
if ($.isFunction(settings.options.submitSuccess)) {
settings.options.submitSuccess($form, e);
}
}
});
return this.each(function(){
// Get references to everything we're interested in
var $this = $(this),
$controlGroup = $this.parents(".control-group").first(),
$helpBlock = $controlGroup.find(".help-block").first(),
$form = $this.parents("form").first(),
validatorNames = [];
// create message container if not exists
if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) {
$helpBlock = $('<div class="help-block" />');
$controlGroup.find('.controls').append($helpBlock);
createdElements.push($helpBlock[0]);
}
// =============================================================
// SNIFF HTML FOR VALIDATORS
// =============================================================
// *snort sniff snuffle*
if (settings.options.sniffHtml) {
var message = "";
// ---------------------------------------------------------
// PATTERN
// ---------------------------------------------------------
if ($this.attr("pattern") !== undefined) {
message = "Not in the expected format<!-- data-validation-pattern-message to override -->";
if ($this.data("validationPatternMessage")) {
message = $this.data("validationPatternMessage");
}
$this.data("validationPatternMessage", message);
$this.data("validationPatternRegex", $this.attr("pattern"));
}
// ---------------------------------------------------------
// MAX
// ---------------------------------------------------------
if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) {
var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax"));
message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->";
if ($this.data("validationMaxMessage")) {
message = $this.data("validationMaxMessage");
}
$this.data("validationMaxMessage", message);
$this.data("validationMaxMax", max);
}
// ---------------------------------------------------------
// MIN
// ---------------------------------------------------------
if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) {
var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin"));
message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->";
if ($this.data("validationMinMessage")) {
message = $this.data("validationMinMessage");
}
$this.data("validationMinMessage", message);
$this.data("validationMinMin", min);
}
// ---------------------------------------------------------
// MAXLENGTH
// ---------------------------------------------------------
if ($this.attr("maxlength") !== undefined) {
message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->";
if ($this.data("validationMaxlengthMessage")) {
message = $this.data("validationMaxlengthMessage");
}
$this.data("validationMaxlengthMessage", message);
$this.data("validationMaxlengthMaxlength", $this.attr("maxlength"));
}
// ---------------------------------------------------------
// MINLENGTH
// ---------------------------------------------------------
if ($this.attr("minlength") !== undefined) {
message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->";
if ($this.data("validationMinlengthMessage")) {
message = $this.data("validationMinlengthMessage");
}
$this.data("validationMinlengthMessage", message);
$this.data("validationMinlengthMinlength", $this.attr("minlength"));
}
// ---------------------------------------------------------
// REQUIRED
// ---------------------------------------------------------
if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) {
message = settings.builtInValidators.required.message;
if ($this.data("validationRequiredMessage")) {
message = $this.data("validationRequiredMessage");
}
$this.data("validationRequiredMessage", message);
}
// ---------------------------------------------------------
// NUMBER
// ---------------------------------------------------------
if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") {
message = settings.builtInValidators.number.message;
if ($this.data("validationNumberMessage")) {
message = $this.data("validationNumberMessage");
}
$this.data("validationNumberMessage", message);
}
// ---------------------------------------------------------
// EMAIL
// ---------------------------------------------------------
if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") {
message = "Not a valid email address<!-- data-validator-validemail-message to override -->";
if ($this.data("validationValidemailMessage")) {
message = $this.data("validationValidemailMessage");
} else if ($this.data("validationEmailMessage")) {
message = $this.data("validationEmailMessage");
}
$this.data("validationValidemailMessage", message);
}
// ---------------------------------------------------------
// MINCHECKED
// ---------------------------------------------------------
if ($this.attr("minchecked") !== undefined) {
message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->";
if ($this.data("validationMincheckedMessage")) {
message = $this.data("validationMincheckedMessage");
}
$this.data("validationMincheckedMessage", message);
$this.data("validationMincheckedMinchecked", $this.attr("minchecked"));
}
// ---------------------------------------------------------
// MAXCHECKED
// ---------------------------------------------------------
if ($this.attr("maxchecked") !== undefined) {
message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->";
if ($this.data("validationMaxcheckedMessage")) {
message = $this.data("validationMaxcheckedMessage");
}
$this.data("validationMaxcheckedMessage", message);
$this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked"));
}
}
// =============================================================
// COLLECT VALIDATOR NAMES
// =============================================================
// Get named validators
if ($this.data("validation") !== undefined) {
validatorNames = $this.data("validation").split(",");
}
// Get extra ones defined on the element's data attributes
$.each($this.data(), function (i, el) {
var parts = i.replace(/([A-Z])/g, ",$1").split(",");
if (parts[0] === "validation" && parts[1]) {
validatorNames.push(parts[1]);
}
});
// =============================================================
// NORMALISE VALIDATOR NAMES
// =============================================================
var validatorNamesToInspect = validatorNames;
var newValidatorNamesToInspect = [];
do // repeatedly expand 'shortcut' validators into their real validators
{
// Uppercase only the first letter of each name
$.each(validatorNames, function (i, el) {
validatorNames[i] = formatValidatorName(el);
});
// Remove duplicate validator names
validatorNames = $.unique(validatorNames);
// Pull out the new validator names from each shortcut
newValidatorNamesToInspect = [];
$.each(validatorNamesToInspect, function(i, el) {
if ($this.data("validation" + el + "Shortcut") !== undefined) {
// Are these custom validators?
// Pull them out!
$.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) {
newValidatorNamesToInspect.push(el2);
});
} else if (settings.builtInValidators[el.toLowerCase()]) {
// Is this a recognised built-in?
// Pull it out!
var validator = settings.builtInValidators[el.toLowerCase()];
if (validator.type.toLowerCase() === "shortcut") {
$.each(validator.shortcut.split(","), function (i, el) {
el = formatValidatorName(el);
newValidatorNamesToInspect.push(el);
validatorNames.push(el);
});
}
}
});
validatorNamesToInspect = newValidatorNamesToInspect;
} while (validatorNamesToInspect.length > 0)
// =============================================================
// SET UP VALIDATOR ARRAYS
// =============================================================
var validators = {};
$.each(validatorNames, function (i, el) {
// Set up the 'override' message
var message = $this.data("validation" + el + "Message");
var hasOverrideMessage = (message !== undefined);
var foundValidator = false;
message =
(
message
? message
: "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->"
)
;
$.each(
settings.validatorTypes,
function (validatorType, validatorTemplate) {
if (validators[validatorType] === undefined) {
validators[validatorType] = [];
}
if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) {
validators[validatorType].push(
$.extend(
true,
{
name: formatValidatorName(validatorTemplate.name),
message: message
},
validatorTemplate.init($this, el)
)
);
foundValidator = true;
}
}
);
if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) {
var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]);
if (hasOverrideMessage) {
validator.message = message;
}
var validatorType = validator.type.toLowerCase();
if (validatorType === "shortcut") {
foundValidator = true;
} else {
$.each(
settings.validatorTypes,
function (validatorTemplateType, validatorTemplate) {
if (validators[validatorTemplateType] === undefined) {
validators[validatorTemplateType] = [];
}
if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) {
$this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]);
validators[validatorType].push(
$.extend(
validator,
validatorTemplate.init($this, el)
)
);
foundValidator = true;
}
}
);
}
}
if (! foundValidator) {
$.error("Cannot find validation info for '" + el + "'");
}
});
// =============================================================
// STORE FALLBACK VALUES
// =============================================================
$helpBlock.data(
"original-contents",
(
$helpBlock.data("original-contents")
? $helpBlock.data("original-contents")
: $helpBlock.html()
)
);
$helpBlock.data(
"original-role",
(
$helpBlock.data("original-role")
? $helpBlock.data("original-role")
: $helpBlock.attr("role")
)
);
$controlGroup.data(
"original-classes",
(
$controlGroup.data("original-clases")
? $controlGroup.data("original-classes")
: $controlGroup.attr("class")
)
);
$this.data(
"original-aria-invalid",
(
$this.data("original-aria-invalid")
? $this.data("original-aria-invalid")
: $this.attr("aria-invalid")
)
);
// =============================================================
// VALIDATION
// =============================================================
$this.bind(
"validation.validation",
function (event, params) {
var value = getValue($this);
// Get a list of the errors to apply
var errorsFound = [];
$.each(validators, function (validatorType, validatorTypeArray) {
if (value || value.length || (params && params.includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) {
$.each(validatorTypeArray, function (i, validator) {
if (settings.validatorTypes[validatorType].validate($this, value, validator)) {
errorsFound.push(validator.message);
}
});
}
});
return errorsFound;
}
);
$this.bind(
"getValidators.validation",
function () {
return validators;
}
);
// =============================================================
// WATCH FOR CHANGES
// =============================================================
$this.bind(
"submit.validation",
function () {
return $this.triggerHandler("change.validation", {submitting: true});
}
);
$this.bind(
[
"keyup",
"focus",
"blur",
"click",
"keydown",
"keypress",
"change"
].join(".validation ") + ".validation",
function (e, params) {
var value = getValue($this);
var errorsFound = [];
$controlGroup.find("input,textarea,select").each(function (i, el) {
var oldCount = errorsFound.length;
$.each($(el).triggerHandler("validation.validation", params), function (j, message) {
errorsFound.push(message);
});
if (errorsFound.length > oldCount) {
$(el).attr("aria-invalid", "true");
} else {
var original = $this.data("original-aria-invalid");
$(el).attr("aria-invalid", (original !== undefined ? original : false));
}
});
$form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation");
errorsFound = $.unique(errorsFound.sort());
// Were there any errors?
if (errorsFound.length) {
// Better flag it up as a warning.
$controlGroup.removeClass("success error").addClass("warning");
// How many errors did we find?
if (settings.options.semanticallyStrict && errorsFound.length === 1) {
// Only one? Being strict? Just output it.
$helpBlock.html(errorsFound[0] +
( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
} else {
// Multiple? Being sloppy? Glue them together into an UL.
$helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" +
( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" ));
}
} else {
$controlGroup.removeClass("warning error success");
if (value.length > 0) {
$controlGroup.addClass("success");
}
$helpBlock.html($helpBlock.data("original-contents"));
}
if (e.type === "blur") {
$controlGroup.removeClass("success");
}
}
);
$this.bind("validationLostFocus.validation", function () {
$controlGroup.removeClass("success");
});
});
},
destroy : function( ) {
return this.each(
function() {
var
$this = $(this),
$controlGroup = $this.parents(".control-group").first(),
$helpBlock = $controlGroup.find(".help-block").first();
// remove our events
$this.unbind('.validation'); // events are namespaced.
// reset help text
$helpBlock.html($helpBlock.data("original-contents"));
// reset classes
$controlGroup.attr("class", $controlGroup.data("original-classes"));
// reset aria
$this.attr("aria-invalid", $this.data("original-aria-invalid"));
// reset role
$helpBlock.attr("role", $this.data("original-role"));
// remove all elements we created
if (createdElements.indexOf($helpBlock[0]) > -1) {
$helpBlock.remove();
}
}
);
},
collectErrors : function(includeEmpty) {
var errorMessages = {};
this.each(function (i, el) {
var $el = $(el);
var name = $el.attr("name");
var errors = $el.triggerHandler("validation.validation", {includeEmpty: true});
errorMessages[name] = $.extend(true, errors, errorMessages[name]);
});
$.each(errorMessages, function (i, el) {
if (el.length === 0) {
delete errorMessages[i];
}
});
return errorMessages;
},
hasErrors: function() {
var errorMessages = [];
this.each(function (i, el) {
errorMessages = errorMessages.concat(
$(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : []
);
});
return (errorMessages.length > 0);
},
override : function (newDefaults) {
defaults = $.extend(true, defaults, newDefaults);
}
},
validatorTypes: {
callback: {
name: "callback",
init: function ($this, name) {
return {
validatorName: name,
callback: $this.data("validation" + name + "Callback"),
lastValue: $this.val(),
lastValid: true,
lastFinished: true
};
},
validate: function ($this, value, validator) {
if (validator.lastValue === value && validator.lastFinished) {
return !validator.lastValid;
}
if (validator.lastFinished === true)
{
validator.lastValue = value;
validator.lastValid = true;
validator.lastFinished = false;
var rrjqbvValidator = validator;
var rrjqbvThis = $this;
executeFunctionByName(
validator.callback,
window,
$this,
value,
function (data) {
if (rrjqbvValidator.lastValue === data.value) {
rrjqbvValidator.lastValid = data.valid;
if (data.message) {
rrjqbvValidator.message = data.message;
}
rrjqbvValidator.lastFinished = true;
rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message);
// Timeout is set to avoid problems with the events being considered 'already fired'
setTimeout(function () {
rrjqbvThis.trigger("change.validation");
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
}
}
);
}
return false;
}
},
ajax: {
name: "ajax",
init: function ($this, name) {
return {
validatorName: name,
url: $this.data("validation" + name + "Ajax"),
lastValue: $this.val(),
lastValid: true,
lastFinished: true
};
},
validate: function ($this, value, validator) {
if (""+validator.lastValue === ""+value && validator.lastFinished === true) {
return validator.lastValid === false;
}
if (validator.lastFinished === true)
{
validator.lastValue = value;
validator.lastValid = true;
validator.lastFinished = false;
$.ajax({
url: validator.url,
data: "value=" + value + "&field=" + $this.attr("name"),
dataType: "json",
success: function (data) {
if (""+validator.lastValue === ""+data.value) {
validator.lastValid = !!(data.valid);
if (data.message) {
validator.message = data.message;
}
validator.lastFinished = true;
$this.data("validation" + validator.validatorName + "Message", validator.message);
// Timeout is set to avoid problems with the events being considered 'already fired'
setTimeout(function () {
$this.trigger("change.validation");
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
}
},
failure: function () {
validator.lastValid = true;
validator.message = "ajax call failed";
validator.lastFinished = true;
$this.data("validation" + validator.validatorName + "Message", validator.message);
// Timeout is set to avoid problems with the events being considered 'already fired'
setTimeout(function () {
$this.trigger("change.validation");
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst
}
});
}
return false;
}
},
regex: {
name: "regex",
init: function ($this, name) {
return {regex: regexFromString($this.data("validation" + name + "Regex"))};
},
validate: function ($this, value, validator) {
return (!validator.regex.test(value) && ! validator.negative)
|| (validator.regex.test(value) && validator.negative);
}
},
required: {
name: "required",
init: function ($this, name) {
return {};
},
validate: function ($this, value, validator) {
return !!(value.length === 0 && ! validator.negative)
|| !!(value.length > 0 && validator.negative);
},
blockSubmit: true
},
match: {
name: "match",
init: function ($this, name) {
var element = $this.parents("form").first().find("[name=\"" + $this.data("validation" + name + "Match") + "\"]").first();
element.bind("validation.validation", function () {
$this.trigger("change.validation", {submitting: true});
});
return {"element": element};
},
validate: function ($this, value, validator) {
return (value !== validator.element.val() && ! validator.negative)
|| (value === validator.element.val() && validator.negative);
},
blockSubmit: true
},
max: {
name: "max",
init: function ($this, name) {
return {max: $this.data("validation" + name + "Max")};
},
validate: function ($this, value, validator) {
return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative)
|| (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative);
}
},
min: {
name: "min",
init: function ($this, name) {
return {min: $this.data("validation" + name + "Min")};
},
validate: function ($this, value, validator) {
return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative)
|| (parseFloat(value) >= parseFloat(validator.min) && validator.negative);
}
},
maxlength: {
name: "maxlength",
init: function ($this, name) {
return {maxlength: $this.data("validation" + name + "Maxlength")};
},
validate: function ($this, value, validator) {
return ((value.length > validator.maxlength) && ! validator.negative)
|| ((value.length <= validator.maxlength) && validator.negative);
}
},
minlength: {
name: "minlength",
init: function ($this, name) {
return {minlength: $this.data("validation" + name + "Minlength")};
},
validate: function ($this, value, validator) {
return ((value.length < validator.minlength) && ! validator.negative)
|| ((value.length >= validator.minlength) && validator.negative);
}
},
maxchecked: {
name: "maxchecked",
init: function ($this, name) {
var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
elements.bind("click.validation", function () {
$this.trigger("change.validation", {includeEmpty: true});
});
return {maxchecked: $this.data("validation" + name + "Maxchecked"), elements: elements};
},
validate: function ($this, value, validator) {
return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative)
|| (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative);
},
blockSubmit: true
},
minchecked: {
name: "minchecked",
init: function ($this, name) {
var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]");
elements.bind("click.validation", function () {
$this.trigger("change.validation", {includeEmpty: true});
});
return {minchecked: $this.data("validation" + name + "Minchecked"), elements: elements};
},
validate: function ($this, value, validator) {
return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative)
|| (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative);
},
blockSubmit: true
}
},
builtInValidators: {
email: {
name: "Email",
type: "shortcut",
shortcut: "validemail"
},
validemail: {
name: "Validemail",
type: "regex",
regex: "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\.[A-Za-z]{2,4}",
message: "Not a valid email address<!-- data-validator-validemail-message to override -->"
},
passwordagain: {
name: "Passwordagain",
type: "match",
match: "password",
message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->"
},
positive: {
name: "Positive",
type: "shortcut",
shortcut: "number,positivenumber"
},
negative: {
name: "Negative",
type: "shortcut",
shortcut: "number,negativenumber"
},
number: {
name: "Number",
type: "regex",
regex: "([+-]?\\\d+(\\\.\\\d*)?([eE][+-]?[0-9]+)?)?",
message: "Must be a number<!-- data-validator-number-message to override -->"
},
integer: {
name: "Integer",
type: "regex",
regex: "[+-]?\\\d+",
message: "No decimal places allowed<!-- data-validator-integer-message to override -->"
},
positivenumber: {
name: "Positivenumber",
type: "min",
min: 0,
message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->"
},
negativenumber: {
name: "Negativenumber",
type: "max",
max: 0,
message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->"
},
required: {
name: "Required",
type: "required",
message: "This is required<!-- data-validator-required-message to override -->"
},
checkone: {
name: "Checkone",
type: "minchecked",
minchecked: 1,
message: "Check at least one option<!-- data-validation-checkone-message to override -->"
}
}
};
var formatValidatorName = function (name) {
return name
.toLowerCase()
.replace(
/(^|\s)([a-z])/g ,
function(m,p1,p2) {
return p1+p2.toUpperCase();
}
)
;
};
var getValue = function ($this) {
// Extract the value we're talking about
var value = $this.val();
var type = $this.attr("type");
if (type === "checkbox") {
value = ($this.is(":checked") ? value : "");
}
if (type === "radio") {
value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : "");
}
return value;
};
function regexFromString(inputstring) {
return new RegExp("^" + inputstring + "$");
}
/**
* Thanks to Jason Bunting via StackOverflow.com
*
* http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910
* Short link: http://tinyurl.com/executeFunctionByName
**/
function executeFunctionByName(functionName, context /*, args*/) {
var args = Array.prototype.slice.call(arguments).splice(2);
var namespaces = functionName.split(".");
var func = namespaces.pop();
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context[func].apply(this, args);
}
$.fn.jqBootstrapValidation = function( method ) {
if ( defaults.methods[method] ) {
return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return defaults.methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' );
return null;
}
};
$.jqBootstrapValidation = function (options) {
$(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments);
};
})( jQuery );

View File

@ -1,22 +0,0 @@
<?php
// Check for empty fields
if(empty($_POST['name']) || empty($_POST['email']) || empty($_POST['phone']) || empty($_POST['message']) || !filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
http_response_code(500);
exit();
}
$name = strip_tags(htmlspecialchars($_POST['name']));
$email = strip_tags(htmlspecialchars($_POST['email']));
$phone = strip_tags(htmlspecialchars($_POST['phone']));
$message = strip_tags(htmlspecialchars($_POST['message']));
// Create the email and send the message
$to = "yourname@yourdomain.com"; // Add your email address inbetween the "" replacing yourname@yourdomain.com - This is where the form will send a message to.
$subject = "Website Contact Form: $name";
$body = "You have received a new message from your website contact form.\n\n"."Here are the details:\n\nName: $name\n\nEmail: $email\n\nPhone: $phone\n\nMessage:\n$message";
$header = "From: noreply@yourdomain.com\n"; // This is the email address the generated message will be from. We recommend using something like noreply@yourdomain.com.
$header .= "Reply-To: $email";
if(!mail($to, $subject, $body, $header))
http_response_code(500);
?>

View File

@ -0,0 +1,43 @@
version: '3'
networks:
webwolflocal:
services:
webgoat:
hostname: www.webgoat.local
image: webgoat/webgoat-8.0
environment:
- WEBGOAT_PORT=8080
- WEBGOAT_SSLENABLED=false
- WEBWOLF_HOST=webwolf
- WEBWOLF_PORT=9090
- TZ=Europe/Amsterdam
volumes:
- .:/home/webgoat/.webgoat
working_dir: /home/webgoat
command: --server.address=0.0.0.0
networks:
webwolflocal:
aliases:
- goat.webgoat.local
webwolf:
image: webgoat/webwolf
environment:
- WEBWOLF_HOST=webwolf
- WEBWOLF_PORT=9090
- TZ=Europe/Amsterdam
command: --spring.datasource.url=jdbc:hsqldb:hsql://webgoat:9001/webgoat --server.address=0.0.0.0
networks:
webwolflocal:
aliases:
- wolf.webwolf.local
depends_on:
- webgoat
reverseproxy:
hostname: www.webwolf.local
image: webgoat/reverseproxy
networks:
webwolflocal:
aliases:
- www.webwolf.local
ports:
- 80:80

1746
pmd-ruleset.xml Normal file

File diff suppressed because it is too large Load Diff

85
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<packaging>pom</packaging>
<version>v8.0.0.M26</version>
<version>v8.1.0</version>
<name>WebGoat Parent Pom</name>
<description>Parent Pom for the WebGoat Project. A deliberately insecure Web Application</description>
@ -21,7 +21,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<version>2.2.2.RELEASE</version>
</parent>
<licenses>
@ -127,7 +127,6 @@
<commons-lang3.version>3.4</commons-lang3.version>
<commons-io.version>2.6</commons-io.version>
<guava.version>18.0</guava.version>
<hsqldb.version>2.3.4</hsqldb.version>
<junit.version>4.12</junit.version>
<lombok.version>1.18.4</lombok.version>
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
@ -135,7 +134,7 @@
<maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<maven-surefire-plugin.version>3.0.0-M4</maven-surefire-plugin.version>
</properties>
<modules>
@ -144,6 +143,7 @@
<module>webgoat-server</module>
<module>webwolf</module>
<module>webgoat-integration-tests</module>
<module>docker</module><!-- copy required jars in preparation of docker all-in-one build -->
</modules>
<dependencies>
@ -151,7 +151,6 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
@ -162,7 +161,6 @@
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
@ -171,7 +169,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>11</source>
<target>11</target>
@ -194,6 +191,80 @@
</plugins>
</build>
<profiles>
<profile>
<id>defaultProfile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>owasp</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<configLocation>config/checkstyle/checkstyle.xml</configLocation>
<suppressionsLocation>config/checkstyle/suppressions.xml</suppressionsLocation>
<suppressionsFileExpression>checkstyle.suppressions.file</suppressionsFileExpression>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<targetJdk>11</targetJdk>
<failurePriority>1</failurePriority><!-- 5 means fail even on the lowest
priority, 0 means never fail -->
<rulesets>
<ruleset>${maven.multiModuleProjectDirectory}/pmd-ruleset.xml</ruleset>
</rulesets>
<failOnViolation>true</failOnViolation>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>5.3.2</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<skipProvidedScope>true</skipProvidedScope>
<skipRuntimeScope>true</skipRuntimeScope>
<suppressionFiles>
<suppressionFile>project-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>central</id>

40
project-suppression.xml Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress base="true">
<notes><![CDATA[
This suppresses false positives identified on spring framework.
]]></notes>
<cpe>cpe:/a:pivotal_software:spring_framework</cpe>
<cve>CVE-2020-5398</cve>
</suppress>
<suppress base="true">
<notes><![CDATA[
This suppresses false positives identified on spring framework.
]]></notes>
<cpe>cpe:/a:redhat:undertow</cpe>
<cve>CVE-2019-14888</cve>
</suppress>
<suppress base="true">
<notes><![CDATA[
This suppresses false positives identified on spring framework.
]]></notes>
<cpe>cpe:/a:pivotal_software:spring_security</cpe>
<cve>CVE-2018-1258</cve>
</suppress>
<suppress base="true">
<cpe>cpe:/a:jruby:jruby</cpe>
<cve>CVE-2018-1000613</cve>
<cve>CVE-2018-1000180</cve>
<cve>CVE-2017-18640</cve>
<cve>CVE-2011-4838</cve>
</suppress>
<suppress base="true"><!-- vulnerable components lesson -->
<cpe>cpe:/a:xstream_project:xstream</cpe>
<cve>CVE-2017-7957</cve>
<cve>CVE-2016-3674</cve>
</suppress>
<suppress base="true"><!-- webgoat-server -->
<cpe>cpe:/a:postgresql:postgresql</cpe>
<cve>CVE-2018-10936</cve>
</suppress>
</suppressions>

View File

@ -10,13 +10,6 @@ if [ ! -z "${TRAVIS_TAG}" ]; then
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
docker push $REPO
#elif [ ! -z "${TRAVIS_TAG}" ]; then
# # Creating a tag build we push it to Docker with that tag
# docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:${TRAVIS_TAG} -t $REPO:latest .
# docker push $REPO
#elif [ "${BRANCH}" == "develop" ]; then
# docker build -f Dockerfile -t $REPO:snapshot .
# docker push $REPO
else
echo "Skipping releasing to DockerHub because it is a build of branch ${BRANCH}"
fi
@ -34,3 +27,16 @@ if [ ! -z "${TRAVIS_TAG}" ]; then
else
echo "Skipping releasing to DockerHub because it is a build of branch ${BRANCH}"
fi
export REPO=webgoat/goatandwolf
cd ..
cd docker
ls target/
if [ ! -z "${TRAVIS_TAG}" ]; then
# If we push a tag to master this will update the LATEST Docker image and tag with the version number
docker build --build-arg webgoat_version=${TRAVIS_TAG:1} -f Dockerfile -t $REPO:latest -t $REPO:${TRAVIS_TAG} .
docker push $REPO
else
echo "Skipping releasing to DockerHub because it is a build of branch ${BRANCH}"
fi

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<groupId>org.owasp.webgoat</groupId>
<name>webgoat-container</name>
<modelVersion>4.0.0</modelVersion>
<artifactId>webgoat-container</artifactId>
@ -10,41 +9,14 @@
<parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat-parent</artifactId>
<version>v8.0.0.M26</version>
<version>v8.1.0</version>
</parent>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/application.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<forkCount>0</forkCount>
<reuseForks>true</reuseForks>
@ -56,7 +28,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<goals>
@ -114,7 +85,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -131,7 +101,6 @@
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
<!-- ************* END spring MVC and related dependencies ************** -->
@ -144,13 +113,12 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>4.1.3.RELEASE</version>
<!-- <version>4.1.3.RELEASE</version>-->
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<type>jar</type>
<scope>test</scope>
</dependency>

View File

@ -115,6 +115,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
Map<String, Object> attributes = new HashMap<>();
attributes.put("source-highlighter", "coderay");
attributes.put("backend", "xhtml");
attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS);
Map<String, Object> options = new HashMap<>();
options.put("attributes", attributes);

View File

@ -1,27 +0,0 @@
package org.owasp.webgoat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.FileSystemUtils;
import javax.annotation.PostConstruct;
import java.io.File;
/**
* @author nbaars
* @since 4/15/17.
*/
@Slf4j
@Configuration
@ConditionalOnExpression("'${webgoat.clean}' == 'true'")
public class CleanupLocalProgressFiles {
@Value("${webgoat.server.directory}")
private String webgoatHome;
@PostConstruct
public void clean() {
}
}

View File

@ -3,7 +3,6 @@ package org.owasp.webgoat.asciidoc;
import org.asciidoctor.ast.AbstractBlock;
import org.asciidoctor.extension.InlineMacroProcessor;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -38,14 +37,23 @@ public class WebWolfMacro extends InlineMacroProcessor {
}
/**
* Look at the remote address from received from the browser first. This way it will also work if you run
* the browser in a Docker container and WebGoat on your local machine.
* Determine the host from the hostname and ports that were used.
* The purpose is to make it possible to use the application behind a reverse proxy. For instance in the docker
* compose/stack version with webgoat webwolf and nginx proxy.
* You do not have to use the indicated hostname, but if you do, you should define two hosts aliases
* 127.0.0.1 www.webgoat.local www.webwolf.locaal
*/
private String determineHost(String host, String port) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String ip = request.getRemoteAddr();
String hostname = StringUtils.hasText(ip) ? ip : host;
return "http://" + hostname + ":" + port + (includeWebWolfContext() ? "/WebWolf" : "");
host = request.getHeader("Host");
int semicolonIndex = host.indexOf(":");
if (semicolonIndex==-1 || host.endsWith(":80")) {
host = host.replace(":80", "").replace("www.webgoat.local", "www.webwolf.local");
} else {
host = host.substring(0, semicolonIndex);
host = host.concat(":").concat(port);
}
return "http://" + host + (includeWebWolfContext() ? "/WebWolf" : "");
}
protected boolean includeWebWolfContext() {

View File

@ -29,7 +29,6 @@ import lombok.Getter;
import org.owasp.webgoat.i18n.PluginMessages;
import org.owasp.webgoat.session.UserSessionData;
import org.owasp.webgoat.session.WebSession;
import org.owasp.webgoat.users.UserTracker;
import org.owasp.webgoat.users.UserTrackerRepository;
import org.springframework.beans.factory.annotation.Autowired;
@ -45,20 +44,6 @@ public abstract class AssignmentEndpoint {
@Autowired
private PluginMessages messages;
protected AttackResult trackProgress(AttackResult attackResult) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (userTracker == null) {
userTracker = new UserTracker(webSession.getUserName());
}
if (attackResult.assignmentSolved()) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), this.getClass().getSimpleName());
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
}
userTrackerRepository.save(userTracker);
return attackResult;
}
protected WebSession getWebSession() {
return webSession;
}
@ -76,9 +61,10 @@ public abstract class AssignmentEndpoint {
* Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder success() {
return AttackResult.builder(messages).lessonCompleted(true).feedback("assignment.solved");
protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(true).attemptWasMade().feedback("assignment.solved").assignment(assignment);
}
/**
@ -90,12 +76,13 @@ public abstract class AssignmentEndpoint {
* Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder failed() {
return AttackResult.builder(messages).lessonCompleted(false).feedback("assignment.not.solved");
protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(false).attemptWasMade().feedback("assignment.not.solved").assignment(assignment);
}
protected AttackResult.AttackResultBuilder informationMessage() {
return AttackResult.builder(messages).lessonCompleted(false);
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
}
}

View File

@ -31,6 +31,7 @@ import org.owasp.webgoat.i18n.PluginMessages;
public class AttackResult {
public static class AttackResultBuilder {
private boolean lessonCompleted;
@ -39,6 +40,8 @@ public class AttackResult {
private String feedbackResourceBundleKey;
private String output;
private Object[] outputArgs;
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
public AttackResultBuilder(PluginMessages messages) {
this.messages = messages;
@ -76,8 +79,18 @@ public class AttackResult {
return this;
}
public AttackResultBuilder attemptWasMade() {
this.attemptWasMade = true;
return this;
}
public AttackResult build() {
return new AttackResult(lessonCompleted, messages.getMessage(feedbackResourceBundleKey, feedbackArgs), messages.getMessage(output, output, outputArgs));
return new AttackResult(lessonCompleted, messages.getMessage(feedbackResourceBundleKey, feedbackArgs), messages.getMessage(output, output, outputArgs), assignment.getClass().getSimpleName(), attemptWasMade);
}
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
this.assignment = assignment;
return this;
}
}
@ -87,11 +100,17 @@ public class AttackResult {
private String feedback;
@Getter
private String output;
@Getter
private final String assignment;
@Getter
private boolean attemptWasMade;
public AttackResult(boolean lessonCompleted, String feedback, String output) {
public AttackResult(boolean lessonCompleted, String feedback, String output, String assignment, boolean attemptWasMade) {
this.lessonCompleted = lessonCompleted;
this.feedback = StringEscapeUtils.escapeJson(feedback);
this.output = StringEscapeUtils.escapeJson(output);
this.assignment = assignment;
this.attemptWasMade = attemptWasMade;
}
public static AttackResultBuilder builder(PluginMessages messages) {

View File

@ -0,0 +1,74 @@
/*
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2019 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webgoat.assignments;
import org.owasp.webgoat.session.WebSession;
import org.owasp.webgoat.users.UserTracker;
import org.owasp.webgoat.users.UserTrackerRepository;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
private UserTrackerRepository userTrackerRepository;
private WebSession webSession;
public LessonTrackerInterceptor(UserTrackerRepository userTrackerRepository, WebSession webSession) {
this.userTrackerRepository = userTrackerRepository;
this.webSession = webSession;
}
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> clazz) {
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o != null && o instanceof AttackResult) {
trackProgress((AttackResult) o);
}
return o;
}
protected AttackResult trackProgress(AttackResult attackResult) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (userTracker == null) {
userTracker = new UserTracker(webSession.getUserName());
}
if (attackResult.assignmentSolved()) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
}
userTrackerRepository.saveAndFlush(userTracker);
return attackResult;
}
}

View File

@ -20,15 +20,13 @@
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webgoat.plugins;
package org.owasp.webgoat.lessons;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AttackResult;
import org.owasp.webgoat.lessons.Lesson;
import org.owasp.webgoat.lessons.Assignment;
import org.owasp.webgoat.session.Course;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -39,6 +37,7 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import static java.util.stream.Collectors.groupingBy;
@ -75,16 +74,26 @@ public class CourseConfiguration {
private String getPath(Class<? extends AssignmentEndpoint> e) {
for (Method m : e.getMethods()) {
if (m.getReturnType() == AttackResult.class) {
if (methodReturnTypeIsOfTypeAttackResult(m)) {
var mapping = getMapping(m);
if (mapping == null) {
log.error("AttackResult method found without mapping in: {}", e.getSimpleName());
} else {
if (mapping != null) {
return mapping;
}
}
}
return "none";
throw new IllegalStateException("Assignment endpoint: " + e + " has no mapping like @GetMapping/@PostMapping etc," +
"with return type 'AttackResult' or 'ResponseEntity<AttackResult>' please consider adding one");
}
private boolean methodReturnTypeIsOfTypeAttackResult(Method m) {
if (m.getReturnType() == AttackResult.class) {
return true;
}
var genericType = m.getGenericReturnType();
if (genericType instanceof ParameterizedType) {
return ((ParameterizedType) m.getGenericReturnType()).getActualTypeArguments()[0] == AttackResult.class;
}
return false;
}
private String getMapping(Method m) {
@ -100,9 +109,9 @@ public class CourseConfiguration {
paths = ArrayUtils.addAll(m.getAnnotation(PutMapping.class).value(), m.getAnnotation(PutMapping.class).path());
}
if (paths == null) {
return "";
return null;
} else {
return Arrays.stream(paths).filter(path -> !"".equals(path)).findFirst().orElseGet(() -> "");
return Arrays.stream(paths).filter(path -> !"".equals(path)).findFirst().orElse("");
}
}

View File

@ -119,5 +119,7 @@ public abstract class Lesson {
return getTitle();
}
public abstract String getId();
public final String getId() {
return this.getClass().getSimpleName();
}
}

View File

@ -1,78 +0,0 @@
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
*/
package org.owasp.webgoat.lessons;
/**
* <p>RequestParameter class.</p>
*
* @author rlawson
* @version $Id: $Id
*/
public class RequestParameter implements Comparable<RequestParameter> {
private final String name;
private final String value;
/**
* <p>Constructor for RequestParameter.</p>
*
* @param name a {@link java.lang.String} object.
* @param value a {@link java.lang.String} object.
*/
public RequestParameter(String name, String value) {
this.name = name;
this.value = value;
}
/**
* <p>Getter for the field <code>name</code>.</p>
*
* @return the name
*/
public String getName() {
return name;
}
/**
* <p>Getter for the field <code>value</code>.</p>
*
* @return the values
*/
public String getValue() {
return value;
}
@Override
public int compareTo(RequestParameter o) {
return this.name.compareTo(o.getName());
}
}

View File

@ -1,29 +0,0 @@
package org.owasp.webgoat.plugins;
/**
* <p>PluginLoadingFailure class.</p>
*
* @version $Id: $Id
* @author dm
*/
public class PluginLoadingFailure extends RuntimeException {
/**
* <p>Constructor for PluginLoadingFailure.</p>
*
* @param message a {@link java.lang.String} object.
*/
public PluginLoadingFailure(String message) {
super(message);
}
/**
* <p>Constructor for PluginLoadingFailure.</p>
*
* @param message a {@link java.lang.String} object.
* @param e a {@link java.lang.Exception} object.
*/
public PluginLoadingFailure(String message, Exception e) {
super(message, e);
}
}

View File

@ -1,74 +0,0 @@
/**
* *************************************************************************************************
*
*
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 20014 Bruce Mayhew
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source ==============
*
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
*/
package org.owasp.webgoat.service;
import org.owasp.webgoat.session.WebSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* <p>LessonPlanService class.</p>
*
* @author rlawson
* @version $Id: $Id
*/
@Controller
//TODO remove
public class LessonPlanService {
private final WebSession webSession;
public LessonPlanService(WebSession webSession) {
this.webSession = webSession;
}
/**
* Returns source for current attack
*
* @return a {@link java.lang.String} object.
*/
@RequestMapping(path = "/service/lessonplan.mvc", produces = "application/html")
public @ResponseBody
String showPlan() {
String plan = getPlan();
return plan;
}
/**
* Description of the Method
*
* @return Description of the Return Value
*/
protected String getPlan() {
return "Plan is not available for this lesson.";
}
}

View File

@ -28,32 +28,8 @@ import java.util.Map;
@AllArgsConstructor
public class LessonProgressService {
private UserTrackerRepository userTrackerRepository;
private WebSession webSession;
/**
* <p>LessonProgressService.</p>
*
* @return a {@link LessonInfoModel} object.
*/
@RequestMapping(value = "/service/lessonprogress.mvc", produces = "application/json")
@ResponseBody
public Map getLessonInfo() {
Map json = new HashMap();
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (webSession.getCurrentLesson() != null) {
LessonTracker lessonTracker = userTracker.getLessonTracker(webSession.getCurrentLesson());
String successMessage = "";
boolean lessonCompleted = false;
if (lessonTracker != null) {
lessonCompleted = isLessonComplete(lessonTracker.getLessonOverview(), webSession.getCurrentLesson());
successMessage = "LessonCompleted"; //@todo we still use this??
}
json.put("lessonCompleted", lessonCompleted);
json.put("successMessage", successMessage);
}
return json;
}
private final UserTrackerRepository userTrackerRepository;
private final WebSession webSession;
/**
* Endpoint for fetching the complete lesson overview which informs the user about whether all the assignments are solved.

View File

@ -1,67 +0,0 @@
/**
* *************************************************************************************************
* <p>
* <p>
* This file is part of WebGoat, an Open Web Application Security Project
* utility. For details, please see http://www.owasp.org/
* <p>
* Copyright (c) 2002 - 20014 Bruce Mayhew
* <p>
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* <p>
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
* <p>
* Getting Source ==============
* <p>
* Source for this application is maintained at
* https://github.com/WebGoat/WebGoat, a repository for free software projects.
*/
package org.owasp.webgoat.service;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* <p>PluginReloadService class.</p>
*
* @author nbaars
* @version $Id: $Id
*/
//TODO REMOVE?
@Controller
public class PluginReloadService {
/**
* Reload all the plugins
*
* @param session a {@link javax.servlet.http.HttpSession} object.
* @return a {@link org.springframework.http.ResponseEntity} object.
*/
@RequestMapping(path = "/service/reloadplugins.mvc", produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody
ResponseEntity<Map<String, Object>> reloadPlugins(HttpSession session) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("success", true);
result.put("message", "Plugins reloaded");
return new ResponseEntity<>(result, HttpStatus.OK);
}
}

View File

@ -67,7 +67,7 @@ public class ReportCardService {
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
@ResponseBody
public ReportCard reportCard() {
ReportCard reportCard = new ReportCard();
final ReportCard reportCard = new ReportCard();
reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());
reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments());
@ -76,7 +76,7 @@ public class ReportCardService {
reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved());
for (Lesson lesson : course.getLessons()) {
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
LessonStatistics lessonStatistics = new LessonStatistics();
final LessonStatistics lessonStatistics = new LessonStatistics();
lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));
lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());
lessonStatistics.setSolved(lessonTracker.isLessonSolved());
@ -87,7 +87,7 @@ public class ReportCardService {
@Getter
@Setter
private class ReportCard {
private final class ReportCard {
private int totalNumberOfLessons;
private int totalNumberOfAssignments;
@ -99,7 +99,7 @@ public class ReportCardService {
@Setter
@Getter
private class LessonStatistics {
private final class LessonStatistics {
private String name;
private boolean solved;
private int numberOfAttempts;

View File

@ -38,6 +38,10 @@ public class Scoreboard {
List<WebGoatUser> allUsers = userRepository.findAll();
List<Ranking> rankings = new ArrayList<>();
for (WebGoatUser user : allUsers) {
if (user.getUsername().startsWith("csrf-")) {
//the csrf- assignment specific users do not need to be in the overview
continue;
}
UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername());
rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker)));
}

View File

@ -29,11 +29,9 @@ logging.level.org.owasp=DEBUG
logging.level.org.owasp.webgoat=DEBUG
webgoat.start.hsqldb=true
webgoat.clean=false
webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/
webgoat.user.directory=${user.home}/.webgoat-${webgoat.build.version}/
webgoat.build.version=@project.version@
webgoat.build.number=@build.number@
webgoat.email=webgoat@owasp.org
webgoat.emaillist=owasp-webgoat@lists.owasp.org
webgoat.feedback.address=webgoat@owasp.org

View File

@ -1,6 +1,8 @@
CREATE SCHEMA CONTAINER;
-- This statement is here the schema is always created even if we use Flyway directly like in test-cases
-- For the normal WebGoat server there is a bean which already provided the schema (and creates it see DatabaseInitialization)
CREATE SCHEMA IF NOT EXISTS CONTAINER;
CREATE SEQUENCE CONTAINER.HIBERNATE_SEQUENCE AS INTEGER START WITH 1;
CREATE SEQUENCE CONTAINER.HIBERNATE_SEQUENCE;
CREATE TABLE CONTAINER.ASSIGNMENT (
ID BIGINT NOT NULL PRIMARY KEY,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 100 100" height="75px" id="Calque_2" version="1.1" viewBox="0 0 100 100" width="75px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M16.64,33.53c0.63,0,1.25,0.16,1.81,0.46c0.88,0.49,1.52,1.28,1.8,2.25c0.28,0.97,0.18,1.98-0.31,2.86l-1.23,2.26 l6.43,3.52l3.52-6.43l-2.26-1.23c-1.82-1-2.49-3.29-1.5-5.11c0.67-1.21,1.93-1.96,3.31-1.96c0.63,0,1.25,0.16,1.81,0.46l2.25,1.24 l3.52-6.43l-7.67-4.2l1.92-3.49c0.62-1.14,0.2-2.57-0.94-3.2c-0.34-0.19-0.73-0.29-1.12-0.29c-0.87,0-1.66,0.47-2.07,1.23L24,18.96 l-7.67-4.2l-4.2,7.67l-3.49-1.91c-0.35-0.19-0.74-0.29-1.13-0.29c-0.86,0-1.65,0.47-2.07,1.22c-0.62,1.14-0.2,2.57,0.94,3.2 l3.49,1.91l-4.2,7.67l6.43,3.52l1.24-2.26C14,34.28,15.27,33.53,16.64,33.53z"/><path d="M34.97,68.32c0-2.07,1.69-3.761,3.77-3.761h2.57V57.23h-8.74v-3.98c0-1.3-1.06-2.36-2.36-2.36 c-1.29,0-2.35,1.061-2.35,2.36v3.98h-8.74v8.739h-3.99c-1.29,0-2.35,1.061-2.35,2.351c0,1.3,1.06,2.359,2.35,2.359h3.99v8.74h7.33 v-2.57c0-2.08,1.69-3.77,3.76-3.77c2.08,0,3.77,1.689,3.77,3.77v2.57h7.33v-7.33h-2.57C36.66,72.09,34.97,70.4,34.97,68.32z"/><path d="M71.25,68.32c0-1.29-1.06-2.351-2.35-2.351h-3.99V57.23h-8.74v-3.98c0-1.3-1.05-2.35-2.35-2.36 c-1.29,0.011-2.34,1.061-2.34,2.36v3.98h-8.74v8.739h-3.99c-1.3,0-2.35,1.061-2.35,2.351c0,1.3,1.05,2.359,2.35,2.359h3.99v8.74 h8.74v3.98c0,1.3,1.05,2.359,2.35,2.359s2.35-1.06,2.35-2.359v-3.98h8.73v-8.74h3.99C70.2,70.68,71.25,69.62,71.25,68.32z"/><path d="M92.49,65.97h-3.98V57.23h-8.74v-3.99c0-0.641-0.26-1.23-0.689-1.66c-0.43-0.42-1.01-0.69-1.66-0.69 c-1.3,0-2.36,1.061-2.36,2.36v3.98h-8.72v7.329h2.57c2.07,0,3.76,1.69,3.76,3.761c0,2.08-1.689,3.77-3.76,3.77h-2.57v7.33h7.311 v-2.57c0-2.08,1.689-3.77,3.76-3.77c2.08,0,3.77,1.689,3.77,3.77v2.57h7.33v-8.74h3.98c1.3,0,2.359-1.06,2.359-2.359 C94.85,67.02,93.79,65.97,92.49,65.97z"/><path d="M59.98,44.73c0,1.29,1.06,2.35,2.359,2.35h3.98v8.74h7.33v-2.57c0-2.08,1.689-3.77,3.77-3.77c1.03,0,1.97,0.43,2.66,1.11 c0.68,0.68,1.1,1.62,1.1,2.66v2.57h7.33v-7.33H85.94c-2.08,0-3.771-1.69-3.771-3.77c0-2.07,1.69-3.76,3.771-3.76h2.569v-7.33h-8.74 v-3.98c0-1.3-1.06-2.36-2.35-2.36c-1.3,0-2.36,1.06-2.36,2.36v3.98H66.32v8.74h-3.98C61.04,42.37,59.98,43.43,59.98,44.73z"/><path d="M38.74,47.08h3.98v8.74h7.33v-2.57c0-1.04,0.42-1.98,1.11-2.67c0.68-0.68,1.62-1.1,2.66-1.1h0.01 c2.08,0,3.77,1.69,3.77,3.77v2.57h7.311v-7.33h-2.57c-2.08,0-3.77-1.69-3.77-3.77c0-2.07,1.689-3.76,3.77-3.76h2.57v-7.33h-8.74 v-3.98c0-1.3-1.06-2.36-2.35-2.36c-1.301,0-2.36,1.06-2.36,2.36v3.98h-8.74v8.74h-3.98c-1.3,0-2.36,1.06-2.36,2.35 C36.38,46.02,37.44,47.08,38.74,47.08z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -7,21 +7,25 @@ body {
font-family: 'Open Sans', sans-serif;
padding: 0px;
margin: 0px;
line-height: 1;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
a:link,
a:visited {
text-decoration: none;
outline: none;
/* color: #e84c3d; */
}
a:hover,
a:active {
outline: none;
text-decoration: none;
color: #16a086;
}
h1,
h2,
h3,
@ -30,22 +34,28 @@ h5,
h6 {
font-family: 'Source Sans Pro', Arial, sans-serif;
}
p {
font-size: 14px;
}
hr {
margin-top: 10px;
margin-bottom: 10px;
}
img {
max-width: 100%;
}
::selection {
background: #fff7dd;
}
::-moz-selection {
background: #fff7dd;
}
/* ==========================================================================
Layout
========================================================================== */
@ -59,6 +69,7 @@ img {
-ms-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
}
/* Header */
#header {
z-index: 200;
@ -71,6 +82,7 @@ img {
transition: all 0.3s ease-in-out;
margin-right: 0;
}
#header .brand {
float: left;
width: 240px;
@ -79,6 +91,7 @@ img {
position: relative;
background: url('img/logoBG.jpg') no-repeat 0px 0px;
}
#header .logo {
color: #fff;
font-size: 1.7em;
@ -86,24 +99,29 @@ img {
padding: 23px 0 0 75px;
display: inline-block;
}
#header .logo span {
font-weight: 700;
}
#header .toggle-navigation button:hover,
#header .toggle-navigation button:active,
#header button#toggle-mail:hover,
#header button#toggle-mail:active {
background: #e84c3d;
}
#header .toggle-navigation button:hover i,
#header button#toggle-mail:hover i {
color: #F6F6F6;
}
#header .toggle-navigation.toggle-left {
margin-top: 5px;
margin-left: 20px;
display: inline-block;
}
#header .btn-default {
padding: 3px 9px;
background: #F6F6F6;
@ -115,27 +133,33 @@ img {
width: 35px;
height: 35px;
}
#header .btn-default .fa-bars,
#header .btn-default .fa-comment {
cursor: pointer;
color: #797979;
}
#header .btn-default .fa-info,
#header .btn-default .fa-envelope,
#header .btn-default .fa-user {
color: #797979;
}
#header .user-nav button:hover,
#header .user-nav button:active {
background: #e84c3d;
}
#header .user-nav button:hover i {
color: #F6F6F6;
}
#header #lesson-title-wrapper {
display: inline-block;
margin: 0 0 0 20px;
}
#header .pull-right {
float: right !important;
margin-top: 25px;
@ -177,6 +201,7 @@ img {
padding: 15px 15px 0 15px;
width: 100%;
}
.main-content-wrapper #main-content .h1 {
margin: 0;
padding: 0px 10px 40px 10px;
@ -186,9 +211,11 @@ img {
font-size: 42px;
font-family: 'Source Sans Pro', Arial, sans-serif;
}
.main-content-toggle-left {
margin-left: 0;
}
.main-content-toggle-right {
margin-right: 240px;
}
@ -205,10 +232,27 @@ img {
padding: 3px !important;
}
div.lesson-page-solution {
position: relative;
border: 2px solid #425c2f;
border-radius: 12px;
padding: 7px;
margin-top: 7px;
padding: 5px;
background-image: url('img/solution.svg');
background-repeat: no-repeat;
background-position: right 10px top;
}
div.lesson-image img {
border: 2px solid #aaa;
border-radius: 8px;
max-width: 50%;
height: auto;
margin-top: 10px;
margin-bottom: 20px;
}
/* ==========================================================================
Buttons
========================================================================== */
@ -227,10 +271,12 @@ div.lesson-image img {
-webkit-transition: border 0.25s linear, color 0.25s linear, background-color 0.25s linear;
transition: border 0.25s linear, color 0.25s linear, background-color 0.25s linear;
}
.btn:hover,
.btn:focus {
outline: none;
}
.btn:active,
.btn.active {
outline: none;
@ -238,6 +284,7 @@ div.lesson-image img {
box-shadow: none;
outline: none !important;
}
.btn.disabled,
.btn[disabled],
.btn fieldset[disabled] .btn {
@ -246,6 +293,7 @@ div.lesson-image img {
opacity: 0.7;
filter: alpha(opacity=70);
}
/* Default Buttons*/
.btn-default,
a.btn-default:link,
@ -254,12 +302,14 @@ a.btn-default:visited {
background-color: #bdc3c7;
outline: none !important;
}
a.btn-default:hover,
a.btn-default:active {
color: #ffffff;
background-color: #cbd0d3;
border-color: #cbd0d3;
}
.btn-default:hover,
.btn-default:focus,
.btn-default:active,
@ -269,12 +319,14 @@ a.btn-default:active {
background-color: #cbd0d3;
border-color: #cbd0d3;
}
.btn-default:active,
.btn-default.active,
.open .dropdown-toggle.btn-default {
background: #bdc3c7;
border-color: #bdc3c7;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
@ -293,18 +345,21 @@ fieldset[disabled] .btn-default.active {
background-color: #bdc3c7;
border-color: #bdc3c7;
}
.btn-primary,
a.btn-primary:link,
a.btn-primary:visited {
color: #fff;
background-color: #e84c3d;
}
a.btn-primary:hover,
a.btn-primary:active {
color: #ffffff;
background-color: #C62F28;
border-color: #C62F28;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
@ -314,12 +369,14 @@ a.btn-primary:active {
background-color: #C62F28;
border-color: #C62F28;
}
.btn-primary:active,
.btn-primary.active,
.open .dropdown-toggle.btn-primary {
background: #e84c3d;
border-color: #e84c3d;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
@ -338,21 +395,25 @@ fieldset[disabled] .btn-primary.active {
background-color: #e84c3d;
border-color: #e84c3d;
}
.btn-info {
color: #ffffff;
background-color: #3598db;
}
.btn-info,
a.btn-info:link,
a.btn-info:visited {
color: #ffffff;
background-color: #3598db;
}
a.btn-info:hover,
a.btn-info:active {
color: #ffffff;
background-color: #4ba3df;
}
.btn-info:hover,
.btn-info:focus,
.btn-info:active,
@ -362,12 +423,14 @@ a.btn-info:active {
background-color: #4ba3df;
border-color: #4ba3df;
}
.btn-info:active,
.btn-info.active,
.open .dropdown-toggle.btn-info {
background: #3598db;
border-color: #3598db;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
@ -386,10 +449,12 @@ fieldset[disabled] .btn-info.active {
background-color: #3598db;
border-color: #3598db;
}
.btn-danger {
color: #ffffff;
background-color: #e84c3d;
}
.btn-danger:hover,
.btn-danger:focus,
.btn-danger:active,
@ -399,12 +464,14 @@ fieldset[disabled] .btn-info.active {
background-color: #eb6154;
border-color: #eb6154;
}
.btn-danger:active,
.btn-danger.active,
.open .dropdown-toggle.btn-danger {
background: #eb6154;
border-color: #eb6154;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
@ -423,10 +490,12 @@ fieldset[disabled] .btn-danger.active {
background-color: #e84c3d;
border-color: #e84c3d;
}
.btn-success {
color: #ffffff;
background-color: #2dcc70;
}
.btn-success:hover,
.btn-success:focus,
.btn-success:active,
@ -436,12 +505,14 @@ fieldset[disabled] .btn-danger.active {
background-color: #3ed47d;
border-color: #3ed47d;
}
.btn-success:active,
.btn-success.active,
.open .dropdown-toggle.btn-success {
background: #2dcc70;
border-color: #2dcc70;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
@ -460,10 +531,12 @@ fieldset[disabled] .btn-success.active {
background-color: #2dcc70;
border-color: #2dcc70;
}
.btn-warning {
color: #ffffff;
background-color: #f1c40f;
}
.btn-warning:hover,
.btn-warning:focus,
.btn-warning:active,
@ -473,6 +546,7 @@ fieldset[disabled] .btn-success.active {
background-color: #f1c40f;
border-color: #f1c40f;
}
.btn-warning:active,
.btn-warning.active,
.open .dropdown-toggle.btn-warning {
@ -498,6 +572,7 @@ fieldset[disabled] .btn-warning.active {
background-color: #f1c40f;
border-color: #f1c40f;
}
/* Button Sizes */
.btn-lg {
padding: 10px 16px;
@ -526,6 +601,7 @@ fieldset[disabled] .btn-warning.active {
-o-border-radius: 3px;
border-radius: 3px;
}
/* ==========================================================================
Breadcrumbs
========================================================================== */
@ -536,6 +612,7 @@ fieldset[disabled] .btn-warning.active {
.breadcrumb > li {
font-size: 12px;
}
/* ==========================================================================
Icons
========================================================================== */
@ -548,6 +625,7 @@ fieldset[disabled] .btn-warning.active {
margin-right: 5px;
width: 20px;
}
/* ==========================================================================
Panels
========================================================================== */
@ -682,6 +760,7 @@ fieldset[disabled] .btn-warning.active {
.modal-footer .btn + .btn {
margin-bottom: 5px;
}
.modal .modal-body.modal-scroll {
max-height: 375px;
overflow-y: scroll auto;
@ -704,45 +783,57 @@ fieldset[disabled] .btn-warning.active {
font-size: 35px;
}
}
@media only screen and (max-width: 660px) {
#header {
height: 160px;
}
#header .brand {
width: 100%;
}
#header .user-nav ul {
padding-left: 0;
}
#header .toggle-navigation.toggle-left {
float: left;
}
.sidebar {
margin-left: -240px;
}
.sidebar-toggle {
margin-left: 0;
width: 100%;
}
.main-content-wrapper {
margin-left: 0;
}
.main-content-toggle-left {
margin-left: 660px;
}
.sidebarRight {
top: 160px;
width: 100%;
}
.user-nav ul li {
font-size: 12px;
}
}
@media only screen and (max-width: 479px) {
/* Main Content */
#main-content .h1 {
font-size: 22px;
}
#header .dropdown.messages {
display: none;
}
@ -849,6 +940,7 @@ cookie-container {
margin: -2px 0 0;
padding: 0;
}
#menu-container ul li {
list-style-type: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
@ -874,6 +966,7 @@ cookie-container {
#menu-container ul li a span {
display: inline-block;
}
#menu-container ul ul li {
/*background: #333;*/
background: #aaa;
@ -1002,7 +1095,9 @@ cookie-container {
}
@keyframes blink {
50% { border-color: white; }
50% {
border-color: white;
}
}
.cur-page {
@ -1081,6 +1176,17 @@ span.show-next-page, span.show-prev-page {
padding: 5px;
}
/* same look but not the behaviour */
.nonattack-container {
position: relative;
background-color: #f1f1f1;
border: 2px solid #a66;
border-radius: 12px;
padding: 7px;
margin-top: 7px;
padding: 5px;
}
/* ERROR NOTIFICATION */
#error-notification-container {
display: none;
@ -1170,3 +1276,212 @@ div.captured-flag {
width: 42px;
height: 47px;
}
.solution {
position: absolute;
top: 10px;
right: 25px;
width: 42px;
height: 47px;
}
/* Taken from asciidoctor page */
code {
font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace;
font-weight: 400;
color: rgba(0, 0, 0, .9)
}
.admonitionblock td.icon [class^="fa icon-"] {
font-size: 2.5em;
text-shadow: 1px 1px 2px rgba(0, 0, 0, .5);
cursor: default
}
.admonitionblock td.icon .icon-note::before {
content: "\f05a";
color: #19407c
}
.admonitionblock td.icon .icon-tip::before {
content: "\f0eb";
text-shadow: 1px 1px 2px rgba(155, 155, 0, .8);
color: #111
}
.admonitionblock td.icon .icon-warning::before {
content: "\f071";
color: #bf6900
}
.admonitionblock td.icon .icon-caution::before {
content: "\f06d";
color: #bf3400
}
.admonitionblock td.icon .icon-important::before {
content: "\f06a";
color: #bf0000
}
.quoteblock {
margin: 0 1em 1.25em 1.5em;
display: table
}
.quoteblock:not(.excerpt) > .title {
margin-left: -1.5em;
margin-bottom: .75em
}
.quoteblock blockquote, .quoteblock p {
color: rgba(0, 0, 0, .85);
font-size: 1.15rem;
line-height: 1.75;
word-spacing: .1em;
letter-spacing: 0;
font-style: italic;
text-align: justify
}
.quoteblock blockquote {
margin: 0;
padding: 0;
border: 0
}
.quoteblock blockquote::before {
content: "\201c";
float: left;
font-size: 2.75em;
font-weight: bold;
line-height: .6em;
margin-left: -.6em;
color: #7a2518;
text-shadow: 0 1px 2px rgba(0, 0, 0, .1)
}
.quoteblock blockquote > .paragraph:last-child p {
margin-bottom: 0
}
.quoteblock .attribution {
margin-top: .75em;
margin-right: .5ex;
text-align: right
}
.verseblock {
margin: 0 1em 1.25em
}
.verseblock pre {
font-family: "Open Sans", "DejaVu Sans", sans;
font-size: 1.15rem;
color: rgba(0, 0, 0, .85);
font-weight: 300;
text-rendering: optimizeLegibility
}
.verseblock pre strong {
font-weight: 400
}
.verseblock .attribution {
margin-top: 1.25rem;
margin-left: .5ex
}
.quoteblock .attribution, .verseblock .attribution {
font-size: .9375em;
line-height: 1.45;
font-style: italic
}
.quoteblock .attribution br, .verseblock .attribution br {
display: none
}
.quoteblock .attribution cite, .verseblock .attribution cite {
display: block;
letter-spacing: -.025em;
color: rgba(0, 0, 0, .6)
}
.quoteblock.abstract blockquote::before, .quoteblock.excerpt blockquote::before, .quoteblock .quoteblock blockquote::before {
display: none
}
.quoteblock.abstract blockquote, .quoteblock.abstract p, .quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p {
line-height: 1.6;
word-spacing: 0
}
.quoteblock.abstract {
margin: 0 1em 1.25em;
display: block
}
.quoteblock.abstract > .title {
margin: 0 0 .375em;
font-size: 1.15em;
text-align: center
}
.quoteblock.excerpt > blockquote, .quoteblock .quoteblock {
padding: 0 0 .25em 1em;
border-left: .25em solid #dddddf
}
.quoteblock.excerpt, .quoteblock .quoteblock {
margin-left: 0
}
.quoteblock.excerpt blockquote, .quoteblock.excerpt p, .quoteblock .quoteblock blockquote, .quoteblock .quoteblock p {
color: inherit;
font-size: 1.0625rem
}
.quoteblock.excerpt .attribution, .quoteblock .quoteblock .attribution {
color: inherit;
text-align: left;
margin-right: 0
}
.conum {
display: inline-block;
color: #fff !important;
background: rgba(0, 0, 0, .8);
-webkit-border-radius: 50%;
border-radius: 50%;
text-align: center;
font-size: .90em;
width: 1.67em;
height: 1.67em;
line-height: 1.67em;
font-family: "Open Sans", "DejaVu Sans", sans-serif;
font-style: normal;
font-weight: bold
}
.conum * {
color: #fff !important
}
.conum + b {
display: none
}
.conum::after {
content: attr(data-value)
}
pre .conum {
position: relative;
top: -.125em
}
b.conum * {
color: inherit !important
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 B

Some files were not shown because too many files have changed in this diff Show More