Compare commits

...

115 Commits

Author SHA1 Message Date
bf02077427 chore: bump org.wiremock:wiremock-standalone from 3.9.1 to 3.9.2 (#1947)
Bumps [org.wiremock:wiremock-standalone](https://github.com/wiremock/wiremock) from 3.9.1 to 3.9.2.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.9.1...3.9.2)

---
updated-dependencies:
- dependency-name: org.wiremock:wiremock-standalone
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 15:46:43 +01:00
e1e00bca73 fix: JWT kid/jku lessons (#1949)
* refactor: rewrite hints

Use active voice and fix grammar issues.

* fix: use Thymeleaf `th:action`

* fix: JWT kid/jku lessons

Split the JavaScript into two files they pointed to the same URL

The JWTs are now valid, they parse successfully.

The paths now include `/kid` and `/jku` to make sure the hints match accordingly in the UI. Otherwise `/delete` would pick up both hints from both assignments as the paths overlap.

Closes: #1715

* fix: update to latest pre-commit version

* fix: increase timeouts for server to start during integration tests
2024-11-07 15:45:33 +01:00
d59153d6d7 Fix password reset lesson (#1941)
* docs: improve text

* fix: use correct POST url
2024-10-29 17:32:51 +01:00
87fae00f03 chore: bump commons-io:commons-io from 2.16.1 to 2.17.0 (#1937)
Bumps commons-io:commons-io from 2.16.1 to 2.17.0.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 16:30:32 +01:00
3f6a74ad86 chore(gh-actions): update dependency 2024-10-28 22:02:02 +01:00
1d37ee0877 ci: run pre-commit checks first
Create a dependency between the jobs.
2024-10-28 21:59:10 +01:00
4f6ab25ebd ci: run pre-commit checks first 2024-10-28 21:57:43 +01:00
af687e71fe chore: bump com.google.guava:guava from 33.3.0-jre to 33.3.1-jre (#1939) 2024-10-28 20:02:09 +01:00
83ed4c3d5c chore: bump org.testcontainers:testcontainers from 1.20.1 to 1.20.3 (#1935) 2024-10-28 15:05:33 +01:00
62cdfd0824 chore: bump com.github.terma:javaniotcpproxy from 1.5 to 1.6 (#1936) 2024-10-28 15:04:15 +01:00
e7457f4821 chore: bump org.apache.maven.plugins:maven-checkstyle-plugin (#1938) 2024-10-28 15:04:01 +01:00
4efaf87c7e Fix passing command line arguments (#1933)
* fix: use banners correctly

* fix: passing command line arguments

Since we already have `webwolf.port` it makes sense to also define `webwolf.port` explicitly and not rely on `server.port`

Closes: #1910
2024-10-27 08:39:02 +01:00
cf5101a633 chore: bump org.asciidoctor:asciidoctorj from 2.5.13 to 3.0.0 (#1897) 2024-10-26 22:53:43 +02:00
3f049ba53a Nbaars/1886 (#1932)
* improved code readbility

* chore: format code

---------

Co-authored-by: guilherme peixoto <peixoto-guilherme7@hotmail.com>
2024-10-26 22:18:28 +02:00
7e294fbdb5 chore: bump org.apache.commons:commons-compress from 1.26.2 to 1.27.1 (#1884) 2024-10-26 19:27:07 +02:00
2177eb663a chore: bump docker/build-push-action from 6.7.0 to 6.9.0 (#1920) 2024-10-26 16:59:13 +02:00
50692300eb docs: Show boolean operators priority on where (#1902) 2024-10-26 14:48:50 +02:00
e2c2d425cb chore: bump actions/cache from 4.0.2 to 4.1.1 (#1925) 2024-10-26 14:25:04 +02:00
6bbd3cb66b chore: bump org.springframework.boot:spring-boot-starter-parent (#1931) 2024-10-26 14:20:14 +02:00
d08a56d351 chore: add test for solving same lesson as different user. (#1930)
We removed the constraint but did not add an extra testcase to cover this bug.

Closes: #1890
2024-10-26 12:06:30 +02:00
ec97568ec2 chore: bump org.apache.maven.plugins:maven-surefire-plugin (#1922)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.3.1 to 3.5.1.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.3.1...surefire-3.5.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-26 10:55:02 +02:00
9b68368b23 chore: bump pre-commit-ci/lite-action from 1.0.1 to 1.1.0 (#1926)
Bumps [pre-commit-ci/lite-action](https://github.com/pre-commit-ci/lite-action) from 1.0.1 to 1.1.0.
- [Release notes](https://github.com/pre-commit-ci/lite-action/releases)
- [Commits](https://github.com/pre-commit-ci/lite-action/compare/v1.0.1...v1.1.0)

---
updated-dependencies:
- dependency-name: pre-commit-ci/lite-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-26 10:54:46 +02:00
ab068901f1 Remove WebGoat session object (#1929)
* refactor: modernize code

* refactor: move to Tomcat

* chore: bump to Spring Boot 3.3.3

* refactor: use Testcontainers to run integration tests

* refactor: lesson/assignment progress

* chore: format code

* refactor: first step into removing base class for assignment

Always been a bit of an ugly construction, as none of the dependencies are clear. The constructors are hidden due to autowiring the base class. This PR removes two of the fields.

As a bonus we now wire the authentication principal directly in the controllers.

* refactor: use authentication principal directly.

* refactor: pass lesson to the endpoints

No more need to get the current lesson set in a session. The lesson is now passed to the endpoints.

* fix: Testcontainers cannot run on Windows host in Github actions.

Since we have Windows specific paths let's run it standalone for now. We need to run these tests on Docker as well (for now disabled)
2024-10-26 10:54:21 +02:00
cb7c508046 fix: reset form and quiz color on reset lesson (#1903)
* ./mvnw spotless:apply

```
[INFO] --- spotless-maven-plugin:2.41.1:apply (default-cli) @ webgoat ---
[INFO] Writing clean file: /home/ulyssa/labs/WebGoat/WebGoat-bb6e84d/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5a.java
```

* On reset lesson: reset form and quizzes colors
2024-10-26 09:22:18 +02:00
f4c86be6c7 Update fix version 2024-10-18 22:50:19 +02:00
cf2c115093 fix: xss lesson typo 2024-10-18 22:38:32 +02:00
bb6e84ddcf chore: bump com.google.guava:guava from 33.2.1-jre to 33.3.0-jre (#1879)
Bumps [com.google.guava:guava](https://github.com/google/guava) from 33.2.1-jre to 33.3.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-04 21:44:38 +02:00
5fc2666b43 chore: bump docker/build-push-action from 6.5.0 to 6.7.0 (#1877)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.5.0 to 6.7.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.5.0...v6.7.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-04 21:43:46 +02:00
6e946f21a2 chore: bump io.github.bonigarcia:webdrivermanager from 5.9.1 to 5.9.2 (#1866)
Bumps [io.github.bonigarcia:webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 5.9.1 to 5.9.2.
- [Release notes](https://github.com/bonigarcia/webdrivermanager/releases)
- [Changelog](https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-5.9.1...webdrivermanager-5.9.2)

---
updated-dependencies:
- dependency-name: io.github.bonigarcia:webdrivermanager
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-04 15:57:57 +02:00
d38ba2a626 chore: bump docker/build-push-action from 6.4.1 to 6.5.0 (#1867)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.1 to 6.5.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.4.1...v6.5.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-04 15:56:33 +02:00
4c7e6ae4f4 chore: bump org.wiremock:wiremock from 3.9.0 to 3.9.1 (#1865)
Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.9.0 to 3.9.1.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.9.0...3.9.1)

---
updated-dependencies:
- dependency-name: org.wiremock:wiremock
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-04 15:56:12 +02:00
58b762eade fix: copying file using transferTo sometimes fails. (#1862)
Turns out that using this method sometimes fails with an exception about unable to delete a directory.
The stacktrace points to:

```
java.nio.file.FileSystemException: /tmp/webwolf-fileserver/dumbanddummer/xxe_a11.dtd: Not a directory
        at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:100) ~[na:na]
        at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106) ~[na:na]
        at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111) ~[na:na]
        at java.base/sun.nio.fs.UnixFileSystemProvider.implDelete(UnixFileSystemProvider.java:248) ~[na:na]
        at java.base/sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(AbstractFileSystemProvider.java:110) ~[na:na]
        at java.base/java.nio.file.Files.deleteIfExists(Files.java:1191) ~[na:na]
        at java.base/java.nio.file.Files.copy(Files.java:3147) ~[na:na]
        at io.undertow.server.handlers.form.FormData$FileItem.write(FormData.java:274) ~[undertow-core-2.3.10.Final.jar!/:2.3.10.Final]
        at io.undertow.servlet.spec.PartImpl.write(PartImpl.java:119) ~[undertow-servlet-2.3.10.Final.jar!/:2.3.10.Final]
        at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile.transferTo(StandardMultipartHttpServletRequest.java:254) ~[spring-web-6.0.13.jar!/:6.0.13]
        at org.owasp.webgoat.webwolf.FileServer.importFile(FileServer.java:89)
```

It has to do with the underlying implmentation in Undertow. An explaination can be found here: https://stackoverflow.com/questions/60336929/java-nio-file-nosuchfileexception-when-file-transferto-is-called

The solution is to take the input stream and use a simple `Files.copy()` to copy the file.

Closes: #1737
2024-07-28 17:47:30 +02:00
2b0c22ac68 Small improvements (#1848)
* refactor: remove CORS

* improvement: add healthcheck to Docker file
2024-07-23 17:42:56 +02:00
85103bbcad chore: bump docker/login-action from 3.2.0 to 3.3.0 (#1855)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-23 17:36:27 +02:00
b98e1a98e1 chore: bump docker/build-push-action from 6.2.0 to 6.4.1 (#1854)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.4.1.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.2.0...v6.4.1)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 20:33:06 +02:00
73de259809 chore: bump org.wiremock:wiremock from 3.8.0 to 3.9.0 (#1852)
Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.8.0 to 3.9.0.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.8.0...3.9.0)

---
updated-dependencies:
- dependency-name: org.wiremock:wiremock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 20:32:48 +02:00
4a804fabb6 chore: bump org.jsoup:jsoup from 1.17.2 to 1.18.1 (#1851)
Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.17.2 to 1.18.1.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.17.2...jsoup-1.18.1)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 20:32:27 +02:00
7f652dadec chore: bump org.apache.maven.plugins:maven-surefire-plugin (#1850)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.3.0...surefire-3.3.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 20:32:10 +02:00
f66dff1aeb chore: bump org.eclipse.jetty.ee10:jetty-ee10-bom (#1840)
Bumps [org.eclipse.jetty.ee10:jetty-ee10-bom](https://github.com/jetty/jetty.project) from 12.0.10 to 12.0.11.
- [Release notes](https://github.com/jetty/jetty.project/releases)
- [Commits](https://github.com/jetty/jetty.project/compare/jetty-12.0.10...jetty-12.0.11)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty.ee10:jetty-ee10-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-10 12:14:45 +02:00
777cec5a57 chore: bump io.github.bonigarcia:webdrivermanager from 5.8.0 to 5.9.1 (#1838)
Bumps [io.github.bonigarcia:webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 5.8.0 to 5.9.1.
- [Release notes](https://github.com/bonigarcia/webdrivermanager/releases)
- [Changelog](https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-5.8.0...webdrivermanager-5.9.1)

---
updated-dependencies:
- dependency-name: io.github.bonigarcia:webdrivermanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 20:53:00 +02:00
98fd280459 chore: bump org.wiremock:wiremock from 3.7.0 to 3.8.0 (#1837)
Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.7.0...3.8.0)

---
updated-dependencies:
- dependency-name: org.wiremock:wiremock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 20:52:49 +02:00
8e0addff0b chore: bump org.jruby:jruby from 9.4.7.0 to 9.4.8.0 (#1841)
Bumps org.jruby:jruby from 9.4.7.0 to 9.4.8.0.

---
updated-dependencies:
- dependency-name: org.jruby:jruby
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 20:52:41 +02:00
a30fbc223a chore: bump docker/setup-qemu-action from 2.2.0 to 3.1.0 (#1842)
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2.2.0 to 3.1.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2.2.0...v3.1.0)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-09 20:52:27 +02:00
a0b6decf34 Fix report card (#1845)
* fix: report card

Fix and simplify calculation of the number of assignments a user solved.
Rename `UserTracker` to `UserProgress`
Rename `LessonTracker` to `LessonProgress`
Rename tables in database
2024-07-09 20:07:09 +02:00
1531987da5 chore: bump docker/build-push-action from 6.1.0 to 6.2.0 (#1839)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-07 12:24:25 +02:00
bec6580c84 chore: bump docker/build-push-action from 5.4.0 to 6.1.0 (#1832)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.4.0 to 6.1.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.4.0...v6.1.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-29 10:16:10 +02:00
900702481b chore: bump org.wiremock:wiremock from 3.6.0 to 3.7.0 (#1834)
Bumps [org.wiremock:wiremock](https://github.com/wiremock/wiremock) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.6.0...3.7.0)

---
updated-dependencies:
- dependency-name: org.wiremock:wiremock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-26 09:14:21 +02:00
f35b23970e chore: bump org.apache.maven.plugins:maven-surefire-plugin (#1828)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.2.1 to 3.3.0.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.1...surefire-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-18 11:44:31 +02:00
074fd8f5ff chore: bump org.webjars:webjars-locator-core from 0.58 to 0.59 (#1827)
Bumps [org.webjars:webjars-locator-core](https://github.com/webjars/webjars-locator-core) from 0.58 to 0.59.
- [Commits](https://github.com/webjars/webjars-locator-core/compare/webjars-locator-core-0.58...webjars-locator-core-0.59)

---
updated-dependencies:
- dependency-name: org.webjars:webjars-locator-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-18 11:32:57 +02:00
8c890b090c chore: bump io.github.bonigarcia:webdrivermanager from 5.6.3 to 5.8.0 (#1826)
Bumps [io.github.bonigarcia:webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 5.6.3 to 5.8.0.
- [Release notes](https://github.com/bonigarcia/webdrivermanager/releases)
- [Changelog](https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-5.6.3...webdrivermanager-5.8.0)

---
updated-dependencies:
- dependency-name: io.github.bonigarcia:webdrivermanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-18 10:38:31 +02:00
630c1e1afd chore: bump org.ow2.asm:asm from 9.5 to 9.7 (#1829)
Bumps org.ow2.asm:asm from 9.5 to 9.7.

---
updated-dependencies:
- dependency-name: org.ow2.asm:asm
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-18 09:11:59 +02:00
061c75a05f chore: bump org.apache.commons:commons-compress from 1.26.0 to 1.26.2 (#1825)
Bumps org.apache.commons:commons-compress from 1.26.0 to 1.26.2.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-compress
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-18 08:28:49 +02:00
5e5a1363b0 chore: bump docker/build-push-action from 5.3.0 to 5.4.0 (#1824)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.3.0...v5.4.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-18 08:20:27 +02:00
47df923c8c chore: bump org.apache.maven.plugins:maven-enforcer-plugin (#1823)
Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.3.0 to 3.5.0.
- [Release notes](https://github.com/apache/maven-enforcer/releases)
- [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.3.0...enforcer-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-enforcer-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 08:20:29 +02:00
5dbc642264 chore: bump com.nulab-inc:zxcvbn from 1.8.0 to 1.9.0 (#1822)
Bumps [com.nulab-inc:zxcvbn](https://github.com/nulab/zxcvbn4j) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/nulab/zxcvbn4j/releases)
- [Changelog](https://github.com/nulab/zxcvbn4j/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nulab/zxcvbn4j/compare/1.8.0...1.9.0)

---
updated-dependencies:
- dependency-name: com.nulab-inc:zxcvbn
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 08:14:48 +02:00
96bc6fa608 chore: bump org.apache.maven.plugins:maven-checkstyle-plugin (#1821)
Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.3.1 to 3.4.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 08:09:49 +02:00
1d5a2bb6a2 chore: bump org.eclipse.jetty.ee10:jetty-ee10-bom from 12.0.3 to 12.0.10 (#1820)
Bumps [org.eclipse.jetty.ee10:jetty-ee10-bom](https://github.com/jetty/jetty.project) from 12.0.3 to 12.0.10.
- [Release notes](https://github.com/jetty/jetty.project/releases)
- [Commits](https://github.com/jetty/jetty.project/compare/jetty-12.0.3...jetty-12.0.10)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty.ee10:jetty-ee10-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 07:55:02 +02:00
8b37bd58d7 chore: bump commons-io:commons-io from 2.15.1 to 2.16.1 (#1819)
Bumps commons-io:commons-io from 2.15.1 to 2.16.1.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-14 17:41:45 +02:00
6545e2a066 chore: bump com.github.tomakehurst:wiremock (#1810)
Bumps [com.github.tomakehurst:wiremock](https://github.com/wiremock/wiremock) from 3.0.0-beta-2 to 3.0.0-beta-10.
- [Release notes](https://github.com/wiremock/wiremock/releases)
- [Commits](https://github.com/wiremock/wiremock/compare/3.0.0-beta-2...3.0.0-beta-10)

---
updated-dependencies:
- dependency-name: com.github.tomakehurst:wiremock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-08 21:15:53 +02:00
9573b30667 chore: bump org.asciidoctor:asciidoctorj from 2.5.10 to 2.5.13 (#1811)
Bumps [org.asciidoctor:asciidoctorj](https://github.com/asciidoctor/asciidoctorj) from 2.5.10 to 2.5.13.
- [Release notes](https://github.com/asciidoctor/asciidoctorj/releases)
- [Changelog](https://github.com/asciidoctor/asciidoctorj/blob/v2.5.13/CHANGELOG.adoc)
- [Commits](https://github.com/asciidoctor/asciidoctorj/compare/v2.5.10...v2.5.13)

---
updated-dependencies:
- dependency-name: org.asciidoctor:asciidoctorj
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-08 21:09:45 +02:00
30e40f2e59 chore: bump org.jruby:jruby from 9.4.3.0 to 9.4.7.0 (#1813)
Bumps org.jruby:jruby from 9.4.3.0 to 9.4.7.0.

---
updated-dependencies:
- dependency-name: org.jruby:jruby
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-08 08:23:28 +02:00
8f11fb6729 chore: bump docker/login-action from 3.0.0 to 3.2.0 (#1815)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.0.0 to 3.2.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3.0.0...v3.2.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-08 08:17:41 +02:00
10e36c203f chore: bump com.google.guava:guava from 32.1.3-jre to 33.2.1-jre (#1814)
Bumps [com.google.guava:guava](https://github.com/google/guava) from 32.1.3-jre to 33.2.1-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-08 08:13:01 +02:00
edcce09b5f chore: bump docker/build-push-action from 5.1.0 to 5.3.0 (#1816)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5.1.0 to 5.3.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.1.0...v5.3.0)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 08:08:14 +02:00
3134f18066 fix: Success if only Smith earn most salary (#1744)
* Update labels

* Update Java

* Update Test

---------

Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-01 10:50:38 +02:00
e219887f14 docs: Update HttpBasics_plan.adoc - fix broken link to https://www.zaproxy.org/ (#1803)
fix broken link OWASP ZAP -  https://www.zaproxy.org/

Co-authored-by: René Zubcevic <rene@zubcevic.com>
2024-06-01 10:45:12 +02:00
508703ffce update dependencies and version (#1807)
* update dependencies and version
* debug macos build issue
* update and fix Dockerfile(s)
2024-05-31 19:39:03 +02:00
e308d7cde7 chore: upgrade checkout out to v4 (#1781) 2024-03-25 22:27:56 +01:00
4ab820e1d1 feat: move CSRF to A3 (#1776)
CSRF is part of security misconfiguration in the OWASP Top 10.
2024-03-21 20:50:37 +01:00
1a6a7e0be1 reverting my goofs after launching from wrong browser tab (#1774) 2024-03-19 18:01:30 +01:00
2e9140ab64 Merge pull request #1773 from misfir3/test-semgrep-on-merge
Test semgrep on merge
2024-03-18 13:21:21 -06:00
b79c83a52e linty 2024-03-18 19:19:12 +00:00
297c6f49b5 Merge branch 'main' into test-semgrep-on-merge 2024-03-18 13:14:39 -06:00
d2049a8fcc updating for testing 2024-03-18 19:13:50 +00:00
24db39eae2 test semgrep 2024-03-18 19:12:13 +00:00
98443184e9 Merge pull request #1 from WebGoat/develop
updating from main branch to test semgrep
2024-03-18 13:05:23 -06:00
62931a1836 feature: enable CORS configuration (#1771) 2024-03-17 10:55:27 +01:00
c18430752a build(Dockerfile): replace deprecated MAINTAINER tag with label of the same
Current syntax now used to denote the "WebGoat team" as maintainer

Link: https://docs.docker.com/reference/dockerfile/#label

Signed-off-by: cap-dev0x <158111888+cap-dev0x@users.noreply.github.com>
2024-02-25 23:20:23 +01:00
57d5b313b9 Fix typo in SQLi blind case 2024-02-10 16:02:35 +01:00
dd0f135088 fix(quiz): use $ instead of jQuery which is undefined (#1736)
Fixes: #1703

Signed-off-by: cap-dev0x <158111888+cap-dev0x@users.noreply.github.com>
2024-02-05 14:30:01 +01:00
ad0286d5ba chore: bump actions/cache from 3.3.1 to 4.0.0 (#1729)
Bumps [actions/cache](https://github.com/actions/cache) from 3.3.1 to 4.0.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.3.1...v4.0.0)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-29 11:03:15 +01:00
b67eb44142 chore: bump io.github.bonigarcia:webdrivermanager from 5.3.3 to 5.6.3 (#1716)
Bumps [io.github.bonigarcia:webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 5.3.3 to 5.6.3.
- [Release notes](https://github.com/bonigarcia/webdrivermanager/releases)
- [Changelog](https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-5.3.3...webdrivermanager-5.6.3)

---
updated-dependencies:
- dependency-name: io.github.bonigarcia:webdrivermanager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 15:10:16 +01:00
7e75e9b8fc chore: bump org.apache.commons:commons-exec from 1.3 to 1.4.0 (#1721)
Bumps org.apache.commons:commons-exec from 1.3 to 1.4.0.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-exec
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 14:59:40 +01:00
40c679ec5a chore: bump org.jsoup:jsoup from 1.16.1 to 1.17.2 (#1717)
Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.16.1 to 1.17.2.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.16.1...jsoup-1.17.2)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 14:51:32 +01:00
4ebb869f5d Fix hidden links in MissingFunctionAC.html. (#1710) 2023-12-29 15:01:35 +01:00
6bb7a182dc Fix typos in texts. 2023-12-14 23:00:59 +01:00
cb2c99d38d Improve texts to avoid confusion. 2023-12-14 22:54:20 +01:00
84029345b4 chore: bump actions/setup-java from 3 to 4 (#1698)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-06 20:17:13 +01:00
a0ca199cdc chore: bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-06 19:39:28 +01:00
2058298e2d chore: move to SNAPSHOT 2023-12-06 17:35:12 +01:00
17acef57b4 chore: add pre-commit hooks
chore: add pre-commit hooks

chore: add pre-commit hooks

chore: add pre-commit hooks

chore: add pre-commit hooks
2023-12-06 17:16:24 +01:00
d913967ec5 refactor: remove usage of RequestMapping 2023-12-06 17:16:24 +01:00
87edc7d1db refactor: use AssertJ for testing
Majority of our test cases use AssertJ
2023-12-06 17:16:24 +01:00
ac7a9c7863 chore: update GitHub action name 2023-12-05 14:22:19 +01:00
2803ef45e4 chore: bump org.webjars:bootstrap from 5.3.1 to 5.3.2 (#1693)
Bumps [org.webjars:bootstrap](https://github.com/webjars/bootstrap) from 5.3.1 to 5.3.2.
- [Commits](https://github.com/webjars/bootstrap/compare/bootstrap-5.3.1...bootstrap-5.3.2)

---
updated-dependencies:
- dependency-name: org.webjars:bootstrap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-05 14:04:09 +01:00
5357a65e05 chore: release 2023.8 2023-12-05 11:21:15 +01:00
d343c60781 chore: do not spend time on building the Docker image
We can test this ourselves there is no need to run this on every PR towards the repository.
2023-12-05 11:15:53 +01:00
98acc1f55a fix: get the right Github token 2023-12-05 11:15:06 +01:00
f99888e61b fix: typo in the step of the name 2023-12-05 11:14:51 +01:00
29dda49190 chore: WebWolf bootstrap can now be updated 2023-12-05 11:14:27 +01:00
369be6f688 fix: disable extra build file 2023-12-05 11:14:08 +01:00
d5f869c006 chore: release version 2023.7 2023-12-04 23:10:52 +01:00
a9caaabb47 fix: wrong Docker image 2023-12-04 23:09:51 +01:00
fb2ff01775 chore: release 2023.6 2023-12-04 22:56:58 +01:00
89ecf1d2ad chore: bump actions/first-interaction from 1.2.0 to 1.3.0 (#1691)
Bumps [actions/first-interaction](https://github.com/actions/first-interaction) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/actions/first-interaction/releases)
- [Commits](https://github.com/actions/first-interaction/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: actions/first-interaction
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 22:45:16 +01:00
1b66a742da chore: bump actions/setup-java from 3 to 4 (#1690)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 22:40:54 +01:00
a831da5886 chore: bump commons-io:commons-io from 2.14.0 to 2.15.1 (#1689)
Bumps commons-io:commons-io from 2.14.0 to 2.15.1.

---
updated-dependencies:
- dependency-name: commons-io:commons-io
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 22:33:27 +01:00
fd5189c102 chore: bump com.diffplug.spotless:spotless-maven-plugin (#1688)
Bumps [com.diffplug.spotless:spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.38.0 to 2.41.1.
- [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md)
- [Commits](https://github.com/diffplug/spotless/compare/lib/2.38.0...maven/2.41.1)

---
updated-dependencies:
- dependency-name: com.diffplug.spotless:spotless-maven-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 22:24:53 +01:00
ae261f201a feat: show directly requested file in requests overview
When a call directly hits a file it is now show up in the requests overview. This helps the user whether an attack from WebGoat actually requested the uploaded file.

Closes: gh-1551
2023-12-04 21:34:16 +01:00
3d651526be feat: show creating time in file upload overview
Closes: gh-1551
2023-12-04 21:32:02 +01:00
c7c2a61f65 chore: fix startup message (#1687)
Since we use two application context, the event listener would print out the last one with the WebWolf context. As WebWolf is part of WebGoat we should not refer to it anymore during startup as users should always go to WebGoat first.
2023-12-04 07:59:29 +01:00
b7f657ad2c chore: fix WebWolf UI (#1686)
Fix-ups after the Bootstrap 5 upgrade for WebWolf.
2023-12-02 12:59:56 +01:00
7fea42afe9 Fix/state of software supply chain links (#1683)
* fix:update state of software supply chain links

* fix:fix second link

* fix:links formatting

---------

Co-authored-by: maurycupitt <maury@cupitt.com>
2023-11-27 15:33:14 +01:00
826887cc83 Consistent environment values and url references (#1677)
* organizing environment variables

* Update application-webgoat.properties

* Update pom.xml

* test without ssl

* fix docker base image and default env entries

* seperate server.address from webgoat.host and webwolf.host

* change base image and enable endpoint logging for docker as well

* change README

* change README

* make integration test able to verify against alternative host names

* use dynamic ports and remove system println
2023-11-27 14:35:49 +01:00
62db86246e chore: back to snapshot 2023-11-23 22:34:34 +01:00
526 changed files with 3618 additions and 3858 deletions

View File

@ -8,10 +8,7 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"
ignore:
- dependency-name: "org.webjars:bootstrap" # First the WebWolf UI needs to be refactored due to breaking changes
- package-ecosystem: "docker" - package-ecosystem: "docker"
directory: "/" directory: "/"
schedule: schedule:
interval: "weekly" interval: "weekly"

View File

@ -11,11 +11,11 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ] os: [ ubuntu-latest, windows-latest, macos-latest ]
java-version: [ 17, 21 ] java-version: [ 21 ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.java-version }} - name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: ${{ matrix.java-version }} java-version: ${{ matrix.java-version }}

View File

@ -1,60 +1,51 @@
name: "Pull requests build" name: "Main / Pull requests build"
on: on:
pull_request: pull_request:
paths-ignore: paths-ignore:
- '.txt' - '.txt'
- 'LICENSE' - 'LICENSE'
- 'docs/**' - 'docs/**'
branches: [ main ]
push:
branches:
- main
jobs: jobs:
pr-build: pre-commit:
if: > name: Pre-commit check
github.event_name == 'pull_request' && !github.event.pull_request.draft && ( runs-on: ubuntu-latest
github.event.action == 'opened' ||
github.event.action == 'reopened' ||
github.event.action == 'synchronize'
)
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
steps: steps:
- uses: actions/checkout@v3 - name: Checkout git repository
- name: Set up JDK 17 uses: actions/checkout@v4.1.6
uses: actions/setup-java@v3 - name: Setup python
uses: actions/setup-python@v5
with:
python-version: "3.9"
- uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 17 java-version: '21'
architecture: x64 - name: Pre-commit checks
- name: Cache Maven packages uses: pre-commit/action@v3.0.1
uses: actions/cache@v3.3.1 - name: pre-commit-ci-lite
uses: pre-commit-ci/lite-action@v1.1.0
if: always()
build:
runs-on: ${{ matrix.os }}
needs: [ pre-commit ]
strategy:
fail-fast: true
matrix:
os: [ windows-latest, ubuntu-latest, macos-13 ]
max-parallel: 1
steps:
- uses: actions/checkout@v4.1.6
- name: Set up JDK 21
uses: actions/setup-java@v4.2.1
with: with:
path: ~/.m2 distribution: 'temurin'
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} java-version: 21
restore-keys: ${{ runner.os }}-m2- architecture: x64
cache: 'maven'
- name: Build with Maven - name: Build with Maven
run: mvn --no-transfer-progress verify run: mvn --no-transfer-progress verify
- name: "Set up QEMU"
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v2.2.0
- name: "Set up Docker Buildx"
if: runner.os == 'Linux'
uses: docker/setup-buildx-action@v3
- name: "Verify Docker WebGoat build"
if: runner.os == 'Linux'
uses: docker/build-push-action@v5.1.0
with:
context: ./
file: ./Dockerfile
push: false
build-args: |
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
- name: "Verify Docker WebGoat desktop build"
uses: docker/build-push-action@v5.1.0
if: runner.os == 'Linux'
with:
context: ./
file: ./Dockerfile_desktop
push: false
build-args: |
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}

View File

@ -8,24 +8,20 @@ jobs:
if: github.repository == 'WebGoat/WebGoat' if: github.repository == 'WebGoat/WebGoat'
name: Release WebGoat name: Release WebGoat
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
environment: environment:
name: release name: release
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up JDK 17 - name: Set up JDK 21
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 17 java-version: 21
architecture: x64 architecture: x64
cache: 'maven'
- name: Cache Maven packages
uses: actions/cache@v3.3.1
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: "Set labels for ${{ github.ref }}" - name: "Set labels for ${{ github.ref }}"
run: | run: |
@ -72,7 +68,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Set up QEMU" - name: "Set up QEMU"
uses: docker/setup-qemu-action@v2.2.0 uses: docker/setup-qemu-action@v3.1.0
with: with:
platforms: all platforms: all
@ -80,18 +76,18 @@ jobs:
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
- name: "Login to dockerhub" - name: "Login to dockerhub"
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.3.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: "Build and push WebGoat" - name: "Build and push WebGoat"
uses: docker/build-push-action@v5.1.0 uses: docker/build-push-action@v6.9.0
with: with:
context: ./ context: ./
file: ./Dockerfile file: ./Dockerfile
push: true push: true
platforms: linux/amd64, linux/arm64, linux/arm/v7 platforms: linux/amd64, linux/arm64
tags: | tags: |
webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }} webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }}
webgoat/webgoat:latest webgoat/webgoat:latest
@ -99,7 +95,7 @@ jobs:
webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }}
- name: "Build and push WebGoat desktop" - name: "Build and push WebGoat desktop"
uses: docker/build-push-action@v5.1.0 uses: docker/build-push-action@v6.9.0
with: with:
context: ./ context: ./
file: ./Dockerfile_desktop file: ./Dockerfile_desktop
@ -116,15 +112,15 @@ jobs:
needs: [ release ] needs: [ release ]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Set up JDK 17 - name: Set up JDK 21
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 17 java-version: 21
architecture: x64 architecture: x64
- name: Set version to next snapshot - name: Set version to next snapshot
@ -145,4 +141,3 @@ jobs:
github_token: "${{ secrets.GITHUB_TOKEN }}" github_token: "${{ secrets.GITHUB_TOKEN }}"
title: ${{ github.event.commits[0].message }} title: ${{ github.event.commits[0].message }}
target_branch: main target_branch: main

View File

@ -21,27 +21,21 @@ jobs:
name: "Robot framework test" name: "Robot framework test"
steps: steps:
# Uses an default action to checkout the code # Uses an default action to checkout the code
- uses: actions/checkout@v3 - uses: actions/checkout@v4.1.6
# Uses an action to add Python to the VM # Uses an action to add Python to the VM
- name: Setup Pyton - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v5
with: with:
python-version: '3.7' python-version: '3.7'
architecture: x64 architecture: x64
# Uses an action to add JDK 17 to the VM (and mvn?) # Uses an action to add JDK 21 to the VM (and mvn?)
- name: set up JDK 17 - name: set up JDK 21
uses: actions/setup-java@v3 uses: actions/setup-java@v4.2.1
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 17 java-version: 21
architecture: x64 architecture: x64
#Uses an action to set up a cache using a certain key based on the hash of the dependencies cache: 'maven'
- name: Cache Maven packages
uses: actions/cache@v3.3.1
with:
path: ~/.m2
key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ubuntu-latest-m2-
- uses: BSFishy/pip-action@v1 - uses: BSFishy/pip-action@v1
with: with:
packages: | packages: |

View File

@ -10,7 +10,7 @@ jobs:
if: github.repository == 'WebGoat/WebGoat' if: github.repository == 'WebGoat/WebGoat'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/first-interaction@v1.2.0 - uses: actions/first-interaction@v1.3.0
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: 'Thanks for submitting your first issue, we will have a look as quickly as possible.' issue-message: 'Thanks for submitting your first issue, we will have a look as quickly as possible.'

28
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,28 @@
ci:
autofix_commit_msg: |
[pre-commit.ci] auto fixes from pre-commit.com hooks
autofix_prs: false # managed in the action step
autoupdate_branch: ""
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
autoupdate_schedule: weekly
skip: []
submodules: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
exclude: ^(README.md|CREATE_RELEASE.md)
- id: trailing-whitespace
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.5.0
hooks:
- id: commitlint
stages: [commit-msg]
- repo: https://github.com/ejba/pre-commit-maven
rev: v0.3.4
hooks:
- id: maven
args: [ 'clean compile' ]
- id: maven-spotless-apply

View File

@ -1,6 +1,8 @@
FROM docker.io/eclipse-temurin:19-jre-focal # We need JDK as some of the lessons needs to be able to compile Java code
LABEL NAME = "WebGoat: A deliberately insecure Web Application" FROM docker.io/eclipse-temurin:21-jdk-jammy
MAINTAINER "WebGoat team"
LABEL name="WebGoat: A deliberately insecure Web Application"
LABEL maintainer="WebGoat team"
RUN \ RUN \
useradd -ms /bin/bash webgoat && \ useradd -ms /bin/bash webgoat && \
@ -14,6 +16,8 @@ COPY --chown=webgoat target/webgoat-*.jar /home/webgoat/webgoat.jar
EXPOSE 8080 EXPOSE 8080
EXPOSE 9090 EXPOSE 9090
ENV TZ=Europe/Amsterdam
WORKDIR /home/webgoat WORKDIR /home/webgoat
ENTRYPOINT [ "java", \ ENTRYPOINT [ "java", \
"-Duser.home=/home/webgoat", \ "-Duser.home=/home/webgoat", \
@ -30,8 +34,7 @@ ENTRYPOINT [ "java", \
"--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \ "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \
"--add-opens", "java.base/java.io=ALL-UNNAMED", \ "--add-opens", "java.base/java.io=ALL-UNNAMED", \
"-Drunning.in.docker=true", \ "-Drunning.in.docker=true", \
"-Dwebgoat.host=0.0.0.0", \ "-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
"-Dwebwolf.host=0.0.0.0", \
"-Dwebgoat.port=8080", \ HEALTHCHECK --interval=5s --timeout=3s \
"-Dwebwolf.port=9090", \ CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1
"-jar", "webgoat.jar" ]

View File

@ -1,6 +1,6 @@
FROM lscr.io/linuxserver/webtop:ubuntu-xfce FROM lscr.io/linuxserver/webtop:ubuntu-xfce
LABEL NAME = "WebGoat: A deliberately insecure Web Application" LABEL NAME = "WebGoat: A deliberately insecure Web Application"
MAINTAINER "WebGoat team" LABEL maintainer = "WebGoat team"
WORKDIR /config WORKDIR /config
@ -9,26 +9,38 @@ COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh
COPY config/desktop/start_zap.sh /config/start_zap.sh COPY config/desktop/start_zap.sh /config/start_zap.sh
COPY config/desktop/WebGoat.txt /config/Desktop/ COPY config/desktop/WebGoat.txt /config/Desktop/
RUN \
apt-get update && \
apt-get --yes install vim nano gzip
RUN \ RUN \
case $(uname -m) in \ case $(uname -m) in \
x86_64) ARCH=x64;; \ x86_64) ARCH=x64;; \
aarch64) ARCH=aarch64;; \ aarch64) ARCH=aarch64;; \
*) ARCH=unknown;; \ *) ARCH=unknown;; \
esac && \ esac && \
curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \ echo ${ARCH}
tar zfxv ZAP_2.12.0_Linux.tar.gz && \
rm -rf ZAP_2.12.0_Linux.tar.gz && \ RUN \
curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz && \
tar zfxv OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ tar zfxv ZAP_2.15.0_Linux.tar.gz && \
rm -rf OpenJDK17U-jre_${ARCH}_linux_hotspot_17.0.6_10.tar.gz && \ rm -rf ZAP_2.15.0_Linux.tar.gz
RUN \
case $(uname -m) in \
x86_64) ARCH=x64;; \
aarch64) ARCH=aarch64;; \
*) ARCH=unknown;; \
esac && \
echo "oeps == ${ARCH}==" && \
curl -L https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jre_"${ARCH}"_linux_hotspot_21.0.3_9.tar.gz -o java.tar.gz && \
tar zfxv java.tar.gz && \
rm -rf java.tar.gz && \
chmod +x /config/start_webgoat.sh && \ chmod +x /config/start_webgoat.sh && \
chmod +x /config/start_zap.sh && \ chmod +x /config/start_zap.sh && \
apt-get update && \ echo "JAVA_HOME=/config/jdk-21.0.3+9-jre/" >> .bash_aliases && \
apt-get --yes install vim nano && \
echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \
echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases
ENV JAVA_HOME=/config/jdk-21.0.3+9-jre
ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre
WORKDIR /config/Desktop WORKDIR /config/Desktop

1
FAQ.md
View File

@ -5,4 +5,3 @@
### Integration tests fail ### Integration tests fail
Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line. Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line.

View File

@ -1,7 +1,7 @@
# WebGoat: A deliberately insecure Web Application # WebGoat: A deliberately insecure Web Application
[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml) [![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
[![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/) [![java-jdk](https://img.shields.io/badge/java%20jdk-21-green.svg)](https://jdk.java.net/)
[![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/) [![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/)
[![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest) [![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest)
[![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@ -44,19 +44,27 @@ Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat
``` ```
If you want to reuse the container, give it a name: For some lessons you need the container run in the same timezone. For this you can set the TZ environment variable.
E.g.
```shell ```shell
docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=America/Boise webgoat/webgoat
``` ```
As long as you don't remove the container you can use: If you want to use OWASP ZAP or another proxy, you can no longer use 127.0.0.1 or localhost. but
you can use custom host entries. For example:
```shell ```shell
docker start webgoat 127.0.0.1 www.webgoat.local www.webwolf.local
``` ```
This way, you can start where you left off. If you remove the container, you need to use `docker run` again. Then you can run the container with:
```shell
docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e WEBGOAT_HOST=www.webgoat.local -e WEBWOLF_HOST=www.webwolf.local -e TZ=America/Boise webgoat/webgoat
```
Then visit http://www.webgoat.local:8080/WebGoat/ and http://www.webwolf.local:9090/WebWolf/
## 2. Run using Docker with complete Linux Desktop ## 2. Run using Docker with complete Linux Desktop
@ -71,16 +79,27 @@ docker run -p 127.0.0.1:3000:3000 webgoat/webgoat-desktop
Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases) Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases)
```shell ```shell
java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat-2023.4.jar export TZ=Europe/Amsterdam # or your timezone
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
``` ```
Click the link in the log to start WebGoat. Click the link in the log to start WebGoat.
### 3.1 Running on a different port
If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter:
```shell
java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002
```
For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties).
## 4. Run from the sources ## 4. Run from the sources
### Prerequisites: ### Prerequisites:
* Java 17 * Java 17 or 21
* Your favorite IDE * Your favorite IDE
* Git, or Git support in your IDE * Git, or Git support in your IDE
@ -132,9 +151,10 @@ For specialist only. There is a way to set up WebGoat with a personalized menu.
For instance running as a jar on a Linux/macOS it will look like this: For instance running as a jar on a Linux/macOS it will look like this:
```Shell ```Shell
export TZ=Europe/Amsterdam # or your timezone
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
java -jar target/webgoat-2023.4-SNAPSHOT.jar java -jar target/webgoat-2023.8-SNAPSHOT.jar
``` ```
Or in a docker run it would (once this version is pushed into docker hub) look like this: Or in a docker run it would (once this version is pushed into docker hub) look like this:

View File

@ -16,19 +16,19 @@ The following steps are required when you want to add a new language
1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html) 1. Update [main_new.html](src/main/resources/webgoat/static/main_new.html)
1. Add the parts for showing the flag and providing the correct value for the flag= parameter 1. Add the parts for showing the flag and providing the correct value for the flag= parameter
2. 2.
3. Add a flag image to src/main/resources/webgoat/static/css/img 2. Add a flag image to src/main/resources/webgoat/static/css/img
1. See the main_new.html for a link to download flag resources 1. See the main_new.html for a link to download flag resources
4. Add a welcome page to the introduction lesson 3. Add a welcome page to the introduction lesson
1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish) 1. Copy Introduction_.adoc to Introduction_es.adoc (if in this case you want to add Spanish)
2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable 2. Add a highlighted section that explains that most parts of WebGoat will still be in English and invite people to translate parts where it would be valuable
5. Translate the main labels 4. Translate the main labels
1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish) 1. Copy messages.properties to messages_es.properties (if in this case you want to add Spanish)
2. Translate the label values 2. Translate the label values
6. Optionally translate lessons by 5. Optionally translate lessons by
1. Adding lang specifc adoc files in documentation folder of the lesson 1. Adding lang specifc adoc files in documentation folder of the lesson
2. Adding WebGoatLabels.properties of a specific language if you want to 2. Adding WebGoatLabels.properties of a specific language if you want to
7. Run mvn clean to see if the LabelAndHintIntegration test passes 6. Run mvn clean to see if the LabelAndHintIntegration test passes
8. Run WebGoat and verify that your own language and the other languages work as expected 7. Run WebGoat and verify that your own language and the other languages work as expected
If you only want to translate more for a certain language, you only need to do step 4-8 If you only want to translate more for a certain language, you only need to do step 4-8

View File

@ -1,5 +1,25 @@
# WebGoat release notes # WebGoat release notes
## Version 2023.8
### 🚀 New functionality
- Consistent environment values and url references (#1677)
- Show directly requested file in requests overview
- Show creating time in file upload overview
### 🐞 Bug fixes
- Fix startup message (#1687)
- Fix/state of software supply chain links (#1683)
- Fix WebWolf UI (#1686)
### 🔄 Technical tasks
- bump actions/setup-java from 3 to 4 (#1690)
- bump commons-io:commons-io from 2.14.0 to 2.15.1 (#1689)
- bump com.diffplug.spotless:spotless-maven-plugin (#1688)
## Version 2023.5 ## Version 2023.5
### New functionality ### New functionality
@ -195,4 +215,3 @@ Special thanks to the following contributors providing us with a pull request:
And everyone who provided feedback through Github. And everyone who provided feedback through Github.
Team WebGoat Team WebGoat

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
/config/jdk-17.0.6+10-jre/bin/java \ /config/jdk-21.0.3+9-jre/bin/java \
-Duser.home=/config \ -Duser.home=/config \
-Dfile.encoding=UTF-8 \ -Dfile.encoding=UTF-8 \
-DTZ=Europe/Amsterdam \ -DTZ=Europe/Amsterdam \

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar /config/jdk-21.0.3+9-jre/bin/java -jar /config/ZAP_2.15.0/zap-2.15.0.jar

View File

@ -1,4 +1,3 @@
# WebGoat landing page # WebGoat landing page
Old GitHub page which now redirects to OWASP website. Old GitHub page which now redirects to OWASP website.

203
pom.xml
View File

@ -5,12 +5,12 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version> <version>3.3.5</version>
</parent> </parent>
<groupId>org.owasp.webgoat</groupId> <groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat</artifactId> <artifactId>webgoat</artifactId>
<version>2023.5</version> <version>2024.2-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>WebGoat</name> <name>WebGoat</name>
@ -29,13 +29,6 @@
</licenses> </licenses>
<developers> <developers>
<developer>
<id>mayhew64</id>
<name>Bruce Mayhew</name>
<email>webgoat@owasp.org</email>
<organization>OWASP</organization>
<organizationUrl>https://github.com/WebGoat/WebGoat</organizationUrl>
</developer>
<developer> <developer>
<id>nbaars</id> <id>nbaars</id>
<name>Nanne Baars</name> <name>Nanne Baars</name>
@ -43,11 +36,6 @@
<organizationUrl>https://github.com/nbaars</organizationUrl> <organizationUrl>https://github.com/nbaars</organizationUrl>
<timezone>Europe/Amsterdam</timezone> <timezone>Europe/Amsterdam</timezone>
</developer> </developer>
<developer>
<id>misfir3</id>
<name>Jason White</name>
<email>jason.white@owasp.org</email>
</developer>
<developer> <developer>
<id>zubcevic</id> <id>zubcevic</id>
<name>René Zubcevic</name> <name>René Zubcevic</name>
@ -58,43 +46,8 @@
<name>Àngel Ollé Blázquez</name> <name>Àngel Ollé Blázquez</name>
<email>angel@olleb.com</email> <email>angel@olleb.com</email>
</developer> </developer>
<developer>
<id>jwayman</id>
<name>Jeff Wayman</name>
<email></email>
</developer>
<developer>
<id>dcowden</id>
<name>Dave Cowden</name>
<email></email>
</developer>
<developer>
<id>lawson89</id>
<name>Richard Lawson</name>
<email></email>
</developer>
<developer>
<id>dougmorato</id>
<name>Doug Morato</name>
<email>doug.morato@owasp.org</email>
<organization>OWASP</organization>
<organizationUrl>https://github.com/dougmorato</organizationUrl>
<timezone>America/New_York</timezone>
<properties>
<picUrl>https://avatars2.githubusercontent.com/u/9654?v=3&amp;s=150</picUrl>
</properties>
</developer>
</developers> </developers>
<mailingLists>
<mailingList>
<name>OWASP WebGoat Mailing List</name>
<subscribe>https://lists.owasp.org/mailman/listinfo/owasp-webgoat</subscribe>
<unsubscribe>Owasp-webgoat-request@lists.owasp.org</unsubscribe>
<post>owasp-webgoat@lists.owasp.org</post>
<archive>http://lists.owasp.org/pipermail/owasp-webgoat/</archive>
</mailingList>
</mailingLists>
<scm> <scm>
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection> <connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection> <developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
@ -109,62 +62,56 @@
<properties> <properties>
<!-- Shared properties with plugins and version numbers across submodules--> <!-- Shared properties with plugins and version numbers across submodules-->
<asciidoctorj.version>2.5.10</asciidoctorj.version> <asciidoctorj.version>3.0.0</asciidoctorj.version>
<bootstrap.version>5.3.1</bootstrap.version> <bootstrap.version>5.3.3</bootstrap.version>
<cglib.version>3.3.0</cglib.version> <cglib.version>3.3.0</cglib.version>
<!-- do not update necessary for lesson --> <!-- do not update necessary for lesson -->
<checkstyle.version>3.3.1</checkstyle.version> <checkstyle.version>3.6.0</checkstyle.version>
<commons-collections.version>3.2.1</commons-collections.version> <commons-collections.version>3.2.1</commons-collections.version>
<commons-io.version>2.14.0</commons-io.version> <commons-compress.version>1.27.1</commons-compress.version>
<commons-lang3.version>3.12.0</commons-lang3.version> <commons-io.version>2.17.0</commons-io.version>
<commons-text.version>1.10.0</commons-text.version> <commons-lang3.version>3.14.0</commons-lang3.version>
<guava.version>32.1.3-jre</guava.version> <commons-text.version>1.12.0</commons-text.version>
<guava.version>33.3.1-jre</guava.version>
<jacoco.version>0.8.11</jacoco.version> <jacoco.version>0.8.11</jacoco.version>
<java.version>17</java.version> <java.version>21</java.version>
<jaxb.version>2.3.1</jaxb.version> <jaxb.version>2.3.1</jaxb.version>
<jjwt.version>0.9.1</jjwt.version> <jjwt.version>0.9.1</jjwt.version>
<jose4j.version>0.9.3</jose4j.version> <jose4j.version>0.9.3</jose4j.version>
<jquery.version>3.7.0</jquery.version> <jquery.version>3.7.1</jquery.version>
<jsoup.version>1.16.1</jsoup.version> <jsoup.version>1.18.1</jsoup.version>
<maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
<maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version> <maven-failsafe-plugin.version>2.22.0</maven-failsafe-plugin.version>
<maven-jar-plugin.version>3.1.2</maven-jar-plugin.version> <maven-jar-plugin.version>3.1.2</maven-jar-plugin.version>
<maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version> <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.1.0</maven-source-plugin.version> <maven-source-plugin.version>3.1.0</maven-source-plugin.version>
<maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version> <maven-surefire-plugin.version>3.5.1</maven-surefire-plugin.version>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<pmd.version>3.15.0</pmd.version> <pmd.version>3.15.0</pmd.version>
<!-- Use UTF-8 Encoding --> <!-- Use UTF-8 Encoding -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<thymeleaf.version>3.1.1.RELEASE</thymeleaf.version> <thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
<webdriver.version>5.3.3</webdriver.version> <waittimeForServerStart>60</waittimeForServerStart>
<webdriver.version>5.9.2</webdriver.version>
<webgoat.context>/</webgoat.context> <webgoat.context>/</webgoat.context>
<webgoat.port>8080</webgoat.port> <webgoat.sslenabled>false</webgoat.sslenabled>
<webjars-locator-core.version>0.53</webjars-locator-core.version> <webjars-locator-core.version>0.59</webjars-locator-core.version>
<webwolf.context>/</webwolf.context> <webwolf.context>/</webwolf.context>
<webwolf.port>9090</webwolf.port> <wiremock.version>3.9.2</wiremock.version>
<wiremock.version>2.27.2</wiremock.version>
<xml-resolver.version>1.2</xml-resolver.version> <xml-resolver.version>1.2</xml-resolver.version>
<xstream.version>1.4.5</xstream.version> <xstream.version>1.4.5</xstream.version>
<!-- do not update necessary for lesson --> <!-- do not update necessary for lesson -->
<zxcvbn.version>1.8.0</zxcvbn.version> <zxcvbn.version>1.9.0</zxcvbn.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId> <artifactId>commons-exec</artifactId>
<version>1.3</version> <version>1.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.asciidoctor</groupId> <groupId>org.asciidoctor</groupId>
@ -249,8 +196,8 @@
<version>${webjars-locator-core.version}</version> <version>${webjars-locator-core.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.tomakehurst</groupId> <groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId> <artifactId>wiremock-standalone</artifactId>
<version>${wiremock.version}</version> <version>${wiremock.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -261,12 +208,12 @@
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId> <artifactId>commons-compress</artifactId>
<version>1.25.0</version> <version>${commons-compress.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jruby</groupId> <groupId>org.jruby</groupId>
<artifactId>jruby</artifactId> <artifactId>jruby</artifactId>
<version>9.4.3.0</version> <version>9.4.8.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -285,24 +232,26 @@
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.1</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>javax.xml.bind</groupId> <groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId> <artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version> <version>${jaxb.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -312,6 +261,10 @@
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-hsqldb</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.asciidoctor</groupId> <groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId> <artifactId>asciidoctorj</artifactId>
@ -418,6 +371,12 @@
<artifactId>jaxb-impl</artifactId> <artifactId>jaxb-impl</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.terma</groupId>
<artifactId>javaniotcpproxy</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -430,10 +389,8 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.tomakehurst</groupId> <groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId> <artifactId>wiremock-standalone</artifactId>
<version>3.0.0-beta-2</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.rest-assured</groupId> <groupId>io.rest-assured</groupId>
@ -513,10 +470,19 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<configuration> <configuration>
<environmentVariables>
<WEBGOAT_SSLENABLED>${webgoat.sslenabled}</WEBGOAT_SSLENABLED>
<WEBGOAT_HOST>127.0.0.1</WEBGOAT_HOST>
<WEBGOAT_PORT>${webgoat.port}</WEBGOAT_PORT>
<WEBGOAT_CONTEXT>${webgoat.context}</WEBGOAT_CONTEXT>
<WEBWOLF_HOST>127.0.0.1</WEBWOLF_HOST>
<WEBWOLF_PORT>${webwolf.port}</WEBWOLF_PORT>
<WEBWOLF_CONTEXT>${webwolf.context}</WEBWOLF_CONTEXT>
</environmentVariables>
<systemPropertyVariables> <systemPropertyVariables>
<logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile> <logback.configurationFile>${basedir}/src/test/resources/logback-test.xml</logback.configurationFile>
</systemPropertyVariables> </systemPropertyVariables>
<argLine>-Xmx512m -Dwebgoatport=${webgoat.port} -Dwebwolfport=${webwolf.port} -Dwebwolfcontext=${webwolf.context} -Dwebgoatcontext=${webgoat.context}</argLine> <argLine>-Xmx512m</argLine>
<includes>org/owasp/webgoat/*Test</includes> <includes>org/owasp/webgoat/*Test</includes>
</configuration> </configuration>
<executions> <executions>
@ -540,6 +506,7 @@
<version>${maven-surefire-plugin.version}</version> <version>${maven-surefire-plugin.version}</version>
<configuration> <configuration>
<forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds> <forkedProcessTimeoutInSeconds>600</forkedProcessTimeoutInSeconds>
<!-- Necessary for vulnerable components lesson -->
<argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED <argLine>--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
@ -547,8 +514,6 @@
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine> --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
<excludes> <excludes>
<exclude>**/*IntegrationTest.java</exclude> <exclude>**/*IntegrationTest.java</exclude>
<exclude>src/it/java</exclude>
<exclude>org/owasp/webgoat/*Test</exclude>
</excludes> </excludes>
</configuration> </configuration>
</plugin> </plugin>
@ -557,7 +522,6 @@
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>${checkstyle.version}</version> <version>${checkstyle.version}</version>
<configuration> <configuration>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput> <consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError> <failsOnError>true</failsOnError>
<configLocation>config/checkstyle/checkstyle.xml</configLocation> <configLocation>config/checkstyle/checkstyle.xml</configLocation>
@ -568,7 +532,7 @@
<plugin> <plugin>
<groupId>com.diffplug.spotless</groupId> <groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId> <artifactId>spotless-maven-plugin</artifactId>
<version>2.38.0</version> <version>2.41.1</version>
<configuration> <configuration>
<formats> <formats>
<format> <format>
@ -629,7 +593,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId> <artifactId>maven-enforcer-plugin</artifactId>
<version>3.3.0</version> <version>3.5.0</version>
<executions> <executions>
<execution> <execution>
<id>restrict-log4j-versions</id> <id>restrict-log4j-versions</id>
@ -653,10 +617,6 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
@ -686,16 +646,15 @@
<portNames> <portNames>
<portName>webgoat.port</portName> <portName>webgoat.port</portName>
<portName>webwolf.port</portName> <portName>webwolf.port</portName>
<portName>jmxPort</portName>
</portNames> </portNames>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>com.bazaarvoice.maven.plugins</groupId> <groupId>org.honton.chas</groupId>
<artifactId>process-exec-maven-plugin</artifactId> <artifactId>process-exec-maven-plugin</artifactId>
<version>0.9</version> <version>0.9.2</version>
<executions> <executions>
<execution> <execution>
<id>start-jar</id> <id>start-jar</id>
@ -703,8 +662,18 @@
<goal>start</goal> <goal>start</goal>
</goals> </goals>
<phase>pre-integration-test</phase> <phase>pre-integration-test</phase>
<configuration> <configuration>
<workingDir>${project.build.directory}</workingDir> <workingDir>${project.build.directory}</workingDir>
<environment>
<WEBGOAT_SSLENABLED>${webgoat.sslenabled}</WEBGOAT_SSLENABLED>
<WEBGOAT_HOST>127.0.0.1</WEBGOAT_HOST>
<WEBGOAT_PORT>${webgoat.port}</WEBGOAT_PORT>
<WEBGOAT_CONTEXT>${webgoat.context}</WEBGOAT_CONTEXT>
<WEBWOLF_HOST>127.0.0.1</WEBWOLF_HOST>
<WEBWOLF_PORT>${webwolf.port}</WEBWOLF_PORT>
<WEBWOLF_CONTEXT>${webwolf.context}</WEBWOLF_CONTEXT>
</environment>
<arguments> <arguments>
<argument>java</argument> <argument>java</argument>
<argument>-jar</argument> <argument>-jar</argument>
@ -712,10 +681,6 @@
<argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument> <argument>-Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
<argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument> <argument>-Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port}</argument>
<argument>-Dspring.main.banner-mode=off</argument> <argument>-Dspring.main.banner-mode=off</argument>
<argument>-Dwebgoat.port=${webgoat.port}</argument>
<argument>-Dwebgoat.context=${webgoat.context}</argument>
<argument>-Dwebwolf.port=${webwolf.port}</argument>
<argument>-Dwebwolf.context=${webwolf.context}</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.lang=ALL-UNNAMED</argument> <argument>java.base/java.lang=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
@ -723,26 +688,18 @@
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.lang.reflect=ALL-UNNAMED</argument> <argument>java.base/java.lang.reflect=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.text=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.desktop/java.beans=ALL-UNNAMED</argument> <argument>java.desktop/java.beans=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.desktop/java.awt.font=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument> <argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.io=ALL-UNNAMED</argument> <argument>java.base/java.io=ALL-UNNAMED</argument>
<argument>--add-opens</argument> <argument>--add-opens</argument>
<argument>java.base/java.util=ALL-UNNAMED</argument> <argument>java.base/java.util=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/sun.nio.ch=ALL-UNNAMED</argument>
<argument>--add-opens</argument>
<argument>java.base/java.io=ALL-UNNAMED</argument>
<argument>${project.build.directory}/webgoat-${project.version}.jar</argument> <argument>${project.build.directory}/webgoat-${project.version}.jar</argument>
</arguments> </arguments>
<waitForInterrupt>false</waitForInterrupt> <waitForInterrupt>false</waitForInterrupt>
<waitAfterLaunch>120</waitAfterLaunch> <waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
<healthcheckUrl>http://localhost:${webgoat.port}/WebGoat/actuator/health</healthcheckUrl> <healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}login</healthCheckUrl>
</configuration> </configuration>
</execution> </execution>
<execution> <execution>
@ -767,7 +724,6 @@
<plugin> <plugin>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId> <artifactId>dependency-check-maven</artifactId>
<version>8.4.3</version>
<configuration> <configuration>
<failBuildOnCVSS>7</failBuildOnCVSS> <failBuildOnCVSS>7</failBuildOnCVSS>
<skipProvidedScope>false</skipProvidedScope> <skipProvidedScope>false</skipProvidedScope>
@ -816,7 +772,6 @@
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions> <executions>
<execution> <execution>
<id>before-unit-test</id> <id>before-unit-test</id>

View File

@ -15,7 +15,7 @@ class AccessControlIntegrationTest extends IntegrationTest {
assignment2(); assignment2();
assignment3(); assignment3();
checkResults("/access-control"); checkResults("MissingFunctionAC");
} }
private void assignment3() { private void assignment3() {

View File

@ -86,7 +86,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
// logout(); // logout();
login(); // because old cookie got replaced and invalidated login(); // because old cookie got replaced and invalidated
startLesson("CSRF", false); startLesson("CSRF", false);
checkResults("/csrf"); checkResults("CSRF");
} }
private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException {
@ -103,7 +103,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", htmlName, htmlContent.getBytes()) .multiPart("file", htmlName, htmlContent.getBytes())
.post(webWolfUrl("fileupload")) .post(new WebWolfUrlBuilder().path("fileupload").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -118,7 +118,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("files/" + this.getUser() + "/" + htmlName)) .get(new WebWolfUrlBuilder().path("files/%s/%s", this.getUser(), htmlName).build())
.then() .then()
.extract() .extract()
.response() .response()
@ -136,7 +136,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrl("files/fake.html")) .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
.post(goatURL) .post(goatURL)
.then() .then()
.extract() .extract()
@ -163,7 +163,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrl("files/fake.html")) .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
.formParams(params) .formParams(params)
.post(goatURL) .post(goatURL)
.then() .then()
@ -184,7 +184,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrl("files/fake.html")) .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
.contentType(ContentType.TEXT) .contentType(ContentType.TEXT)
.body( .body(
"{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is" "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is"
@ -217,7 +217,7 @@ public class CSRFIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.header("Referer", webWolfUrl("files/fake.html")) .header("Referer", new WebWolfUrlBuilder().path("files/fake.html").build())
.params(params) .params(params)
.post(goatURL) .post(goatURL)
.then() .then()
@ -253,15 +253,16 @@ public class CSRFIntegrationTest extends IntegrationTest {
Overview[] assignments = Overview[] assignments =
RestAssured.given() RestAssured.given()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/lessonoverview.mvc")) .relaxedHTTPSValidation()
.get(url("service/lessonoverview.mvc/CSRF"))
.then() .then()
.extract() .extract()
.jsonPath() .jsonPath()
.getObject("$", Overview[].class); .getObject("$", Overview[].class);
// assertThat(assignments) assertThat(assignments)
// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin"))
// .extracting(o -> o.solved) .extracting(o -> o.solved)
// .containsExactly(true); .containsExactly(true);
} }
@Data @Data

View File

@ -50,9 +50,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
params.clear(); params.clear();
params.put("flag", flag); params.put("flag", flag);
checkAssignment(url("challenge/flag"), params, true); checkAssignment(url("challenge/flag/1"), params, true);
checkResults("/challenge/1"); checkResults("Challenge1");
List<String> capturefFlags = List<String> capturefFlags =
RestAssured.given() RestAssured.given()
@ -92,9 +92,9 @@ public class ChallengeIntegrationTest extends IntegrationTest {
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
params.clear(); params.clear();
params.put("flag", flag); params.put("flag", flag);
checkAssignment(url("challenge/flag"), params, true); checkAssignment(url("challenge/flag/5"), params, true);
checkResults("/challenge/5"); checkResults("Challenge5");
List<String> capturefFlags = List<String> capturefFlags =
RestAssured.given() RestAssured.given()
@ -126,7 +126,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.extract() .extract()
.asString(); .asString();
// Should send an email to WebWolf inbox this should give a hint to the link being static // Should email WebWolf inbox this should give a hint to the link being static
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
@ -144,7 +144,7 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("mail")) .get(new WebWolfUrlBuilder().path("mail").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -165,6 +165,6 @@ public class ChallengeIntegrationTest extends IntegrationTest {
.asString(); .asString();
String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42);
checkAssignment(url("challenge/flag"), Map.of("flag", flag), true); checkAssignment(url("challenge/flag/7"), Map.of("flag", flag), true);
} }
} }

View File

@ -42,7 +42,7 @@ public class CryptoIntegrationTest extends IntegrationTest {
checkAssignmentDefaults(); checkAssignmentDefaults();
checkResults("/crypto"); checkResults("Cryptography");
} }
private void checkAssignment2() { private void checkAssignment2() {

View File

@ -28,6 +28,6 @@ public class DeserializationIntegrationTest extends IntegrationTest {
} }
checkAssignment(url("InsecureDeserialization/task"), params, true); checkAssignment(url("InsecureDeserialization/task"), params, true);
checkResults("/InsecureDeserialization/"); checkResults("InsecureDeserialization");
} }
} }

View File

@ -31,7 +31,17 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("magic_num", "33"); params.put("magic_num", "33");
checkAssignment(url("HttpBasics/attack2"), params, true); checkAssignment(url("HttpBasics/attack2"), params, true);
checkResults("/HttpBasics/"); checkResults("HttpBasics");
}
@Test
public void solveAsOtherUserHttpBasics() {
login("steven");
startLesson("HttpBasics");
Map<String, Object> params = new HashMap<>();
params.clear();
params.put("person", "goatuser");
checkAssignment(url("HttpBasics/attack1"), params, true);
} }
@Test @Test
@ -51,7 +61,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
.path("lessonCompleted"), .path("lessonCompleted"),
CoreMatchers.is(true)); CoreMatchers.is(true));
checkResults("/HttpProxies/"); checkResults("HttpProxies");
} }
@Test @Test
@ -73,7 +83,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
"question_3_solution", "question_3_solution",
"Solution 2: The systems security is compromised even if only one goal is harmed."); "Solution 2: The systems security is compromised even if only one goal is harmed.");
checkAssignment(url("cia/quiz"), params, true); checkAssignment(url("cia/quiz"), params, true);
checkResults("/cia/"); checkResults("CIA");
} }
@Test @Test
@ -96,7 +106,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("payload", solution); params.put("payload", solution);
checkAssignment(url("VulnerableComponents/attack1"), params, true); checkAssignment(url("VulnerableComponents/attack1"), params, true);
checkResults("/VulnerableComponents/"); checkResults("VulnerableComponents");
} }
} }
@ -108,7 +118,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("username", "CaptainJack"); params.put("username", "CaptainJack");
params.put("password", "BlackPearl"); params.put("password", "BlackPearl");
checkAssignment(url("InsecureLogin/task"), params, true); checkAssignment(url("InsecureLogin/task"), params, true);
checkResults("/InsecureLogin/"); checkResults("InsecureLogin");
} }
@Test @Test
@ -118,7 +128,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear(); params.clear();
params.put("password", "ajnaeliclm^&&@kjn."); params.put("password", "ajnaeliclm^&&@kjn.");
checkAssignment(url("SecurePasswords/assignment"), params, true); checkAssignment(url("SecurePasswords/assignment"), params, true);
checkResults("SecurePasswords/"); checkResults("SecurePasswords");
startLesson("AuthBypass"); startLesson("AuthBypass");
params.clear(); params.clear();
@ -128,7 +138,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("verifyMethod", "SEC_QUESTIONS"); params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746"); params.put("userId", "12309746");
checkAssignment(url("auth-bypass/verify-account"), params, true); checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("/auth-bypass/"); checkResults("AuthBypass");
startLesson("HttpProxies"); startLesson("HttpProxies");
MatcherAssert.assertThat( MatcherAssert.assertThat(
@ -144,7 +154,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
.extract() .extract()
.path("lessonCompleted"), .path("lessonCompleted"),
CoreMatchers.is(true)); CoreMatchers.is(true));
checkResults("/HttpProxies/"); checkResults("HttpProxies");
} }
@Test @Test
@ -180,7 +190,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("network_num", "24"); params.put("network_num", "24");
checkAssignment(url("ChromeDevTools/network"), params, true); checkAssignment(url("ChromeDevTools/network"), params, true);
checkResults("/ChromeDevTools/"); checkResults("ChromeDevTools");
} }
@Test @Test
@ -194,7 +204,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("verifyMethod", "SEC_QUESTIONS"); params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746"); params.put("userId", "12309746");
checkAssignment(url("auth-bypass/verify-account"), params, true); checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("/auth-bypass/"); checkResults("AuthBypass");
} }
@Test @Test
@ -205,6 +215,6 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("param1", "secr37Value"); params.put("param1", "secr37Value");
params.put("param2", "Main"); params.put("param2", "Main");
checkAssignment(url("lesson-template/sample-attack"), params, true); checkAssignment(url("lesson-template/sample-attack"), params, true);
checkResults("/lesson-template/"); checkResults("LessonTemplate");
} }
} }

View File

@ -30,7 +30,7 @@ public class IDORIntegrationTest extends IntegrationTest {
@AfterEach @AfterEach
public void shutdown() { public void shutdown() {
checkResults("/IDOR"); checkResults("IDOR");
} }
private void loginIDOR() { private void loginIDOR() {

View File

@ -3,9 +3,9 @@ package org.owasp.webgoat;
import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.given;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import lombok.Getter; import lombok.Getter;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert; import org.hamcrest.MatcherAssert;
@ -15,41 +15,81 @@ import org.springframework.http.HttpStatus;
public abstract class IntegrationTest { public abstract class IntegrationTest {
private static String webGoatPort = private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
Objects.requireNonNull(System.getProperty("webgoatport", "8080")); @Getter private static String webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090");
private static String webGoatContext =
Objects.requireNonNull(System.getProperty("webgoatcontext", "/WebGoat/"));
@Getter @Getter
private static String webWolfPort = private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
Objects.requireNonNull(System.getProperty("webwolfport", "9090"));
private static String webGoatContext =
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
private static String webWolfContext = private static String webWolfContext =
Objects.requireNonNull(System.getProperty("webwolfcontext", "/WebWolf/")); System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
private static boolean useSSL = false;
private static String webgoatUrl =
(useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + webGoatContext;
private static String webWolfUrl =
(useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + webWolfContext;
@Getter private String webGoatCookie; @Getter private String webGoatCookie;
@Getter private String webWolfCookie; @Getter private String webWolfCookie;
@Getter private final String user = "webgoat"; @Getter private final String user = "webgoat";
protected String url(String url) { protected String url(String url) {
return webgoatUrl + url; return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url);
} }
protected String webWolfUrl(String url) { protected class WebWolfUrlBuilder {
return webWolfUrl + url;
private boolean attackMode = false;
private String path = null;
protected String build() {
return "http://localhost:%s%s%s"
.formatted(webWolfPort, webWolfContext, path != null ? path : "");
}
/**
* In attack mode it means WebGoat calls WebWolf to perform an attack. In this case we need to
* use port 9090 in a Docker environment.
*/
protected WebWolfUrlBuilder attackMode() {
attackMode = true;
return this;
}
protected WebWolfUrlBuilder path(String path) {
this.path = path;
return this;
}
protected WebWolfUrlBuilder path(String path, String... uriVariables) {
this.path = path.formatted(uriVariables);
return this;
}
} }
protected String webWolfFileUrl(String fileName) { /**
return webWolfUrl("files") + "/" + getUser() + "/" + fileName; * Debugging options: install TestContainers Desktop and map port 5005 to the host machine with
} * https://newsletter.testcontainers.com/announcements/set-fixed-ports-to-easily-debug-development-services
*
* <p>Start the test and connect a remote debugger in IntelliJ to localhost:5005 and attach it.
*/
// private static GenericContainer<?> webGoatContainer =
// new GenericContainer(new ImageFromDockerfile("webgoat").withFileFromPath("/",
// Paths.get(".")))
// .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("webgoat")))
// .withExposedPorts(8080, 9090, 5005)
// .withEnv(
// "_JAVA_OPTIONS",
// "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005")
// .waitingFor(Wait.forHealthcheck());
//
// static {
// webGoatContainer.start();
// }
@BeforeEach @BeforeEach
public void login() { public void login() {
login("webgoat");
}
protected void login(String user) {
String location = String location =
given() given()
.when() .when()
@ -58,6 +98,8 @@ public abstract class IntegrationTest {
.formParam("password", "password") .formParam("password", "password")
.post(url("login")) .post(url("login"))
.then() .then()
.log()
.ifValidationFails(LogDetail.ALL) // Log the response details if validation fails
.cookie("JSESSIONID") .cookie("JSESSIONID")
.statusCode(302) .statusCode(302)
.extract() .extract()
@ -98,7 +140,7 @@ public abstract class IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.formParam("username", user) .formParam("username", user)
.formParam("password", "password") .formParam("password", "password")
.post(webWolfUrl("login")) .post(new WebWolfUrlBuilder().path("login").build())
.then() .then()
.statusCode(302) .statusCode(302)
.cookie("WEBWOLFSESSION") .cookie("WEBWOLFSESSION")
@ -129,7 +171,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/restartlesson.mvc")) .get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName)))
.then() .then()
.statusCode(200); .statusCode(200);
} }
@ -165,23 +207,18 @@ public abstract class IntegrationTest {
CoreMatchers.is(expectedResult)); CoreMatchers.is(expectedResult));
} }
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are public void checkResults(String lesson) {
// only required to be in the same package) var result =
public void checkResults(String prefix) {
checkResults();
MatcherAssert.assertThat(
RestAssured.given() RestAssured.given()
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/lessonoverview.mvc")) .get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson)))
.then() .andReturn();
.statusCode(200)
.extract() MatcherAssert.assertThat(
.jsonPath() result.then().statusCode(200).extract().jsonPath().getList("solved"),
.getList("assignment.path"), CoreMatchers.everyItem(CoreMatchers.is(true)));
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
} }
public void checkResults() { public void checkResults() {
@ -236,7 +273,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("file-server-location")) .get(new WebWolfUrlBuilder().path("file-server-location").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -264,7 +301,7 @@ public abstract class IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.delete(webWolfUrl("mail")) .delete(new WebWolfUrlBuilder().path("mail").build())
.then() .then()
.statusCode(HttpStatus.ACCEPTED.value()); .statusCode(HttpStatus.ACCEPTED.value());
} }

View File

@ -13,7 +13,6 @@ import io.jsonwebtoken.impl.TextCodec;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -34,7 +33,7 @@ import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint;
public class JWTLessonIntegrationTest extends IntegrationTest { public class JWTLessonIntegrationTest extends IntegrationTest {
@Test @Test
public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { public void solveAssignment() throws IOException, NoSuchAlgorithmException {
startLesson("JWT"); startLesson("JWT");
decodingToken(); decodingToken();
@ -51,11 +50,10 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
quiz(); quiz();
checkResults("/JWT/"); checkResults("JWT");
} }
private String generateToken(String key) { private String generateToken(String key) {
return Jwts.builder() return Jwts.builder()
.setIssuer("WebGoat Token Builder") .setIssuer("WebGoat Token Builder")
.setAudience("webgoat.org") .setAudience("webgoat.org")
@ -96,7 +94,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
CoreMatchers.is(true)); CoreMatchers.is(true));
} }
private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { private void findPassword() {
String accessToken = String accessToken =
RestAssured.given() RestAssured.given()
@ -256,7 +254,7 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "jwks.json", jwks.toJson().getBytes()) .multiPart("file", "jwks.json", jwks.toJson().getBytes())
.post(webWolfUrl("fileupload")) .post(new WebWolfUrlBuilder().path("fileupload").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -265,7 +263,10 @@ public class JWTLessonIntegrationTest extends IntegrationTest {
Map<String, Object> header = new HashMap(); Map<String, Object> header = new HashMap();
header.put(Header.TYPE, Header.JWT_TYPE); header.put(Header.TYPE, Header.JWT_TYPE);
header.put(JwsHeader.JWK_SET_URL, webWolfFileUrl("jwks.json")); header.put(
JwsHeader.JWK_SET_URL,
new WebWolfUrlBuilder().attackMode().path("files/%s/jwks.json", getUser()).build());
String token = String token =
Jwts.builder() Jwts.builder()
.setHeader(header) .setHeader(header)

View File

@ -151,7 +151,6 @@ public class LabelAndHintIntegrationTest extends IntegrationTest {
checkLang(propsDefault, "nl"); checkLang(propsDefault, "nl");
checkLang(propsDefault, "de"); checkLang(propsDefault, "de");
checkLang(propsDefault, "fr"); checkLang(propsDefault, "fr");
checkLang(propsDefault, "ru");
} }
private Properties getProperties(String lang) { private Properties getProperties(String lang) {

View File

@ -69,7 +69,6 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
// WebWolf // WebWolf
var link = getPasswordResetLinkFromLandingPage(); var link = getPasswordResetLinkFromLandingPage();
// WebGoat // WebGoat
changePassword(link); changePassword(link);
checkAssignment( checkAssignment(
@ -86,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("mail")) .get(new WebWolfUrlBuilder().path("mail").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -100,7 +99,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
public void shutdown() { public void shutdown() {
// this will run only once after the list of dynamic tests has run, this is to test if the // this will run only once after the list of dynamic tests has run, this is to test if the
// lesson is marked complete // lesson is marked complete
checkResults("/PasswordReset"); checkResults("PasswordReset");
} }
private void changePassword(String link) { private void changePassword(String link) {
@ -120,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("requests")) .get(new WebWolfUrlBuilder().path("requests").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -137,7 +136,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
private void clickForgotEmailLink(String user) { private void clickForgotEmailLink(String user) {
RestAssured.given() RestAssured.given()
.when() .when()
.header(HttpHeaders.HOST, String.format("%s:%s", "127.0.0.1", getWebWolfPort())) .header(HttpHeaders.HOST, String.format("%s:%s", getWebWolfHost(), getWebWolfPort()))
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams("email", user) .formParams("email", user)

View File

@ -147,6 +147,6 @@ class PathTraversalIT extends IntegrationTest {
void shutdown() { void shutdown() {
// this will run only once after the list of dynamic tests has run, this is to test if the // this will run only once after the list of dynamic tests has run, this is to test if the
// lesson is marked complete // lesson is marked complete
checkResults("/PathTraversal"); checkResults("PathTraversal");
} }
} }

View File

@ -29,7 +29,7 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) .cookie("JSESSIONID", getWebGoatCookie())
.formParams(Map.of("flag", "test")) .formParams(Map.of("flag", "test"))
.post(url("challenge/flag")); .post(url("challenge/flag/1"));
}; };
ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS); ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS);
List<? extends Callable<Response>> flagCalls = List<? extends Callable<Response>> flagCalls =

View File

@ -1,6 +1,5 @@
package org.owasp.webgoat; package org.owasp.webgoat;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -8,7 +7,7 @@ import org.junit.jupiter.api.Test;
public class SSRFIntegrationTest extends IntegrationTest { public class SSRFIntegrationTest extends IntegrationTest {
@Test @Test
public void runTests() throws IOException { public void runTests() {
startLesson("SSRF"); startLesson("SSRF");
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
@ -21,6 +20,6 @@ public class SSRFIntegrationTest extends IntegrationTest {
checkAssignment(url("SSRF/task2"), params, true); checkAssignment(url("SSRF/task2"), params, true);
checkResults("/SSRF/"); checkResults("SSRF");
} }
} }

View File

@ -56,6 +56,6 @@ public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest {
"Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'.");
checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true); checkAssignment(url("SqlInjectionAdvanced/quiz"), params, true);
checkResults("/SqlInjectionAdvanced/"); checkResults("SqlInjectionAdvanced");
} }
} }

View File

@ -73,6 +73,6 @@ public class SqlInjectionLessonIntegrationTest extends IntegrationTest {
params.put("action_string", sql_13); params.put("action_string", sql_13);
checkAssignment(url("SqlInjection/attack10"), params, true); checkAssignment(url("SqlInjection/attack10"), params, true);
checkResults("/SqlInjection/"); checkResults("SqlInjection");
} }
} }

View File

@ -80,6 +80,6 @@ public class SqlInjectionMitigationIntegrationTest extends IntegrationTest {
params.put("ip", "104.130.219.202"); params.put("ip", "104.130.219.202");
checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true); checkAssignment(url("SqlInjectionMitigations/attack12a"), params, true);
checkResults(); checkResults("SqlInjectionMitigations");
} }
} }

View File

@ -23,7 +23,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("mail")) .get(new WebWolfUrlBuilder().path("mail").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -53,7 +53,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.queryParams(params) .queryParams(params)
.get(webWolfUrl("landing")) .get(new WebWolfUrlBuilder().path("landing").build())
.then() .then()
.statusCode(200); .statusCode(200);
responseBody = responseBody =
@ -61,7 +61,7 @@ public class WebWolfIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("requests")) .get(new WebWolfUrlBuilder().path("requests").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -72,6 +72,6 @@ public class WebWolfIntegrationTest extends IntegrationTest {
params.put("uniqueCode", uniqueCode); params.put("uniqueCode", uniqueCode);
checkAssignment(url("WebWolf/landing"), params, true); checkAssignment(url("WebWolf/landing"), params, true);
checkResults("/WebWolf"); checkResults("WebWolfIntroduction");
} }
} }

View File

@ -111,6 +111,6 @@ public class XSSIntegrationTest extends IntegrationTest {
+ "MyCommentDAO.addComment(threadID, userID).getCleanHTML());"); + "MyCommentDAO.addComment(threadID, userID).getCleanHTML());");
checkAssignment(url("CrossSiteScripting/attack4"), params, true); checkAssignment(url("CrossSiteScripting/attack4"), params, true);
checkResults("/CrossSiteScripting"); checkResults("CrossSiteScripting");
} }
} }

View File

@ -3,9 +3,6 @@ package org.owasp.webgoat;
import io.restassured.RestAssured; import io.restassured.RestAssured;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class XXEIntegrationTest extends IntegrationTest { public class XXEIntegrationTest extends IntegrationTest {
@ -28,47 +25,40 @@ public class XXEIntegrationTest extends IntegrationTest {
"""; """;
private String webGoatHomeDirectory; private String webGoatHomeDirectory;
private String webWolfFileServerLocation;
/* // TODO fix me
* This test is to verify that all is secure when XXE security patch is applied. // /*
*/ // * This test is to verify that all is secure when XXE security patch is applied.
@Test // */
public void xxeSecure() throws IOException { // @Test
startLesson("XXE"); // public void xxeSecure() throws IOException {
webGoatHomeDirectory = webGoatServerDirectory(); // startLesson("XXE");
webWolfFileServerLocation = getWebWolfFileServerLocation(); // webGoatHomeDirectory = webGoatServerDirectory();
RestAssured.given() // RestAssured.given()
.when() // .when()
.relaxedHTTPSValidation() // .relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie()) // .cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/enable-security.mvc")) // .get(url("service/enable-security.mvc"))
.then() // .then()
.statusCode(200); // .statusCode(200);
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false); // checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, false);
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false); // checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, false);
checkAssignment( // checkAssignment(
url("xxe/blind"), // url("xxe/blind"),
ContentType.XML, // ContentType.XML,
"<comment><text>" + getSecret() + "</text></comment>", // "<comment><text>" + getSecret() + "</text></comment>",
false); // false);
} // }
/** /**
* This performs the steps of the exercise before the secret can be committed in the final step. * This performs the steps of the exercise before the secret can be committed in the final step.
* *
* @return * @return
* @throws IOException
*/ */
private String getSecret() throws IOException { private String getSecret() {
// remove any left over DTD
Path webWolfFilePath = Paths.get(webWolfFileServerLocation);
if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) {
Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")));
}
String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt");
String dtd7String = String webWolfCallback = new WebWolfUrlBuilder().path("landing").attackMode().build();
dtd7.replace("WEBWOLFURL", webWolfUrl("landing")).replace("SECRET", secretFile); String dtd7String = dtd7.replace("WEBWOLFURL", webWolfCallback).replace("SECRET", secretFile);
// upload DTD // upload DTD
RestAssured.given() RestAssured.given()
@ -76,15 +66,17 @@ public class XXEIntegrationTest extends IntegrationTest {
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.multiPart("file", "blind.dtd", dtd7String.getBytes()) .multiPart("file", "blind.dtd", dtd7String.getBytes())
.post(webWolfUrl("fileupload")) .post(new WebWolfUrlBuilder().path("fileupload").build())
.then() .then()
.extract() .extract()
.response() .response()
.getBody() .getBody()
.asString(); .asString();
// upload attack // upload attack
String xxe7String = String xxe7String =
xxe7.replace("WEBWOLFURL", webWolfUrl("files")).replace("USERNAME", this.getUser()); xxe7.replace("WEBWOLFURL", new WebWolfUrlBuilder().attackMode().path("files").build())
.replace("USERNAME", this.getUser());
checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false); checkAssignment(url("xxe/blind"), ContentType.XML, xxe7String, false);
// read results from WebWolf // read results from WebWolf
@ -93,7 +85,7 @@ public class XXEIntegrationTest extends IntegrationTest {
.when() .when()
.relaxedHTTPSValidation() .relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie()) .cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("requests")) .get(new WebWolfUrlBuilder().path("requests").build())
.then() .then()
.extract() .extract()
.response() .response()
@ -113,7 +105,6 @@ public class XXEIntegrationTest extends IntegrationTest {
public void runTests() throws IOException { public void runTests() throws IOException {
startLesson("XXE", true); startLesson("XXE", true);
webGoatHomeDirectory = webGoatServerDirectory(); webGoatHomeDirectory = webGoatServerDirectory();
webWolfFileServerLocation = getWebWolfFileServerLocation();
checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true); checkAssignment(url("xxe/simple"), ContentType.XML, xxe3, true);
checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true); checkAssignment(url("xxe/content-type"), ContentType.XML, xxe4, true);
checkAssignment( checkAssignment(
@ -121,6 +112,6 @@ public class XXEIntegrationTest extends IntegrationTest {
ContentType.XML, ContentType.XML,
"<comment><text>" + getSecret() + "</text></comment>", "<comment><text>" + getSecret() + "</text></comment>",
true); true);
checkResults("xxe/"); checkResults("XXE");
} }
} }

View File

@ -32,22 +32,23 @@ package org.owasp.webgoat.container;
import static org.asciidoctor.Asciidoctor.Factory.create; import static org.asciidoctor.Asciidoctor.Factory.create;
import io.undertow.util.Headers;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.asciidoctor.Asciidoctor; import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.Options;
import org.asciidoctor.extension.JavaExtensionRegistry; import org.asciidoctor.extension.JavaExtensionRegistry;
import org.owasp.webgoat.container.asciidoc.*; import org.owasp.webgoat.container.asciidoc.*;
import org.owasp.webgoat.container.i18n.Language; import org.owasp.webgoat.container.i18n.Language;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.springframework.web.servlet.i18n.SessionLocaleResolver;
@ -135,17 +136,17 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
return computedResourceName; return computedResourceName;
} }
private Map<String, Object> createAttributes() { private Options createAttributes() {
Map<String, Object> attributes = new HashMap<>();
attributes.put("source-highlighter", "coderay");
attributes.put("backend", "xhtml");
attributes.put("lang", determineLanguage());
attributes.put("icons", org.asciidoctor.Attributes.FONT_ICONS);
Map<String, Object> options = new HashMap<>(); return Options.builder()
options.put("attributes", attributes); .attributes(
Attributes.builder()
return options; .attribute("source-highlighter", "coderay")
.attribute("backend", "xhtml")
.attribute("lang", determineLanguage())
.attribute("icons", org.asciidoctor.Attributes.FONT_ICONS)
.build())
.build();
} }
private String determineLanguage() { private String determineLanguage() {
@ -159,7 +160,7 @@ public class AsciiDoctorTemplateResolver extends FileTemplateResolver {
log.debug("browser locale {}", browserLocale); log.debug("browser locale {}", browserLocale);
return browserLocale.getLanguage(); return browserLocale.getLanguage();
} else { } else {
String langHeader = request.getHeader(Headers.ACCEPT_LANGUAGE_STRING); String langHeader = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
if (null != langHeader) { if (null != langHeader) {
log.debug("browser locale {}", langHeader); log.debug("browser locale {}", langHeader);
return langHeader.substring(0, 2); return langHeader.substring(0, 2);

View File

@ -0,0 +1,14 @@
package org.owasp.webgoat.container;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {}

View File

@ -0,0 +1,14 @@
package org.owasp.webgoat.container;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "#this.getUsername()")
public @interface CurrentUsername {}

View File

@ -6,8 +6,8 @@ import javax.sql.DataSource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.owasp.webgoat.container.lessons.LessonScanner;
import org.owasp.webgoat.container.service.RestartLessonService; import org.owasp.webgoat.container.service.RestartLessonService;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -20,7 +20,6 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
public class DatabaseConfiguration { public class DatabaseConfiguration {
private final DataSourceProperties properties; private final DataSourceProperties properties;
private final LessonScanner lessonScanner;
@Bean @Bean
@Primary @Primary
@ -36,8 +35,8 @@ public class DatabaseConfiguration {
/** /**
* Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users * Define 2 Flyway instances, 1 for WebGoat itself which it uses for internal storage like users
* and 1 for lesson specific tables we use. This way we clean the data in the lesson database * and 1 for lesson specific tables we use. This way we clean the data in the lesson database
* quite easily see {@link RestartLessonService#restartLesson()} for how we clean the lesson * quite easily see {@link RestartLessonService#restartLesson(String, WebGoatUser)} for how we
* related tables. * clean the lesson related tables.
*/ */
@Bean(initMethod = "migrate") @Bean(initMethod = "migrate")
public Flyway flyWayContainer() { public Flyway flyWayContainer() {
@ -62,7 +61,7 @@ public class DatabaseConfiguration {
} }
@Bean @Bean
public LessonDataSource lessonDataSource() { public LessonDataSource lessonDataSource(DataSource dataSource) {
return new LessonDataSource(dataSource()); return new LessonDataSource(dataSource);
} }
} }

View File

@ -32,30 +32,33 @@
package org.owasp.webgoat.container; package org.owasp.webgoat.container;
import java.io.File; import java.io.File;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.UserRepository; import org.owasp.webgoat.container.users.UserRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@Configuration @Configuration
@ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"}) @ComponentScan(basePackages = {"org.owasp.webgoat.container", "org.owasp.webgoat.lessons"})
@PropertySource("classpath:application-webgoat.properties") @PropertySource("classpath:application-webgoat.properties")
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableJpaRepositories(basePackages = {"org.owasp.webgoat.container"})
@EntityScan(basePackages = "org.owasp.webgoat.container")
public class WebGoat { public class WebGoat {
@Autowired private UserRepository userRepository; private final UserRepository userRepository;
public WebGoat(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Bean(name = "pluginTargetDirectory") @Bean(name = "pluginTargetDirectory")
public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) { public File pluginTargetDirectory(@Value("${webgoat.user.directory}") final String webgoatHome) {
@ -64,21 +67,8 @@ public class WebGoat {
@Bean @Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public WebSession webSession() { public LessonSession userSessionData() {
WebGoatUser webGoatUser = null; return new LessonSession();
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof WebGoatUser) {
webGoatUser = (WebGoatUser) principal;
} else if (principal instanceof DefaultOAuth2User) {
webGoatUser = userRepository.findByUsername(((DefaultOAuth2User) principal).getName());
}
return new WebSession(webGoatUser);
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserSessionData userSessionData() {
return new UserSessionData("test", "data");
} }
@Bean @Bean

View File

@ -35,6 +35,7 @@ import org.owasp.webgoat.container.users.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
@ -57,7 +58,6 @@ public class WebSecurityConfig {
return http.authorizeHttpRequests( return http.authorizeHttpRequests(
auth -> auth ->
auth.requestMatchers( auth.requestMatchers(
"/",
"/favicon.ico", "/favicon.ico",
"/css/**", "/css/**",
"/images/**", "/images/**",
@ -65,7 +65,8 @@ public class WebSecurityConfig {
"fonts/**", "fonts/**",
"/plugins/**", "/plugins/**",
"/registration", "/registration",
"/register.mvc") "/register.mvc",
"/actuator/**")
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated()) .authenticated())
@ -97,6 +98,7 @@ public class WebSecurityConfig {
} }
@Bean @Bean
@Primary
public UserDetailsService userDetailsServiceBean() { public UserDetailsService userDetailsServiceBean() {
return userDetailsService; return userDetailsService;
} }

View File

@ -16,7 +16,7 @@ public class EnvironmentExposure implements ApplicationContextAware {
private static ApplicationContext context; private static ApplicationContext context;
public static Environment getEnv() { public static Environment getEnv() {
return (null != context) ? context.getEnvironment() : null; return null != context ? context.getEnvironment() : null;
} }
@Override @Override

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.ContentNode; import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class OperatingSystemMacro extends InlineMacroProcessor { public class OperatingSystemMacro extends InlineMacroProcessor {
@ -15,7 +16,8 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
} }
@Override @Override
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) { public PhraseNode process(
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var osName = System.getProperty("os.name"); var osName = System.getProperty("os.name");
// see // see

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.ContentNode; import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
import org.owasp.webgoat.container.users.WebGoatUser; import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
@ -17,7 +18,8 @@ public class UsernameMacro extends InlineMacroProcessor {
} }
@Override @Override
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) { public PhraseNode process(
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var auth = SecurityContextHolder.getContext().getAuthentication(); var auth = SecurityContextHolder.getContext().getAuthentication();
var username = "unknown"; var username = "unknown";
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) { if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.ContentNode; import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatTmpDirMacro extends InlineMacroProcessor { public class WebGoatTmpDirMacro extends InlineMacroProcessor {
@ -15,11 +16,12 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
} }
@Override @Override
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) { public PhraseNode process(
StructuralNode structuralNode, String target, Map<String, Object> attributes) {
var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory"); var env = EnvironmentExposure.getEnv().getProperty("webgoat.server.directory");
// see // see
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used // https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
return createPhraseNode(contentNode, "quoted", env); return createPhraseNode(structuralNode, "quoted", env);
} }
} }

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.ContentNode; import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatVersionMacro extends InlineMacroProcessor { public class WebGoatVersionMacro extends InlineMacroProcessor {
@ -15,7 +16,8 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
} }
@Override @Override
public Object process(ContentNode contentNode, String target, Map<String, Object> attributes) { public PhraseNode process(
StructuralNode contentNode, String target, Map<String, Object> attributes) {
var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version"); var webgoatVersion = EnvironmentExposure.getEnv().getProperty("webgoat.build.version");
// see // see

View File

@ -1,12 +1,10 @@
package org.owasp.webgoat.container.asciidoc; package org.owasp.webgoat.container.asciidoc;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.asciidoctor.ast.ContentNode; import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor; import org.asciidoctor.extension.InlineMacroProcessor;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/** /**
* Usage in asciidoc: * Usage in asciidoc:
@ -24,9 +22,10 @@ public class WebWolfMacro extends InlineMacroProcessor {
} }
@Override @Override
public Object process(ContentNode contentNode, String linkText, Map<String, Object> attributes) { public PhraseNode process(
StructuralNode contentNode, String linkText, Map<String, Object> attributes) {
var env = EnvironmentExposure.getEnv(); var env = EnvironmentExposure.getEnv();
var hostname = determineHost(env.getProperty("webwolf.port")); var hostname = env.getProperty("webwolf.url");
var target = (String) attributes.getOrDefault("target", "home"); var target = (String) attributes.getOrDefault("target", "home");
var href = hostname + "/" + target; var href = hostname + "/" + target;
@ -39,35 +38,10 @@ public class WebWolfMacro extends InlineMacroProcessor {
options.put("type", ":link"); options.put("type", ":link");
options.put("target", href); options.put("target", href);
attributes.put("window", "_blank"); attributes.put("window", "_blank");
return createPhraseNode(contentNode, "anchor", linkText, attributes, options).convert(); return createPhraseNode(contentNode, "anchor", linkText, attributes, options);
} }
private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) { private boolean displayCompleteLinkNoFormatting(Map<String, Object> attributes) {
return attributes.values().stream().anyMatch(a -> a.equals("noLink")); return attributes.values().stream().anyMatch(a -> a.equals("noLink"));
} }
/**
* Determine the host from the hostname and ports that were used. The purpose is to make it
* possible to use the application behind a reverse proxy. For instance in the docker
* compose/stack version with webgoat webwolf and nginx proxy. You do not have to use the
* indicated hostname, but if you do, you should define two hosts aliases 127.0.0.1
* www.webgoat.local www.webwolf.local
*/
private String determineHost(String port) {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String host = request.getHeader("Host");
int semicolonIndex = host.indexOf(":");
if (semicolonIndex == -1 || host.endsWith(":80")) {
host = host.replace(":80", "").replace("www.webgoat.local", "www.webwolf.local");
} else {
host = host.substring(0, semicolonIndex);
host = host.concat(":").concat(port);
}
return "http://" + host + (includeWebWolfContext() ? "/WebWolf" : "");
}
protected boolean includeWebWolfContext() {
return true;
}
} }

View File

@ -17,9 +17,4 @@ public class WebWolfRootMacro extends WebWolfMacro {
public WebWolfRootMacro(String macroName, Map<String, Object> config) { public WebWolfRootMacro(String macroName, Map<String, Object> config) {
super(macroName, config); super(macroName, config);
} }
@Override
protected boolean includeWebWolfContext() {
return false;
}
} }

View File

@ -25,27 +25,13 @@
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
import lombok.Getter;
import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.Initializeable;
import org.owasp.webgoat.container.session.UserSessionData;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
public abstract class AssignmentEndpoint implements Initializeable { public abstract class AssignmentEndpoint {
@Autowired private WebSession webSession; // TODO: move this to different bean.
@Autowired private UserSessionData userSessionData; @Autowired private PluginMessages messages;
@Getter @Autowired private PluginMessages messages;
protected WebSession getWebSession() {
return webSession;
}
protected UserSessionData getUserSessionData() {
return userSessionData;
}
/** /**
* Convenience method for create a successful result: * Convenience method for create a successful result:
@ -86,7 +72,4 @@ public abstract class AssignmentEndpoint implements Initializeable {
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) { protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment); return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
} }
@Override
public void initialize(WebGoatUser user) {}
} }

View File

@ -22,27 +22,30 @@
package org.owasp.webgoat.container.assignments; package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserTrackerRepository; import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice @RestControllerAdvice
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> { public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
private UserTrackerRepository userTrackerRepository; private final Course course;
private WebSession webSession; private final UserProgressRepository userProgressRepository;
public LessonTrackerInterceptor( public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) {
UserTrackerRepository userTrackerRepository, WebSession webSession) { this.course = course;
this.userTrackerRepository = userTrackerRepository; this.userProgressRepository = userProgressRepository;
this.webSession = webSession;
} }
@Override @Override
@ -65,18 +68,30 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
return o; return o;
} }
protected AttackResult trackProgress(AttackResult attackResult) { private void trackProgress(AttackResult attackResult) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (userTracker == null) { Assert.notNull(user, "User not found in SecurityContext");
userTracker = new UserTracker(webSession.getUserName()); var username = realUsername(user);
}
if (attackResult.assignmentSolved()) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
}
userTrackerRepository.save(userTracker);
return attackResult; var userProgress = userProgressRepository.findByUser(username);
if (userProgress == null) {
userProgress = new UserProgress(username);
}
Lesson lesson = course.getLessonByAssignment(attackResult.getAssignment());
Assert.notNull(lesson, "Lesson not found for assignment " + attackResult.getAssignment());
if (attackResult.assignmentSolved()) {
userProgress.assignmentSolved(lesson, attackResult.getAssignment());
} else {
userProgress.assignmentFailed(lesson);
}
userProgressRepository.save(userProgress);
}
private String realUsername(WebGoatUser user) {
// maybe we shouldn't hard code this with just csrf- prefix for now it works
return user.getUsername().startsWith("csrf-")
? user.getUsername().substring("csrf-".length())
: user.getUsername();
} }
} }

View File

@ -33,42 +33,20 @@ package org.owasp.webgoat.container.controller;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@Controller @Controller
public class StartLesson { public class StartLesson {
private final WebSession ws;
private final Course course; private final Course course;
public StartLesson(WebSession ws, Course course) { public StartLesson(Course course) {
this.ws = ws;
this.course = course; this.course = course;
} }
/** @GetMapping(
* start.
*
* @return a {@link ModelAndView} object.
*/
@RequestMapping(
path = "startlesson.mvc",
method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView start() {
var model = new ModelAndView();
model.addObject("course", course);
model.addObject("lesson", ws.getCurrentLesson());
model.setViewName("lesson_content");
return model;
}
@RequestMapping(
value = {"*.lesson"}, value = {"*.lesson"},
produces = "text/html") produces = "text/html")
public ModelAndView lessonPage(HttpServletRequest request) { public ModelAndView lessonPage(HttpServletRequest request) {
@ -81,8 +59,7 @@ public class StartLesson {
.findFirst() .findFirst()
.ifPresent( .ifPresent(
lesson -> { lesson -> {
ws.setCurrentLesson(lesson); request.setAttribute("lesson", lesson);
model.addObject("lesson", lesson);
}); });
return model; return model;

View File

@ -51,10 +51,11 @@ public class Assignment {
private String name; private String name;
private String path; private String path;
private boolean solved = false;
@Transient private List<String> hints; @Transient private List<String> hints;
private Assignment() { protected Assignment() {
// Hibernate // Hibernate
} }
@ -74,4 +75,8 @@ public class Assignment {
this.path = path; this.path = path;
this.hints = hints; this.hints = hints;
} }
public void solved() {
this.solved = true;
}
} }

View File

@ -34,30 +34,28 @@ import lombok.Getter;
* @since October 28, 2003 * @since October 28, 2003
*/ */
public enum Category { public enum Category {
INTRODUCTION("Introduction", 5), INTRODUCTION("Introduction"),
GENERAL("General", 100), GENERAL("General"),
A1("(A1) Broken Access Control", 301), A1("(A1) Broken Access Control"),
A2("(A2) Cryptographic Failures", 302), A2("(A2) Cryptographic Failures"),
A3("(A3) Injection", 303), A3("(A3) Injection"),
A5("(A5) Security Misconfiguration", 305), A5("(A5) Security Misconfiguration"),
A6("(A6) Vuln & Outdated Components", 306), A6("(A6) Vuln & Outdated Components"),
A7("(A7) Identity & Auth Failure", 307), A7("(A7) Identity & Auth Failure"),
A8("(A8) Software & Data Integrity", 308), A8("(A8) Software & Data Integrity"),
A9("(A9) Security Logging Failures", 309), A9("(A9) Security Logging Failures"),
A10("(A10) Server-side Request Forgery", 310), A10("(A10) Server-side Request Forgery"),
CLIENT_SIDE("Client side", 1700), CLIENT_SIDE("Client side"),
CHALLENGE("Challenges", 3000); CHALLENGE("Challenges");
@Getter private String name; @Getter private String name;
@Getter private Integer ranking;
Category(String name, Integer ranking) { Category(String name) {
this.name = name; this.name = name;
this.ranking = ranking;
} }
@Override @Override

View File

@ -22,12 +22,9 @@
package org.owasp.webgoat.container.lessons; package org.owasp.webgoat.container.lessons;
import static java.util.stream.Collectors.groupingBy;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.util.*; import java.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
@ -35,45 +32,91 @@ import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.Course;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@Slf4j
@Configuration @Configuration
public class CourseConfiguration { public class CourseConfiguration {
private final List<Lesson> lessons; private final List<Lesson> lessons;
private final List<AssignmentEndpoint> assignments; private final List<AssignmentEndpoint> assignments;
private final Map<String, List<AssignmentEndpoint>> assignmentsByPackage;
public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) { public CourseConfiguration(List<Lesson> lessons, List<AssignmentEndpoint> assignments) {
this.lessons = lessons; this.lessons = lessons;
this.assignments = assignments; this.assignments = assignments;
assignmentsByPackage = }
this.assignments.stream().collect(groupingBy(a -> a.getClass().getPackageName()));
private void attachToLessonInParentPackage(
AssignmentEndpoint assignmentEndpoint, String packageName) {
if (packageName.equals("org.owasp.webgoat.lessons")) {
throw new IllegalStateException(
"No lesson found for assignment: '%s'"
.formatted(assignmentEndpoint.getClass().getSimpleName()));
}
lessons.stream()
.filter(l -> l.getClass().getPackageName().equals(packageName))
.findFirst()
.ifPresentOrElse(
l -> l.addAssignment(toAssignment(assignmentEndpoint)),
() ->
attachToLessonInParentPackage(
assignmentEndpoint, packageName.substring(0, packageName.lastIndexOf("."))));
}
/**
* For each assignment endpoint, find the lesson in the same package or if not found, find the
* lesson in the parent package
*/
private void attachToLesson(AssignmentEndpoint assignmentEndpoint) {
lessons.stream()
.filter(
l ->
l.getClass()
.getPackageName()
.equals(assignmentEndpoint.getClass().getPackageName()))
.findFirst()
.ifPresentOrElse(
l -> l.addAssignment(toAssignment(assignmentEndpoint)),
() -> {
var assignmentPackageName = assignmentEndpoint.getClass().getPackageName();
attachToLessonInParentPackage(
assignmentEndpoint,
assignmentPackageName.substring(0, assignmentPackageName.lastIndexOf(".")));
});
}
private Assignment toAssignment(AssignmentEndpoint endpoint) {
return new Assignment(
endpoint.getClass().getSimpleName(),
getPath(endpoint.getClass()),
getHints(endpoint.getClass()));
} }
@Bean @Bean
public Course course() { public Course course() {
lessons.stream().forEach(l -> l.setAssignments(createAssignment(l))); assignments.stream().forEach(this::attachToLesson);
// Check if all assignments are attached to a lesson
var assignmentsAttachedToLessons =
lessons.stream().mapToInt(l -> l.getAssignments().size()).sum();
Assert.isTrue(
assignmentsAttachedToLessons == assignments.size(),
"Not all assignments are attached to a lesson, please check the configuration. The"
+ " following assignments are not attached to any lesson: "
+ findDiff());
return new Course(lessons); return new Course(lessons);
} }
private List<Assignment> createAssignment(Lesson lesson) { private List<String> findDiff() {
var endpoints = assignmentsByPackage.get(lesson.getClass().getPackageName()); var matchedToLessons =
if (CollectionUtils.isEmpty(endpoints)) { lessons.stream().flatMap(l -> l.getAssignments().stream()).map(a -> a.getName()).toList();
log.warn("Lesson: {} has no endpoints, is this intentionally?", lesson.getTitle()); var allAssignments = assignments.stream().map(a -> a.getClass().getSimpleName()).toList();
return new ArrayList<>();
} var diff = new ArrayList<>(allAssignments);
return endpoints.stream() diff.removeAll(matchedToLessons);
.map( return diff;
e ->
new Assignment(
e.getClass().getSimpleName(), getPath(e.getClass()), getHints(e.getClass())))
.toList();
} }
private String getPath(Class<? extends AssignmentEndpoint> e) { private String getPath(Class<? extends AssignmentEndpoint> e) {

View File

@ -6,7 +6,7 @@ import org.owasp.webgoat.container.users.WebGoatUser;
* Interface for initialization of a lesson. It is called when a new user is added to WebGoat and * Interface for initialization of a lesson. It is called when a new user is added to WebGoat and
* when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson. * when a users reset a lesson. Make sure to clean beforehand and then re-initialize the lesson.
*/ */
public interface Initializeable { public interface Initializable {
void initialize(WebGoatUser webGoatUser); default void initialize(WebGoatUser webGoatUser) {}
} }

View File

@ -22,6 +22,7 @@
package org.owasp.webgoat.container.lessons; package org.owasp.webgoat.container.lessons;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -30,13 +31,10 @@ import lombok.Setter;
@Setter @Setter
public abstract class Lesson { public abstract class Lesson {
private static int count = 1; private List<Assignment> assignments = new ArrayList<>();
private Integer id = null;
private List<Assignment> assignments;
/** Constructor for the Lesson object */ public void addAssignment(Assignment assignment) {
protected Lesson() { this.assignments.add(assignment);
id = ++count;
} }
/** /**
@ -44,9 +42,9 @@ public abstract class Lesson {
* *
* @return a {@link java.lang.String} object. * @return a {@link java.lang.String} object.
*/ */
public String getName() { public LessonName getName() {
String className = getClass().getName(); String className = getClass().getName();
return className.substring(className.lastIndexOf('.') + 1); return new LessonName(className.substring(className.lastIndexOf('.') + 1));
} }
/** /**
@ -116,6 +114,10 @@ public abstract class Lesson {
return this.getClass().getSimpleName(); return this.getClass().getSimpleName();
} }
/**
* This is used in Thymeleaf to construct the HTML to load the lesson content from. See
* lesson_content.html
*/
public final String getPackage() { public final String getPackage() {
var packageName = this.getClass().getPackageName(); var packageName = this.getClass().getPackageName();
// package name is the direct package name below lessons (any subpackage will be removed) // package name is the direct package name below lessons (any subpackage will be removed)

View File

@ -0,0 +1,21 @@
package org.owasp.webgoat.container.lessons;
import org.springframework.util.Assert;
/**
* Wrapper class for the name of a lesson. This class is used to ensure that the lesson name is not
* null and does not contain the ".lesson" suffix. The front-end passes the lesson name as a string
* to the back-end, which then creates a new LessonName object with the lesson name as a parameter.
* The constructor of the LessonName class checks if the lesson name is null and removes the
* ".lesson" suffix if it is present.
*
* @param lessonName
*/
public record LessonName(String lessonName) {
public LessonName {
Assert.notNull(lessonName, "Lesson name cannot be null");
if (lessonName.contains(".lesson")) {
lessonName = lessonName.substring(0, lessonName.indexOf(".lesson"));
}
}
}

View File

@ -0,0 +1,3 @@
package org.owasp.webgoat.container.report;
record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}

View File

@ -0,0 +1,88 @@
/**
* *************************************************************************************************
*
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*/
package org.owasp.webgoat.container.report;
import java.util.List;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReportCardController {
private final UserProgressRepository userProgressRepository;
private final Course course;
private final PluginMessages pluginMessages;
public ReportCardController(
UserProgressRepository userProgressRepository, Course course, PluginMessages pluginMessages) {
this.userProgressRepository = userProgressRepository;
this.course = course;
this.pluginMessages = pluginMessages;
}
/**
* Endpoint which generates the report card for the current use to show the stats on the solved
* lessons
*/
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
@ResponseBody
public ReportCard reportCard(@CurrentUsername String username) {
var userProgress = userProgressRepository.findByUser(username);
var lessonStatistics =
course.getLessons().stream()
.map(
lesson -> {
var lessonTracker = userProgress.getLessonProgress(lesson);
return new LessonStatistics(
pluginMessages.getMessage(lesson.getTitle()),
lessonTracker.isLessonSolved(),
lessonTracker.getNumberOfAttempts());
})
.toList();
return new ReportCard(
course.getTotalOfLessons(),
course.getTotalOfAssignments(),
userProgress.numberOfAssignmentsSolved(),
userProgress.numberOfLessonsSolved(),
lessonStatistics);
}
private record ReportCard(
int totalNumberOfLessons,
int totalNumberOfAssignments,
long numberOfAssignmentsSolved,
long numberOfLessonsSolved,
List<LessonStatistics> lessonStatistics) {}
private record LessonStatistics(String name, boolean solved, int numberOfAttempts) {}
}

View File

@ -10,26 +10,24 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Assignment;
import org.owasp.webgoat.container.lessons.Hint; import org.owasp.webgoat.container.lessons.Hint;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* HintService class.
*
* @author rlawson
* @version $Id: $Id
*/
@RestController @RestController
public class HintService { public class HintService {
public static final String URL_HINTS_MVC = "/service/hint.mvc"; public static final String URL_HINTS_MVC = "/service/hint.mvc";
private final WebSession webSession; private final List<Hint> allHints;
public HintService(WebSession webSession) { public HintService(Course course) {
this.webSession = webSession; this.allHints =
course.getLessons().stream()
.flatMap(lesson -> lesson.getAssignments().stream())
.map(this::createHint)
.flatMap(Collection::stream)
.toList();
} }
/** /**
@ -40,15 +38,7 @@ public class HintService {
@GetMapping(path = URL_HINTS_MVC, produces = "application/json") @GetMapping(path = URL_HINTS_MVC, produces = "application/json")
@ResponseBody @ResponseBody
public List<Hint> getHints() { public List<Hint> getHints() {
Lesson l = webSession.getCurrentLesson(); return allHints;
return createAssignmentHints(l);
}
private List<Hint> createAssignmentHints(Lesson l) {
if (l != null) {
return l.getAssignments().stream().map(this::createHint).flatMap(Collection::stream).toList();
}
return List.of();
} }
private List<Hint> createHint(Assignment a) { private List<Hint> createHint(Assignment a) {

View File

@ -1,33 +1,24 @@
package org.owasp.webgoat.container.service; package org.owasp.webgoat.container.service;
import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.lessons.LessonInfoModel; import org.owasp.webgoat.container.lessons.LessonInfoModel;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.lessons.LessonName;
import org.springframework.web.bind.annotation.RequestMapping; import org.owasp.webgoat.container.session.Course;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* LessonInfoService class.
*
* @author dm
* @version $Id: $Id
*/
@RestController @RestController
@AllArgsConstructor @RequiredArgsConstructor
public class LessonInfoService { public class LessonInfoService {
private final WebSession webSession; private final Course course;
/** @GetMapping(path = "/service/lessoninfo.mvc/{lesson}")
* getLessonInfo. public @ResponseBody LessonInfoModel getLessonInfo(
* @PathVariable("lesson") LessonName lessonName) {
* @return a {@link LessonInfoModel} object. var lesson = course.getLessonByName(lessonName);
*/
@RequestMapping(path = "/service/lessoninfo.mvc", produces = "application/json")
public @ResponseBody LessonInfoModel getLessonInfo() {
Lesson lesson = webSession.getCurrentLesson();
return new LessonInfoModel(lesson.getTitle(), false, false, false); return new LessonInfoModel(lesson.getTitle(), false, false, false);
} }
} }

View File

@ -32,16 +32,16 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Assignment;
import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Category;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.lessons.LessonMenuItem; import org.owasp.webgoat.container.lessons.LessonMenuItem;
import org.owasp.webgoat.container.lessons.LessonMenuItemType; import org.owasp.webgoat.container.lessons.LessonMenuItemType;
import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.LessonProgress;
import org.owasp.webgoat.container.users.LessonTracker; import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -59,8 +59,7 @@ public class LessonMenuService {
public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc"; public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc";
private final Course course; private final Course course;
private final WebSession webSession; private UserProgressRepository userTrackerRepository;
private UserTrackerRepository userTrackerRepository;
@Value("#{'${exclude.categories}'.split(',')}") @Value("#{'${exclude.categories}'.split(',')}")
private List<String> excludeCategories; private List<String> excludeCategories;
@ -74,10 +73,13 @@ public class LessonMenuService {
* @return a {@link java.util.List} object. * @return a {@link java.util.List} object.
*/ */
@RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json") @RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json")
public @ResponseBody List<LessonMenuItem> showLeftNav() { public @ResponseBody List<LessonMenuItem> showLeftNav(@CurrentUsername String username) {
// TODO: this looks way too complicated. Either we save it incorrectly or we miss something to
// easily find out
// if a lesson if solved or not.
List<LessonMenuItem> menu = new ArrayList<>(); List<LessonMenuItem> menu = new ArrayList<>();
List<Category> categories = course.getCategories(); List<Category> categories = course.getCategories();
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); UserProgress userTracker = userTrackerRepository.findByUser(username);
for (Category category : categories) { for (Category category : categories) {
if (excludeCategories.contains(category.name())) { if (excludeCategories.contains(category.name())) {
@ -97,12 +99,12 @@ public class LessonMenuService {
lessonItem.setName(lesson.getTitle()); lessonItem.setName(lesson.getTitle());
lessonItem.setLink(lesson.getLink()); lessonItem.setLink(lesson.getLink());
lessonItem.setType(LessonMenuItemType.LESSON); lessonItem.setType(LessonMenuItemType.LESSON);
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson); LessonProgress lessonTracker = userTracker.getLessonProgress(lesson);
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson); boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
lessonItem.setComplete(lessonSolved); lessonItem.setComplete(lessonSolved);
categoryItem.addChild(lessonItem); categoryItem.addChild(lessonItem);
} }
categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking()); categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking));
menu.add(categoryItem); menu.add(categoryItem);
} }
return menu; return menu;

View File

@ -4,11 +4,15 @@ import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Assignment;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.lessons.LessonName;
import org.owasp.webgoat.container.users.UserTrackerRepository; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
/** /**
@ -20,8 +24,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
@RequiredArgsConstructor @RequiredArgsConstructor
public class LessonProgressService { public class LessonProgressService {
private final UserTrackerRepository userTrackerRepository; private final UserProgressRepository userProgressRepository;
private final WebSession webSession; private final Course course;
/** /**
* Endpoint for fetching the complete lesson overview which informs the user about whether all the * Endpoint for fetching the complete lesson overview which informs the user about whether all the
@ -29,19 +33,19 @@ public class LessonProgressService {
* *
* @return list of assignments * @return list of assignments
*/ */
@RequestMapping(value = "/service/lessonoverview.mvc", produces = "application/json") @GetMapping(value = "/service/lessonoverview.mvc/{lesson}")
@ResponseBody @ResponseBody
public List<LessonOverview> lessonOverview() { public List<LessonOverview> lessonOverview(
var userTracker = userTrackerRepository.findByUser(webSession.getUserName()); @PathVariable("lesson") LessonName lessonName, @CurrentUsername String username) {
var currentLesson = webSession.getCurrentLesson(); var userProgress = userProgressRepository.findByUser(username);
var lesson = course.getLessonByName(lessonName);
if (currentLesson != null) { Assert.isTrue(lesson != null, "Lesson not found: " + lessonName);
var lessonTracker = userTracker.getLessonTracker(currentLesson);
return lessonTracker.getLessonOverview().entrySet().stream() var lessonProgress = userProgress.getLessonProgress(lesson);
.map(entry -> new LessonOverview(entry.getKey(), entry.getValue())) return lessonProgress.getLessonOverview().entrySet().stream()
.toList(); .map(entry -> new LessonOverview(entry.getKey(), entry.getValue()))
} .toList();
return List.of();
} }
@AllArgsConstructor @AllArgsConstructor

View File

@ -1,34 +0,0 @@
package org.owasp.webgoat.container.service;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* LessonTitleService class.
*
* @author dm
* @version $Id: $Id
*/
@Controller
public class LessonTitleService {
private final WebSession webSession;
public LessonTitleService(final WebSession webSession) {
this.webSession = webSession;
}
/**
* Returns the title for the current attack
*
* @return a {@link java.lang.String} object.
*/
@RequestMapping(path = "/service/lessontitle.mvc", produces = "application/html")
public @ResponseBody String showPlan() {
Lesson lesson = webSession.getCurrentLesson();
return lesson != null ? lesson.getTitle() : "";
}
}

View File

@ -1,105 +0,0 @@
/**
* *************************************************************************************************
*
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*/
package org.owasp.webgoat.container.service;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.LessonTracker;
import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* ReportCardService
*
* @author nbaars
* @version $Id: $Id
*/
@Controller
@AllArgsConstructor
public class ReportCardService {
private final WebSession webSession;
private final UserTrackerRepository userTrackerRepository;
private final Course course;
private final PluginMessages pluginMessages;
/**
* Endpoint which generates the report card for the current use to show the stats on the solved
* lessons
*/
@GetMapping(path = "/service/reportcard.mvc", produces = "application/json")
@ResponseBody
public ReportCard reportCard() {
final ReportCard reportCard = new ReportCard();
reportCard.setTotalNumberOfLessons(course.getTotalOfLessons());
reportCard.setTotalNumberOfAssignments(course.getTotalOfAssignments());
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
reportCard.setNumberOfAssignmentsSolved(userTracker.numberOfAssignmentsSolved());
reportCard.setNumberOfLessonsSolved(userTracker.numberOfLessonsSolved());
for (Lesson lesson : course.getLessons()) {
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
final LessonStatistics lessonStatistics = new LessonStatistics();
lessonStatistics.setName(pluginMessages.getMessage(lesson.getTitle()));
lessonStatistics.setNumberOfAttempts(lessonTracker.getNumberOfAttempts());
lessonStatistics.setSolved(lessonTracker.isLessonSolved());
reportCard.lessonStatistics.add(lessonStatistics);
}
return reportCard;
}
@Getter
@Setter
private final class ReportCard {
private int totalNumberOfLessons;
private int totalNumberOfAssignments;
private int solvedLessons;
private int numberOfAssignmentsSolved;
private int numberOfLessonsSolved;
private List<LessonStatistics> lessonStatistics = new ArrayList<>();
}
@Setter
@Getter
private final class LessonStatistics {
private String name;
private boolean solved;
private int numberOfAttempts;
}
}

View File

@ -29,14 +29,17 @@ import java.util.function.Function;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.owasp.webgoat.container.lessons.Initializeable; import org.owasp.webgoat.container.CurrentUser;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.Initializable;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.lessons.LessonName;
import org.owasp.webgoat.container.users.UserTracker; import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserTrackerRepository; import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
@Controller @Controller
@ -44,25 +47,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@Slf4j @Slf4j
public class RestartLessonService { public class RestartLessonService {
private final WebSession webSession; private final Course course;
private final UserTrackerRepository userTrackerRepository; private final UserProgressRepository userTrackerRepository;
private final Function<String, Flyway> flywayLessons; private final Function<String, Flyway> flywayLessons;
private final List<Initializeable> lessonsToInitialize; private final List<Initializable> lessonsToInitialize;
@RequestMapping(path = "/service/restartlesson.mvc", produces = "text/text") @GetMapping(path = "/service/restartlesson.mvc/{lesson}")
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)
public void restartLesson() { public void restartLesson(
Lesson al = webSession.getCurrentLesson(); @PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) {
log.debug("Restarting lesson: " + al); var lesson = course.getLessonByName(lessonName);
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername());
userTracker.reset(al); userTracker.reset(lesson);
userTrackerRepository.save(userTracker); userTrackerRepository.save(userTracker);
var flyway = flywayLessons.apply(webSession.getUserName()); var flyway = flywayLessons.apply(user.getUsername());
flyway.clean(); flyway.clean();
flyway.migrate(); flyway.migrate();
lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser())); lessonsToInitialize.forEach(i -> i.initialize(user));
} }
} }

View File

@ -7,8 +7,9 @@
package org.owasp.webgoat.container.service; package org.owasp.webgoat.container.service;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.owasp.webgoat.container.CurrentUser;
import org.owasp.webgoat.container.i18n.Messages; import org.owasp.webgoat.container.i18n.Messages;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -17,17 +18,17 @@ import org.springframework.web.bind.annotation.ResponseBody;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SessionService { public class SessionService {
private final WebSession webSession;
private final RestartLessonService restartLessonService; private final RestartLessonService restartLessonService;
private final Messages messages; private final Messages messages;
@RequestMapping(path = "/service/enable-security.mvc", produces = "application/json") @RequestMapping(path = "/service/enable-security.mvc", produces = "application/json")
@ResponseBody @ResponseBody
public String applySecurity() { public String applySecurity(@CurrentUser WebGoatUser user) {
webSession.toggleSecurity(); // webSession.toggleSecurity();
restartLessonService.restartLesson(); // restartLessonService.restartLesson(user);
var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled"; // TODO disabled for now
return messages.getMessage(msg); // var msg = webSession.isSecurityEnabled() ? "security.enabled" : "security.disabled";
return messages.getMessage("Not working...");
} }
} }

View File

@ -4,6 +4,7 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Category;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.lessons.LessonName;
/** /**
* ************************************************************************************************ * ************************************************************************************************
@ -96,4 +97,21 @@ public class Course {
return this.lessons.stream() return this.lessons.stream()
.reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum); .reduce(0, (total, lesson) -> lesson.getAssignments().size() + total, Integer::sum);
} }
public Lesson getLessonByName(LessonName lessonName) {
return lessons.stream()
.filter(lesson -> lesson.getName().equals(lessonName))
.findFirst()
.orElse(null);
}
public Lesson getLessonByAssignment(String assignmentName) {
return lessons.stream()
.filter(
lesson ->
lesson.getAssignments().stream()
.anyMatch(assignment -> assignment.getName().equals(assignmentName)))
.findFirst()
.orElse(null);
}
} }

View File

@ -0,0 +1,44 @@
package org.owasp.webgoat.container.session;
import java.util.HashMap;
import java.util.Map;
/**
* This class is responsible for managing user session data within a lesson. It uses a HashMap to
* store key-value pairs representing session data.
*/
public class LessonSession {
private Map<String, Object> userSessionData = new HashMap<>();
/** Default constructor initializing an empty session. */
public LessonSession() {}
/**
* Retrieves the value associated with the given key.
*
* @param key the key for the session data
* @return the value associated with the key, or null if the key does not exist
*/
public Object getValue(String key) {
if (!userSessionData.containsKey(key)) {
return null;
}
// else
return userSessionData.get(key);
}
/**
* Sets the value for the given key. If the key already exists, its value is updated.
*
* @param key the key for the session data
* @param value the value to be associated with the key
*/
public void setValue(String key, Object value) {
if (userSessionData.containsKey(key)) {
userSessionData.replace(key, value);
} else {
userSessionData.put(key, value);
}
}
}

View File

@ -1,32 +0,0 @@
package org.owasp.webgoat.container.session;
import java.util.HashMap;
/** Created by jason on 1/4/17. */
public class UserSessionData {
private HashMap<String, Object> userSessionData = new HashMap<>();
public UserSessionData() {}
public UserSessionData(String key, String value) {
setValue(key, value);
}
// GETTERS & SETTERS
public Object getValue(String key) {
if (!userSessionData.containsKey(key)) {
return null;
}
// else
return userSessionData.get(key);
}
public void setValue(String key, Object value) {
if (userSessionData.containsKey(key)) {
userSessionData.replace(key, value);
} else {
userSessionData.put(key, value);
}
}
}

View File

@ -1,88 +0,0 @@
package org.owasp.webgoat.container.session;
import java.io.Serializable;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.users.WebGoatUser;
/**
* *************************************************************************************************
*
* <p>
*
* <p>This file is part of WebGoat, an Open Web Application Security Project utility. For details,
* please see http://www.owasp.org/
*
* <p>Copyright (c) 2002 - 2014 Bruce Mayhew
*
* <p>This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <p>You should have received a copy of the GNU General Public License along with this program; if
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* <p>Getting Source ==============
*
* <p>Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository
* for free software projects.
*
* @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
* @version $Id: $Id
* @since October 28, 2003
*/
public class WebSession implements Serializable {
private static final long serialVersionUID = -4270066103101711560L;
private WebGoatUser currentUser;
private transient Lesson currentLesson;
private boolean securityEnabled;
public WebSession(WebGoatUser webGoatUser) {
this.currentUser = webGoatUser;
}
/**
* Setter for the field <code>currentScreen</code>.
*
* @param lesson current lesson
*/
public void setCurrentLesson(Lesson lesson) {
this.currentLesson = lesson;
}
/**
* getCurrentLesson.
*
* @return a {@link Lesson} object.
*/
public Lesson getCurrentLesson() {
return this.currentLesson;
}
/**
* Gets the userName attribute of the WebSession object
*
* @return The userName value
*/
public String getUserName() {
return currentUser.getUsername();
}
public WebGoatUser getUser() {
return currentUser;
}
public void toggleSecurity() {
this.securityEnabled = !this.securityEnabled;
}
public boolean isSecurityEnabled() {
return securityEnabled;
}
}

View File

@ -52,7 +52,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
*/ */
@Entity @Entity
@EqualsAndHashCode @EqualsAndHashCode
public class LessonTracker { public class LessonProgress {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ -61,25 +61,22 @@ public class LessonTracker {
@Getter private String lessonName; @Getter private String lessonName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private final Set<Assignment> solvedAssignments = new HashSet<>(); private final Set<Assignment> assignments = new HashSet<>();
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private final Set<Assignment> allAssignments = new HashSet<>();
@Getter private int numberOfAttempts = 0; @Getter private int numberOfAttempts = 0;
@Version private Integer version; @Version private Integer version;
private LessonTracker() { protected LessonProgress() {
// JPA // JPA
} }
public LessonTracker(Lesson lesson) { public LessonProgress(Lesson lesson) {
lessonName = lesson.getId(); lessonName = lesson.getId();
allAssignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments()); assignments.addAll(lesson.getAssignments() == null ? List.of() : lesson.getAssignments());
} }
public Optional<Assignment> getAssignment(String name) { public Optional<Assignment> getAssignment(String name) {
return allAssignments.stream().filter(a -> a.getName().equals(name)).findFirst(); return assignments.stream().filter(a -> a.getName().equals(name)).findFirst();
} }
/** /**
@ -88,14 +85,14 @@ public class LessonTracker {
* @param solvedAssignment the assignment which the user solved * @param solvedAssignment the assignment which the user solved
*/ */
public void assignmentSolved(String solvedAssignment) { public void assignmentSolved(String solvedAssignment) {
getAssignment(solvedAssignment).ifPresent(solvedAssignments::add); getAssignment(solvedAssignment).ifPresent(Assignment::solved);
} }
/** /**
* @return did they user solved all solvedAssignments for the lesson? * @return did they user solved all solvedAssignments for the lesson?
*/ */
public boolean isLessonSolved() { public boolean isLessonSolved() {
return allAssignments.size() == solvedAssignments.size(); return assignments.stream().allMatch(Assignment::isSolved);
} }
/** Increase the number attempts to solve the lesson */ /** Increase the number attempts to solve the lesson */
@ -105,18 +102,17 @@ public class LessonTracker {
/** Reset the tracker. We do not reset the number of attempts here! */ /** Reset the tracker. We do not reset the number of attempts here! */
void reset() { void reset() {
solvedAssignments.clear(); assignments.clear();
} }
/** /**
* @return list containing all the assignments solved or not * @return list containing all the assignments solved or not
*/ */
public Map<Assignment, Boolean> getLessonOverview() { public Map<Assignment, Boolean> getLessonOverview() {
List<Assignment> notSolved = return assignments.stream().collect(Collectors.toMap(a -> a, Assignment::isSolved));
allAssignments.stream().filter(i -> !solvedAssignments.contains(i)).toList(); }
Map<Assignment, Boolean> overview =
notSolved.stream().collect(Collectors.toMap(a -> a, b -> false)); long numberOfSolvedAssignments() {
overview.putAll(solvedAssignments.stream().collect(Collectors.toMap(a -> a, b -> true))); return assignments.size();
return overview;
} }
} }

View File

@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
@AllArgsConstructor @AllArgsConstructor
public class Scoreboard { public class Scoreboard {
private final UserTrackerRepository userTrackerRepository; private final UserProgressRepository userTrackerRepository;
private final UserRepository userRepository; private final UserRepository userRepository;
private final Course course; private final Course course;
private final PluginMessages pluginMessages; private final PluginMessages pluginMessages;
@ -46,7 +46,7 @@ public class Scoreboard {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private List<String> challengesSolved(UserTracker userTracker) { private List<String> challengesSolved(UserProgress userTracker) {
List<String> challenges = List<String> challenges =
List.of( List.of(
"Challenge1", "Challenge1",
@ -59,10 +59,10 @@ public class Scoreboard {
"Challenge8", "Challenge8",
"Challenge9"); "Challenge9");
return challenges.stream() return challenges.stream()
.map(userTracker::getLessonTracker) .map(userTracker::getLessonProgress)
.flatMap(Optional::stream) .flatMap(Optional::stream)
.filter(LessonTracker::isLessonSolved) .filter(LessonProgress::isLessonSolved)
.map(LessonTracker::getLessonName) .map(LessonProgress::getLessonName)
.map(this::toLessonTitle) .map(this::toLessonTitle)
.toList(); .toList();
} }

View File

@ -9,13 +9,10 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.lessons.Assignment;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.Lesson;
/** /**
@ -52,7 +49,7 @@ import org.owasp.webgoat.container.lessons.Lesson;
@Slf4j @Slf4j
@Entity @Entity
@EqualsAndHashCode @EqualsAndHashCode
public class UserTracker { public class UserProgress {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ -62,11 +59,11 @@ public class UserTracker {
private String user; private String user;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<LessonTracker> lessonTrackers = new HashSet<>(); private Set<LessonProgress> lessonProgress = new HashSet<>();
private UserTracker() {} protected UserProgress() {}
public UserTracker(final String user) { public UserProgress(final String user) {
this.user = user; this.user = user;
} }
@ -76,15 +73,15 @@ public class UserTracker {
* @param lesson the lesson * @param lesson the lesson
* @return a lesson tracker created if not already present * @return a lesson tracker created if not already present
*/ */
public LessonTracker getLessonTracker(Lesson lesson) { public LessonProgress getLessonProgress(Lesson lesson) {
Optional<LessonTracker> lessonTracker = Optional<LessonProgress> progress =
lessonTrackers.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst(); lessonProgress.stream().filter(l -> l.getLessonName().equals(lesson.getId())).findFirst();
if (!lessonTracker.isPresent()) { if (!progress.isPresent()) {
LessonTracker newLessonTracker = new LessonTracker(lesson); LessonProgress newLessonTracker = new LessonProgress(lesson);
lessonTrackers.add(newLessonTracker); lessonProgress.add(newLessonTracker);
return newLessonTracker; return newLessonTracker;
} else { } else {
return lessonTracker.get(); return progress.get();
} }
} }
@ -94,43 +91,34 @@ public class UserTracker {
* @param id the id of the lesson * @param id the id of the lesson
* @return optional due to the fact we can only create a lesson tracker based on a lesson * @return optional due to the fact we can only create a lesson tracker based on a lesson
*/ */
public Optional<LessonTracker> getLessonTracker(String id) { public Optional<LessonProgress> getLessonProgress(String id) {
return lessonTrackers.stream().filter(l -> l.getLessonName().equals(id)).findFirst(); return lessonProgress.stream().filter(l -> l.getLessonName().equals(id)).findFirst();
} }
public void assignmentSolved(Lesson lesson, String assignmentName) { public void assignmentSolved(Lesson lesson, String assignmentName) {
LessonTracker lessonTracker = getLessonTracker(lesson); LessonProgress progress = getLessonProgress(lesson);
lessonTracker.incrementAttempts(); progress.incrementAttempts();
lessonTracker.assignmentSolved(assignmentName); progress.assignmentSolved(assignmentName);
} }
public void assignmentFailed(Lesson lesson) { public void assignmentFailed(Lesson lesson) {
LessonTracker lessonTracker = getLessonTracker(lesson); LessonProgress progress = getLessonProgress(lesson);
lessonTracker.incrementAttempts(); progress.incrementAttempts();
} }
public void reset(Lesson al) { public void reset(Lesson al) {
LessonTracker lessonTracker = getLessonTracker(al); LessonProgress progress = getLessonProgress(al);
lessonTracker.reset(); progress.reset();
} }
public int numberOfLessonsSolved() { public long numberOfLessonsSolved() {
int numberOfLessonsSolved = 0; return lessonProgress.stream().filter(LessonProgress::isLessonSolved).count();
for (LessonTracker lessonTracker : lessonTrackers) {
if (lessonTracker.isLessonSolved()) {
numberOfLessonsSolved = numberOfLessonsSolved + 1;
}
}
return numberOfLessonsSolved;
} }
public int numberOfAssignmentsSolved() { public long numberOfAssignmentsSolved() {
int numberOfAssignmentsSolved = 0; return lessonProgress.stream()
for (LessonTracker lessonTracker : lessonTrackers) { .map(LessonProgress::numberOfSolvedAssignments)
Map<Assignment, Boolean> lessonOverview = lessonTracker.getLessonOverview(); .mapToLong(Long::valueOf)
numberOfAssignmentsSolved = .sum();
lessonOverview.values().stream().filter(b -> b).collect(Collectors.counting()).intValue();
}
return numberOfAssignmentsSolved;
} }
} }

View File

@ -0,0 +1,9 @@
package org.owasp.webgoat.container.users;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserProgressRepository extends JpaRepository<UserProgress, String> {
// TODO: make optional
UserProgress findByUser(String user);
}

View File

@ -4,7 +4,7 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.owasp.webgoat.container.lessons.Initializeable; import org.owasp.webgoat.container.lessons.Initializable;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -19,10 +19,10 @@ import org.springframework.stereotype.Service;
public class UserService implements UserDetailsService { public class UserService implements UserDetailsService {
private final UserRepository userRepository; private final UserRepository userRepository;
private final UserTrackerRepository userTrackerRepository; private final UserProgressRepository userTrackerRepository;
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final Function<String, Flyway> flywayLessons; private final Function<String, Flyway> flywayLessons;
private final List<Initializeable> lessonInitializables; private final List<Initializable> lessonInitializables;
@Override @Override
public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException { public WebGoatUser loadUserByUsername(String username) throws UsernameNotFoundException {
@ -43,7 +43,7 @@ public class UserService implements UserDetailsService {
if (!userAlreadyExists) { if (!userAlreadyExists) {
userTrackerRepository.save( userTrackerRepository.save(
new UserTracker(username)); // if user previously existed it will not get another tracker new UserProgress(username)); // if user previously existed it will not get another tracker
createLessonsForUser(webGoatUser); createLessonsForUser(webGoatUser);
} }
} }

View File

@ -1,12 +0,0 @@
package org.owasp.webgoat.container.users;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author nbaars
* @since 4/30/17.
*/
public interface UserTrackerRepository extends JpaRepository<UserTracker, String> {
UserTracker findByUser(String user);
}

View File

@ -32,9 +32,7 @@ import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -50,9 +48,11 @@ import org.springframework.web.bind.annotation.RestController;
}) })
public class VerifyAccount extends AssignmentEndpoint { public class VerifyAccount extends AssignmentEndpoint {
@Autowired private WebSession webSession; private final LessonSession userSessionData;
@Autowired UserSessionData userSessionData; public VerifyAccount(LessonSession userSessionData) {
this.userSessionData = userSessionData;
}
@PostMapping( @PostMapping(
path = "/auth-bypass/verify-account", path = "/auth-bypass/verify-account",

View File

@ -2,11 +2,13 @@ package org.owasp.webgoat.lessons.challenges;
import org.owasp.webgoat.container.lessons.Category; import org.owasp.webgoat.container.lessons.Category;
import org.owasp.webgoat.container.lessons.Lesson; import org.owasp.webgoat.container.lessons.Lesson;
import org.springframework.stereotype.Component;
/** /**
* @author nbaars * @author nbaars
* @since 3/21/17. * @since 3/21/17.
*/ */
@Component
public class ChallengeIntro extends Lesson { public class ChallengeIntro extends Lesson {
@Override @Override

View File

@ -25,8 +25,7 @@ package org.owasp.webgoat.lessons.challenges;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.WebSession; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -36,13 +35,12 @@ import org.springframework.web.bind.annotation.RestController;
@AllArgsConstructor @AllArgsConstructor
public class FlagController extends AssignmentEndpoint { public class FlagController extends AssignmentEndpoint {
private final WebSession webSession;
private final Flags flags; private final Flags flags;
@PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE) @PostMapping(path = "/challenge/flag/{flagNumber}")
@ResponseBody @ResponseBody
public AttackResult postFlag(@RequestParam String flag) { public AttackResult postFlag(@PathVariable int flagNumber, @RequestParam String flag) {
Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson()); var expectedFlag = flags.getFlag(flagNumber);
if (expectedFlag.isCorrect(flag)) { if (expectedFlag.isCorrect(flag)) {
return success(this).feedback("challenge.flag.correct").build(); return success(this).feedback("challenge.flag.correct").build();
} else { } else {

View File

@ -4,7 +4,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import org.owasp.webgoat.container.lessons.Lesson;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@ -15,12 +14,6 @@ public class Flags {
IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString()))); IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString())));
} }
public Flag getFlag(Lesson forLesson) {
String lessonName = forLesson.getName();
int challengeNumber = Integer.valueOf(lessonName.substring(lessonName.length() - 1));
return FLAGS.get(challengeNumber);
}
public Flag getFlag(int flagNumber) { public Flag getFlag(int flagNumber) {
return FLAGS.get(flagNumber); return FLAGS.get(flagNumber);
} }

View File

@ -24,14 +24,14 @@ package org.owasp.webgoat.lessons.chromedevtools;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/** /**
* This is just a class used to make the the HTTP request. * This is just a class used to make the HTTP request.
* *
* @author TMelzer * @author TMelzer
* @since 30.11.18 * @since 30.11.18
@ -39,11 +39,16 @@ import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
public class NetworkDummy extends AssignmentEndpoint { public class NetworkDummy extends AssignmentEndpoint {
private final LessonSession lessonSession;
public NetworkDummy(LessonSession lessonSession) {
this.lessonSession = lessonSession;
}
@PostMapping("/ChromeDevTools/dummy") @PostMapping("/ChromeDevTools/dummy")
@ResponseBody @ResponseBody
public AttackResult completed(@RequestParam String successMessage) { public AttackResult completed(@RequestParam String successMessage) {
UserSessionData userSessionData = getUserSessionData(); String answer = (String) lessonSession.getValue("randValue");
String answer = (String) userSessionData.getValue("randValue");
if (successMessage != null && successMessage.equals(answer)) { if (successMessage != null && successMessage.equals(answer)) {
return success(this).feedback("xss-dom-message-success").build(); return success(this).feedback("xss-dom-message-success").build();

View File

@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
public class CSRF extends Lesson { public class CSRF extends Lesson {
@Override @Override
public Category getDefaultCategory() { public Category getDefaultCategory() {
return Category.A10; return Category.A5;
} }
@Override @Override

View File

@ -25,7 +25,7 @@ package org.owasp.webgoat.lessons.csrf;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"}) @AssignmentHints({"csrf-get.hint1", "csrf-get.hint2", "csrf-get.hint3", "csrf-get.hint4"})
public class CSRFConfirmFlag1 extends AssignmentEndpoint { public class CSRFConfirmFlag1 extends AssignmentEndpoint {
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@PostMapping( @PostMapping(
path = "/csrf/confirm-flag-1", path = "/csrf/confirm-flag-1",

View File

@ -33,7 +33,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -42,15 +42,11 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* @author nbaars
* @since 11/17/17.
*/
@RestController @RestController
@AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"}) @AssignmentHints({"csrf-feedback-hint1", "csrf-feedback-hint2", "csrf-feedback-hint3"})
public class CSRFFeedback extends AssignmentEndpoint { public class CSRFFeedback extends AssignmentEndpoint {
@Autowired private UserSessionData userSessionData; @Autowired private LessonSession userSessionData;
@Autowired private ObjectMapper objectMapper; @Autowired private ObjectMapper objectMapper;
@PostMapping( @PostMapping(

View File

@ -27,10 +27,9 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -38,13 +37,12 @@ import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
public class CSRFGetFlag { public class CSRFGetFlag {
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@Autowired private PluginMessages pluginMessages; @Autowired private PluginMessages pluginMessages;
@RequestMapping( @PostMapping(
path = "/csrf/basic-get-flag", path = "/csrf/basic-get-flag",
produces = {"application/json"}, produces = {"application/json"})
method = RequestMethod.POST)
@ResponseBody @ResponseBody
public Map<String, Object> invoke(HttpServletRequest req) { public Map<String, Object> invoke(HttpServletRequest req) {

View File

@ -22,47 +22,26 @@
package org.owasp.webgoat.lessons.csrf; package org.owasp.webgoat.lessons.csrf;
import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* @author nbaars
* @since 11/17/17.
*/
@RestController @RestController
@AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"}) @AssignmentHints({"csrf-login-hint1", "csrf-login-hint2", "csrf-login-hint3"})
public class CSRFLogin extends AssignmentEndpoint { public class CSRFLogin extends AssignmentEndpoint {
private final UserTrackerRepository userTrackerRepository;
public CSRFLogin(UserTrackerRepository userTrackerRepository) {
this.userTrackerRepository = userTrackerRepository;
}
@PostMapping( @PostMapping(
path = "/csrf/login", path = "/csrf/login",
produces = {"application/json"}) produces = {"application/json"})
@ResponseBody @ResponseBody
public AttackResult completed(HttpServletRequest request) { public AttackResult completed(@CurrentUsername String username) {
String userName = request.getUserPrincipal().getName(); if (username.startsWith("csrf")) {
if (userName.startsWith("csrf")) {
markAssignmentSolvedWithRealUser(userName.substring("csrf-".length()));
return success(this).feedback("csrf-login-success").build(); return success(this).feedback("csrf-login-success").build();
} }
return failed(this).feedback("csrf-login-failed").feedbackArgs(userName).build(); return failed(this).feedback("csrf-login-failed").feedbackArgs(username).build();
}
private void markAssignmentSolvedWithRealUser(String username) {
UserTracker userTracker = userTrackerRepository.findByUser(username);
userTracker.assignmentSolved(
getWebSession().getCurrentLesson(), this.getClass().getSimpleName());
userTrackerRepository.save(userTracker);
} }
} }

View File

@ -33,11 +33,10 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@ -48,7 +47,6 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"}) @AssignmentHints({"csrf-review-hint1", "csrf-review-hint2", "csrf-review-hint3"})
public class ForgedReviews extends AssignmentEndpoint { public class ForgedReviews extends AssignmentEndpoint {
@Autowired private WebSession webSession;
private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss"); private static DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd, HH:mm:ss");
private static final Map<String, List<Review>> userReviews = new HashMap<>(); private static final Map<String, List<Review>> userReviews = new HashMap<>();
@ -73,9 +71,9 @@ public class ForgedReviews extends AssignmentEndpoint {
produces = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE,
consumes = ALL_VALUE) consumes = ALL_VALUE)
@ResponseBody @ResponseBody
public Collection<Review> retrieveReviews() { public Collection<Review> retrieveReviews(@CurrentUsername String username) {
Collection<Review> allReviews = Lists.newArrayList(); Collection<Review> allReviews = Lists.newArrayList();
Collection<Review> newReviews = userReviews.get(webSession.getUserName()); Collection<Review> newReviews = userReviews.get(username);
if (newReviews != null) { if (newReviews != null) {
allReviews.addAll(newReviews); allReviews.addAll(newReviews);
} }
@ -88,7 +86,11 @@ public class ForgedReviews extends AssignmentEndpoint {
@PostMapping("/csrf/review") @PostMapping("/csrf/review")
@ResponseBody @ResponseBody
public AttackResult createNewReview( public AttackResult createNewReview(
String reviewText, Integer stars, String validateReq, HttpServletRequest request) { String reviewText,
Integer stars,
String validateReq,
HttpServletRequest request,
@CurrentUsername String username) {
final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host"); final String host = (request.getHeader("host") == null) ? "NULL" : request.getHeader("host");
final String referer = final String referer =
(request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer"); (request.getHeader("referer") == null) ? "NULL" : request.getHeader("referer");
@ -97,11 +99,11 @@ public class ForgedReviews extends AssignmentEndpoint {
Review review = new Review(); Review review = new Review();
review.setText(reviewText); review.setText(reviewText);
review.setDateTime(LocalDateTime.now().format(fmt)); review.setDateTime(LocalDateTime.now().format(fmt));
review.setUser(webSession.getUserName()); review.setUser(username);
review.setStars(stars); review.setStars(stars);
var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList<>()); var reviews = userReviews.getOrDefault(username, new ArrayList<>());
reviews.add(review); reviews.add(review);
userReviews.put(webSession.getUserName(), reviews); userReviews.put(username, reviews);
// short-circuit // short-circuit
if (validateReq == null || !validateReq.equals(weakAntiCSRF)) { if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {
return failed(this).feedback("csrf-you-forgot-something").build(); return failed(this).feedback("csrf-you-forgot-something").build();

View File

@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
}) })
public class IDOREditOtherProfile extends AssignmentEndpoint { public class IDOREditOtherProfile extends AssignmentEndpoint {
@Autowired private UserSessionData userSessionData; @Autowired private LessonSession userSessionData;
@PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json") @PutMapping(path = "/IDOR/profile/{userId}", consumes = "application/json")
@ResponseBody @ResponseBody

View File

@ -28,7 +28,7 @@ import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -38,6 +38,12 @@ import org.springframework.web.bind.annotation.RestController;
@AssignmentHints({"idor.hints.idor_login"}) @AssignmentHints({"idor.hints.idor_login"})
public class IDORLogin extends AssignmentEndpoint { public class IDORLogin extends AssignmentEndpoint {
private final LessonSession lessonSession;
public IDORLogin(LessonSession lessonSession) {
this.lessonSession = lessonSession;
}
private Map<String, Map<String, String>> idorUserInfo = new HashMap<>(); private Map<String, Map<String, String>> idorUserInfo = new HashMap<>();
public void initIDORInfo() { public void initIDORInfo() {
@ -59,13 +65,11 @@ public class IDORLogin extends AssignmentEndpoint {
@ResponseBody @ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) { public AttackResult completed(@RequestParam String username, @RequestParam String password) {
initIDORInfo(); initIDORInfo();
UserSessionData userSessionData = getUserSessionData();
if (idorUserInfo.containsKey(username)) { if (idorUserInfo.containsKey(username)) {
if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) { if ("tom".equals(username) && idorUserInfo.get("tom").get("password").equals(password)) {
userSessionData.setValue("idor-authenticated-as", username); lessonSession.setValue("idor-authenticated-as", username);
userSessionData.setValue( lessonSession.setValue("idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
"idor-authenticated-user-id", idorUserInfo.get(username).get("id"));
return success(this).feedback("idor.login.success").feedbackArgs(username).build(); return success(this).feedback("idor.login.success").feedbackArgs(username).build();
} else { } else {
return failed(this).feedback("idor.login.failure").build(); return failed(this).feedback("idor.login.failure").build();

View File

@ -27,7 +27,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -48,7 +48,7 @@ import org.springframework.web.bind.annotation.RestController;
}) })
public class IDORViewOtherProfile extends AssignmentEndpoint { public class IDORViewOtherProfile extends AssignmentEndpoint {
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@GetMapping( @GetMapping(
path = "/IDOR/profile/{userId}", path = "/IDOR/profile/{userId}",

View File

@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
public class IDORViewOwnProfile { public class IDORViewOwnProfile {
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@GetMapping( @GetMapping(
path = {"/IDOR/own", "/IDOR/profile"}, path = {"/IDOR/own", "/IDOR/profile"},

View File

@ -26,7 +26,7 @@ package org.owasp.webgoat.lessons.idor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -41,7 +41,7 @@ import org.springframework.web.bind.annotation.RestController;
}) })
public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint { public class IDORViewOwnProfileAltUrl extends AssignmentEndpoint {
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@PostMapping("/IDOR/profile/alt-path") @PostMapping("/IDOR/profile/alt-path")
@ResponseBody @ResponseBody

View File

@ -1,7 +1,6 @@
package org.owasp.webgoat.lessons.jwt.claimmisuse; package org.owasp.webgoat.lessons.jwt.claimmisuse;
import com.auth0.jwk.JwkException; import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder; import com.auth0.jwk.JwkProviderBuilder;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
@ -20,7 +19,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/JWT/jku") @RequestMapping("/JWT/")
@RestController @RestController
@AssignmentHints({ @AssignmentHints({
"jwt-jku-hint1", "jwt-jku-hint1",
@ -31,7 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
}) })
public class JWTHeaderJKUEndpoint extends AssignmentEndpoint { public class JWTHeaderJKUEndpoint extends AssignmentEndpoint {
@PostMapping("/follow/{user}") @PostMapping("jku/follow/{user}")
public @ResponseBody String follow(@PathVariable("user") String user) { public @ResponseBody String follow(@PathVariable("user") String user) {
if ("Jerry".equals(user)) { if ("Jerry".equals(user)) {
return "Following yourself seems redundant"; return "Following yourself seems redundant";
@ -40,7 +39,7 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint {
} }
} }
@PostMapping("/delete") @PostMapping("jku/delete")
public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) {
if (StringUtils.isEmpty(token)) { if (StringUtils.isEmpty(token)) {
return failed(this).feedback("jwt-invalid-token").build(); return failed(this).feedback("jwt-invalid-token").build();
@ -48,12 +47,12 @@ public class JWTHeaderJKUEndpoint extends AssignmentEndpoint {
try { try {
var decodedJWT = JWT.decode(token); var decodedJWT = JWT.decode(token);
var jku = decodedJWT.getHeaderClaim("jku"); var jku = decodedJWT.getHeaderClaim("jku");
JwkProvider jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build(); var jwkProvider = new JwkProviderBuilder(new URL(jku.asString())).build();
var jwk = jwkProvider.get(decodedJWT.getKeyId()); var jwk = jwkProvider.get(decodedJWT.getKeyId());
var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey()); var algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey());
JWT.require(algorithm).build().verify(decodedJWT); JWT.require(algorithm).build().verify(decodedJWT);
String username = decodedJWT.getClaims().get("username").asString(); var username = decodedJWT.getClaims().get("username").asString();
if ("Jerry".equals(username)) { if ("Jerry".equals(username)) {
return failed(this).feedback("jwt-final-jerry-account").build(); return failed(this).feedback("jwt-final-jerry-account").build();
} }

View File

@ -52,7 +52,7 @@ import org.springframework.web.bind.annotation.RestController;
"jwt-kid-hint5", "jwt-kid-hint5",
"jwt-kid-hint6" "jwt-kid-hint6"
}) })
@RequestMapping("/JWT/kid") @RequestMapping("/JWT/")
public class JWTHeaderKIDEndpoint extends AssignmentEndpoint { public class JWTHeaderKIDEndpoint extends AssignmentEndpoint {
private final LessonDataSource dataSource; private final LessonDataSource dataSource;
@ -61,7 +61,7 @@ public class JWTHeaderKIDEndpoint extends AssignmentEndpoint {
this.dataSource = dataSource; this.dataSource = dataSource;
} }
@PostMapping("/follow/{user}") @PostMapping("kid/follow/{user}")
public @ResponseBody String follow(@PathVariable("user") String user) { public @ResponseBody String follow(@PathVariable("user") String user) {
if ("Jerry".equals(user)) { if ("Jerry".equals(user)) {
return "Following yourself seems redundant"; return "Following yourself seems redundant";
@ -70,7 +70,7 @@ public class JWTHeaderKIDEndpoint extends AssignmentEndpoint {
} }
} }
@PostMapping("/delete") @PostMapping("kid/delete")
public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) { public @ResponseBody AttackResult resetVotes(@RequestParam("token") String token) {
if (StringUtils.isEmpty(token)) { if (StringUtils.isEmpty(token)) {
return failed(this).feedback("jwt-invalid-token").build(); return failed(this).feedback("jwt-invalid-token").build();

View File

@ -27,7 +27,7 @@ import lombok.AllArgsConstructor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData; import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -44,7 +44,7 @@ public class SampleAttack extends AssignmentEndpoint {
String secretValue = "secr37Value"; String secretValue = "secr37Value";
// UserSessionData is bound to session and can be used to persist data across multiple assignments // UserSessionData is bound to session and can be used to persist data across multiple assignments
@Autowired UserSessionData userSessionData; @Autowired LessonSession userSessionData;
@PostMapping("/lesson-template/sample-attack") @PostMapping("/lesson-template/sample-attack")
@ResponseBody @ResponseBody

View File

@ -30,7 +30,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.session.WebSession; import org.owasp.webgoat.container.CurrentUsername;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -47,7 +47,6 @@ import org.springframework.web.servlet.ModelAndView;
public class MissingFunctionACUsers { public class MissingFunctionACUsers {
private final MissingAccessControlUserRepository userRepository; private final MissingAccessControlUserRepository userRepository;
private final WebSession webSession;
@GetMapping(path = {"access-control/users"}) @GetMapping(path = {"access-control/users"})
public ModelAndView listUsers() { public ModelAndView listUsers() {
@ -81,8 +80,8 @@ public class MissingFunctionACUsers {
path = {"access-control/users-admin-fix"}, path = {"access-control/users-admin-fix"},
consumes = "application/json") consumes = "application/json")
@ResponseBody @ResponseBody
public ResponseEntity<List<DisplayUser>> usersFixed() { public ResponseEntity<List<DisplayUser>> usersFixed(@CurrentUsername String username) {
var currentUser = userRepository.findByUsername(webSession.getUserName()); var currentUser = userRepository.findByUsername(username);
if (currentUser != null && currentUser.isAdmin()) { if (currentUser != null && currentUser.isAdmin()) {
return ResponseEntity.ok( return ResponseEntity.ok(
userRepository.findAllUsers().stream() userRepository.findAllUsers().stream()

View File

@ -22,11 +22,14 @@
package org.owasp.webgoat.lessons.passwordreset; package org.owasp.webgoat.lessons.passwordreset;
import static org.springframework.util.StringUtils.hasText;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.assignments.AttackResult;
@ -81,10 +84,10 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
@PostMapping("/PasswordReset/reset/login") @PostMapping("/PasswordReset/reset/login")
@ResponseBody @ResponseBody
public AttackResult login(@RequestParam String password, @RequestParam String email) { public AttackResult login(
@RequestParam String password, @RequestParam String email, @CurrentUsername String username) {
if (TOM_EMAIL.equals(email)) { if (TOM_EMAIL.equals(email)) {
String passwordTom = String passwordTom = usersToTomPassword.getOrDefault(username, PASSWORD_TOM_9);
usersToTomPassword.getOrDefault(getWebSession().getUserName(), PASSWORD_TOM_9);
if (passwordTom.equals(PASSWORD_TOM_9)) { if (passwordTom.equals(PASSWORD_TOM_9)) {
return failed(this).feedback("login_failed").build(); return failed(this).feedback("login_failed").build();
} else if (passwordTom.equals(password)) { } else if (passwordTom.equals(password)) {
@ -112,9 +115,11 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
@PostMapping("/PasswordReset/reset/change-password") @PostMapping("/PasswordReset/reset/change-password")
public ModelAndView changePassword( public ModelAndView changePassword(
@ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) { @ModelAttribute("form") PasswordChangeForm form,
BindingResult bindingResult,
@CurrentUsername String username) {
ModelAndView modelAndView = new ModelAndView(); ModelAndView modelAndView = new ModelAndView();
if (!org.springframework.util.StringUtils.hasText(form.getPassword())) { if (!hasText(form.getPassword())) {
bindingResult.rejectValue("password", "not.empty"); bindingResult.rejectValue("password", "not.empty");
} }
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
@ -125,15 +130,15 @@ public class ResetLinkAssignment extends AssignmentEndpoint {
modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found"));
return modelAndView; return modelAndView;
} }
if (checkIfLinkIsFromTom(form.getResetLink())) { if (checkIfLinkIsFromTom(form.getResetLink(), username)) {
usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); usersToTomPassword.put(username, form.getPassword());
} }
modelAndView.setViewName(VIEW_FORMATTER.formatted("success")); modelAndView.setViewName(VIEW_FORMATTER.formatted("success"));
return modelAndView; return modelAndView;
} }
private boolean checkIfLinkIsFromTom(String resetLinkFromForm) { private boolean checkIfLinkIsFromTom(String resetLinkFromForm, String username) {
String resetLink = userToTomResetLink.getOrDefault(getWebSession().getUserName(), "unknown"); String resetLink = userToTomResetLink.getOrDefault(username, "unknown");
return resetLink.equals(resetLinkFromForm); return resetLink.equals(resetLinkFromForm);
} }
} }

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