Compare commits

...

111 Commits

Author SHA1 Message Date
6a83ddcdef refactor: remove unused code 2024-12-15 12:49:42 +01:00
67c1f622ef refactor: make AssignmentEndpoint an interface 2024-12-02 20:32:06 +01:00
a3e0fcc9b3 refactor: move plugin messages
It is now done afterward through an interceptor. No more need to burden assignments with plugin messages etc. Only return the key and the optional args.
2024-12-02 10:51:19 +01:00
d8100385b6 fix: automatically solve XSS mitigation (#1957)
This PR moves the mitigation Java class into the correct package.

The lesson was automatically solved because no assignments were found.

Closes: #1943
2024-11-14 08:42:55 +01:00
4880afa0e3 fix: remove implicit context path guessing (#1956)
Pass the context-path in the assignment overview so the frontend can easily match an assignment.
2024-11-13 21:32:28 +01:00
e60ca6ce72 chore: bump org.jruby:jruby from 9.4.8.0 to 9.4.9.0 (#1954) 2024-11-11 13:46:45 +01:00
88a763f513 chore: bump org.testcontainers:junit-jupiter from 1.20.1 to 1.20.3 (#1946)
Bumps [org.testcontainers:junit-jupiter](https://github.com/testcontainers/testcontainers-java) from 1.20.1 to 1.20.3.
- [Release notes](https://github.com/testcontainers/testcontainers-java/releases)
- [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.20.1...1.20.3)

---
updated-dependencies:
- dependency-name: org.testcontainers:junit-jupiter
  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>
2024-11-07 16:13:27 +01:00
7f33d3609f chore: bump org.apache.maven.plugins:maven-surefire-plugin (#1948)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.1...surefire-3.5.2)

---
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-11-07 16:13:10 +01:00
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
601 changed files with 4097 additions and 4496 deletions

View File

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

View File

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

View File

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

View File

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

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:21-jdk-ubi9-minimal
LABEL NAME = "WebGoat: A deliberately insecure Web Application"
MAINTAINER "WebGoat team"
# We need JDK as some of the lessons needs to be able to compile Java code
FROM docker.io/eclipse-temurin:21-jdk-jammy
LABEL name="WebGoat: A deliberately insecure Web Application"
LABEL maintainer="WebGoat team"
RUN \
useradd -ms /bin/bash webgoat && \
@ -33,3 +35,6 @@ ENTRYPOINT [ "java", \
"--add-opens", "java.base/java.io=ALL-UNNAMED", \
"-Drunning.in.docker=true", \
"-jar", "webgoat.jar", "--server.address", "0.0.0.0" ]
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl --fail http://localhost:8080/WebGoat/actuator/health || exit 1

View File

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

1
FAQ.md
View File

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

View File

@ -1,7 +1,7 @@
# WebGoat: A deliberately insecure Web Application
[![Build](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/WebGoat/WebGoat/actions/workflows/build.yml)
[![java-jdk](https://img.shields.io/badge/java%20jdk-17-green.svg)](https://jdk.java.net/)
[![java-jdk](https://img.shields.io/badge/java%20jdk-21-green.svg)](https://jdk.java.net/)
[![OWASP Labs](https://img.shields.io/badge/OWASP-Lab%20project-f7b73c.svg)](https://owasp.org/projects/)
[![GitHub release](https://img.shields.io/github/release/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/releases/latest)
[![Gitter](https://badges.gitter.im/OWASPWebGoat/community.svg)](https://gitter.im/OWASPWebGoat/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@ -80,11 +80,21 @@ Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/rel
```shell
export TZ=Europe/Amsterdam # or your timezone
java -Dfile.encoding=UTF-8 -jar webgoat-2023.5.jar
java -Dfile.encoding=UTF-8 -jar webgoat-2023.8.jar
```
Click the link in the log to start WebGoat.
### 3.1 Running on a different port
If for some reason you want to run WebGoat on a different port, you can do so by adding the following parameter:
```shell
java -jar webgoat-2023.8.jar --webgoat.port=8001 --webwolf.port=8002
```
For a full overview of all the parameters you can use, please check the [WebGoat properties file](webgoat-container/src/main/resources/application-{webgoat, webwolf}.properties).
## 4. Run from the sources
### Prerequisites:
@ -144,7 +154,7 @@ For instance running as a jar on a Linux/macOS it will look like this:
export TZ=Europe/Amsterdam # or your timezone
export EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE"
export EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations"
java -jar target/webgoat-2023.6-SNAPSHOT.jar
java -jar target/webgoat-2023.8-SNAPSHOT.jar
```
Or in a docker run it would (once this version is pushed into docker hub) look like this:

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

View File

@ -1,6 +1,6 @@
# WebGoat release notes
## Version 2023.6
## Version 2023.8
### 🚀 New functionality
@ -215,4 +215,3 @@ Special thanks to the following contributors providing us with a pull request:
And everyone who provided feedback through Github.
Team WebGoat

View File

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

View File

@ -1,3 +1,3 @@
#!/bin/sh
/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar
/config/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
Old GitHub page which now redirects to OWASP website.

163
pom.xml
View File

@ -5,12 +5,12 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
<version>3.3.5</version>
</parent>
<groupId>org.owasp.webgoat</groupId>
<artifactId>webgoat</artifactId>
<version>2023.6</version>
<version>2024.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>WebGoat</name>
@ -29,13 +29,6 @@
</licenses>
<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>
<id>nbaars</id>
<name>Nanne Baars</name>
@ -43,11 +36,6 @@
<organizationUrl>https://github.com/nbaars</organizationUrl>
<timezone>Europe/Amsterdam</timezone>
</developer>
<developer>
<id>misfir3</id>
<name>Jason White</name>
<email>jason.white@owasp.org</email>
</developer>
<developer>
<id>zubcevic</id>
<name>René Zubcevic</name>
@ -58,43 +46,8 @@
<name>Àngel Ollé Blázquez</name>
<email>angel@olleb.com</email>
</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>
<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>
<connection>scm:git:git@github.com:WebGoat/WebGoat.git</connection>
<developerConnection>scm:git:git@github.com:WebGoat/WebGoat.git</developerConnection>
@ -109,61 +62,56 @@
<properties>
<!-- Shared properties with plugins and version numbers across submodules-->
<asciidoctorj.version>2.5.10</asciidoctorj.version>
<bootstrap.version>5.3.1</bootstrap.version>
<asciidoctorj.version>3.0.0</asciidoctorj.version>
<bootstrap.version>5.3.3</bootstrap.version>
<cglib.version>3.3.0</cglib.version>
<!-- 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-io.version>2.15.1</commons-io.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<commons-text.version>1.10.0</commons-text.version>
<guava.version>32.1.3-jre</guava.version>
<commons-compress.version>1.27.1</commons-compress.version>
<commons-io.version>2.17.0</commons-io.version>
<commons-lang3.version>3.14.0</commons-lang3.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>
<java.version>17</java.version>
<java.version>21</java.version>
<jaxb.version>2.3.1</jaxb.version>
<jjwt.version>0.9.1</jjwt.version>
<jose4j.version>0.9.3</jose4j.version>
<jquery.version>3.7.0</jquery.version>
<jsoup.version>1.16.1</jsoup.version>
<jquery.version>3.7.1</jquery.version>
<jsoup.version>1.18.1</jsoup.version>
<maven-compiler-plugin.version>3.8.0</maven-compiler-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-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.1.0</maven-source-plugin.version>
<maven-surefire-plugin.version>3.2.1</maven-surefire-plugin.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven-surefire-plugin.version>3.5.2</maven-surefire-plugin.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<pmd.version>3.15.0</pmd.version>
<!-- Use UTF-8 Encoding -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<thymeleaf.version>3.1.1.RELEASE</thymeleaf.version>
<webdriver.version>5.3.3</webdriver.version>
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
<waittimeForServerStart>60</waittimeForServerStart>
<webdriver.version>5.9.2</webdriver.version>
<webgoat.context>/</webgoat.context>
<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>
<wiremock.version>2.27.2</wiremock.version>
<wiremock.version>3.9.2</wiremock.version>
<xml-resolver.version>1.2</xml-resolver.version>
<xstream.version>1.4.5</xstream.version>
<!-- do not update necessary for lesson -->
<zxcvbn.version>1.8.0</zxcvbn.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
@ -248,8 +196,8 @@
<version>${webjars-locator-core.version}</version>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>${wiremock.version}</version>
</dependency>
<dependency>
@ -260,12 +208,12 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.25.0</version>
<version>${commons-compress.version}</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby</artifactId>
<version>9.4.3.0</version>
<version>9.4.9.0</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -284,24 +232,26 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -311,6 +261,10 @@
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-hsqldb</artifactId>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
@ -417,6 +371,12 @@
<artifactId>jaxb-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.github.terma</groupId>
<artifactId>javaniotcpproxy</artifactId>
<version>1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -429,10 +389,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<version>3.0.0-beta-2</version>
<scope>test</scope>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
@ -548,6 +506,7 @@
<version>${maven-surefire-plugin.version}</version>
<configuration>
<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
--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
@ -555,8 +514,6 @@
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED</argLine>
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
<exclude>src/it/java</exclude>
<exclude>org/owasp/webgoat/*Test</exclude>
</excludes>
</configuration>
</plugin>
@ -636,7 +593,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.3.0</version>
<version>3.5.0</version>
<executions>
<execution>
<id>restrict-log4j-versions</id>
@ -660,10 +617,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
@ -735,24 +688,18 @@
<argument>--add-opens</argument>
<argument>java.base/java.lang.reflect=ALL-UNNAMED</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>--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>--add-opens</argument>
<argument>java.base/java.io=ALL-UNNAMED</argument>
<argument>--add-opens</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>
</arguments>
<waitForInterrupt>false</waitForInterrupt>
<waitAfterLaunch>${waittimeForServerStart}</waitAfterLaunch>
<healthCheckUrl>http://127.0.0.1:${webgoat.port}${webgoat.context}login</healthCheckUrl>
</configuration>
</execution>
<execution>

View File

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

View File

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

View File

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

View File

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

View File

@ -28,6 +28,6 @@ public class DeserializationIntegrationTest extends IntegrationTest {
}
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");
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
@ -51,7 +61,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
.path("lessonCompleted"),
CoreMatchers.is(true));
checkResults("/HttpProxies/");
checkResults("HttpProxies");
}
@Test
@ -73,7 +83,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
"question_3_solution",
"Solution 2: The systems security is compromised even if only one goal is harmed.");
checkAssignment(url("cia/quiz"), params, true);
checkResults("/cia/");
checkResults("CIA");
}
@Test
@ -96,7 +106,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear();
params.put("payload", solution);
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("password", "BlackPearl");
checkAssignment(url("InsecureLogin/task"), params, true);
checkResults("/InsecureLogin/");
checkResults("InsecureLogin");
}
@Test
@ -118,7 +128,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.clear();
params.put("password", "ajnaeliclm^&&@kjn.");
checkAssignment(url("SecurePasswords/assignment"), params, true);
checkResults("SecurePasswords/");
checkResults("SecurePasswords");
startLesson("AuthBypass");
params.clear();
@ -128,7 +138,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746");
checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("/auth-bypass/");
checkResults("AuthBypass");
startLesson("HttpProxies");
MatcherAssert.assertThat(
@ -144,7 +154,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
.extract()
.path("lessonCompleted"),
CoreMatchers.is(true));
checkResults("/HttpProxies/");
checkResults("HttpProxies");
}
@Test
@ -180,7 +190,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("network_num", "24");
checkAssignment(url("ChromeDevTools/network"), params, true);
checkResults("/ChromeDevTools/");
checkResults("ChromeDevTools");
}
@Test
@ -194,7 +204,7 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("verifyMethod", "SEC_QUESTIONS");
params.put("userId", "12309746");
checkAssignment(url("auth-bypass/verify-account"), params, true);
checkResults("/auth-bypass/");
checkResults("AuthBypass");
}
@Test
@ -205,6 +215,6 @@ public class GeneralLessonIntegrationTest extends IntegrationTest {
params.put("param1", "secr37Value");
params.put("param2", "Main");
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
public void shutdown() {
checkResults("/IDOR");
checkResults("IDOR");
}
private void loginIDOR() {

View File

@ -3,6 +3,7 @@ package org.owasp.webgoat;
import static io.restassured.RestAssured.given;
import io.restassured.RestAssured;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import java.util.Map;
import lombok.Getter;
@ -15,43 +16,80 @@ import org.springframework.http.HttpStatus;
public abstract class IntegrationTest {
private static String webGoatPort = System.getenv().getOrDefault("WEBGOAT_PORT", "8080");
private static String webGoatContext =
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
@Getter private static String webWolfPort = System.getenv().getOrDefault("WEBWOLF_PORT", "9090");
@Getter
private static String webWolfHost = System.getenv().getOrDefault("WEBWOLF_HOST", "127.0.0.1");
@Getter
private static String webGoatHost = System.getenv().getOrDefault("WEBGOAT_HOST", "127.0.0.1");
private static String webGoatContext =
System.getenv().getOrDefault("WEBGOAT_CONTEXT", "/WebGoat/");
private static String webWolfContext =
System.getenv().getOrDefault("WEBWOLF_CONTEXT", "/WebWolf/");
private static boolean useSSL =
Boolean.valueOf(System.getenv().getOrDefault("WEBGOAT_SSLENABLED", "false"));
private static String webgoatUrl =
(useSSL ? "https://" : "http://") + webGoatHost + ":" + webGoatPort + webGoatContext;
private static String webWolfUrl = "http://" + webWolfHost + ":" + webWolfPort + webWolfContext;
@Getter private String webGoatCookie;
@Getter private String webWolfCookie;
@Getter private final String user = "webgoat";
protected String url(String url) {
return webgoatUrl + url;
return "http://localhost:%s%s%s".formatted(webGoatPort, webGoatContext, url);
}
protected String webWolfUrl(String url) {
return webWolfUrl + url;
protected class WebWolfUrlBuilder {
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
public void login() {
login("webgoat");
}
protected void login(String user) {
String location =
given()
.when()
@ -60,6 +98,8 @@ public abstract class IntegrationTest {
.formParam("password", "password")
.post(url("login"))
.then()
.log()
.ifValidationFails(LogDetail.ALL) // Log the response details if validation fails
.cookie("JSESSIONID")
.statusCode(302)
.extract()
@ -100,7 +140,7 @@ public abstract class IntegrationTest {
.relaxedHTTPSValidation()
.formParam("username", user)
.formParam("password", "password")
.post(webWolfUrl("login"))
.post(new WebWolfUrlBuilder().path("login").build())
.then()
.statusCode(302)
.cookie("WEBWOLFSESSION")
@ -131,7 +171,7 @@ public abstract class IntegrationTest {
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/restartlesson.mvc"))
.get(url("service/restartlesson.mvc/%s.lesson".formatted(lessonName)))
.then()
.statusCode(200);
}
@ -167,23 +207,18 @@ public abstract class IntegrationTest {
CoreMatchers.is(expectedResult));
}
// TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are
// only required to be in the same package)
public void checkResults(String prefix) {
checkResults();
MatcherAssert.assertThat(
public void checkResults(String lesson) {
var result =
RestAssured.given()
.when()
.relaxedHTTPSValidation()
.cookie("JSESSIONID", getWebGoatCookie())
.get(url("service/lessonoverview.mvc"))
.then()
.statusCode(200)
.extract()
.jsonPath()
.getList("assignment.path"),
CoreMatchers.everyItem(CoreMatchers.startsWith(prefix)));
.get(url("service/lessonoverview.mvc/%s.lesson".formatted(lesson)))
.andReturn();
MatcherAssert.assertThat(
result.then().statusCode(200).extract().jsonPath().getList("solved"),
CoreMatchers.everyItem(CoreMatchers.is(true)));
}
public void checkResults() {
@ -238,7 +273,7 @@ public abstract class IntegrationTest {
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("file-server-location"))
.get(new WebWolfUrlBuilder().path("file-server-location").build())
.then()
.extract()
.response()
@ -266,7 +301,7 @@ public abstract class IntegrationTest {
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.delete(webWolfUrl("mail"))
.delete(new WebWolfUrlBuilder().path("mail").build())
.then()
.statusCode(HttpStatus.ACCEPTED.value());
}

View File

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

View File

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

View File

@ -85,7 +85,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("mail"))
.get(new WebWolfUrlBuilder().path("mail").build())
.then()
.extract()
.response()
@ -99,7 +99,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
public void shutdown() {
// this will run only once after the list of dynamic tests has run, this is to test if the
// lesson is marked complete
checkResults("/PasswordReset");
checkResults("PasswordReset");
}
private void changePassword(String link) {
@ -119,7 +119,7 @@ public class PasswordResetLessonIntegrationTest extends IntegrationTest {
.when()
.relaxedHTTPSValidation()
.cookie("WEBWOLFSESSION", getWebWolfCookie())
.get(webWolfUrl("requests"))
.get(new WebWolfUrlBuilder().path("requests").build())
.then()
.extract()
.response()

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@ -55,8 +55,8 @@ import org.thymeleaf.templateresource.StringTemplateResource;
public class LessonTemplateResolver extends FileTemplateResolver {
private static final String PREFIX = "lesson:";
private ResourceLoader resourceLoader;
private Map<String, byte[]> resources = new HashMap<>();
private final ResourceLoader resourceLoader;
private final Map<String, byte[]> resources = new HashMap<>();
public LessonTemplateResolver(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;

View File

@ -40,7 +40,6 @@ import lombok.extern.slf4j.Slf4j;
import org.owasp.webgoat.container.i18n.Language;
import org.owasp.webgoat.container.i18n.Messages;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.LessonScanner;
import org.owasp.webgoat.container.session.LabelDebugger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@ -74,8 +73,6 @@ public class MvcConfiguration implements WebMvcConfigurer {
private static final String UTF8 = "UTF-8";
private final LessonScanner lessonScanner;
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
@ -187,28 +184,6 @@ public class MvcConfiguration implements WebMvcConfigurer {
registry
.addResourceHandler("/fonts/**")
.addResourceLocations("classpath:/webgoat/static/fonts/");
// WebGoat lessons
registry
.addResourceHandler("/images/**")
.addResourceLocations(
lessonScanner.applyPattern("classpath:/lessons/%s/images/").toArray(String[]::new));
registry
.addResourceHandler("/lesson_js/**")
.addResourceLocations(
lessonScanner.applyPattern("classpath:/lessons/%s/js/").toArray(String[]::new));
registry
.addResourceHandler("/lesson_css/**")
.addResourceLocations(
lessonScanner.applyPattern("classpath:/lessons/%s/css/").toArray(String[]::new));
registry
.addResourceHandler("/lesson_templates/**")
.addResourceLocations(
lessonScanner.applyPattern("classpath:/lessons/%s/templates/").toArray(String[]::new));
registry
.addResourceHandler("/video/**")
.addResourceLocations(
lessonScanner.applyPattern("classpath:/lessons/%s/video/").toArray(String[]::new));
}
@Bean

View File

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

View File

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

View File

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

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc;
import java.util.Map;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor;
public class OperatingSystemMacro extends InlineMacroProcessor {
@ -15,7 +16,8 @@ public class OperatingSystemMacro extends InlineMacroProcessor {
}
@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");
// see

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc;
import java.util.Map;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.security.core.context.SecurityContextHolder;
@ -17,7 +18,8 @@ public class UsernameMacro extends InlineMacroProcessor {
}
@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 username = "unknown";
if (auth.getPrincipal() instanceof WebGoatUser webGoatUser) {

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc;
import java.util.Map;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatTmpDirMacro extends InlineMacroProcessor {
@ -15,11 +16,12 @@ public class WebGoatTmpDirMacro extends InlineMacroProcessor {
}
@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");
// see
// https://discuss.asciidoctor.org/How-to-create-inline-macro-producing-HTML-In-AsciidoctorJ-td8313.html for why quoted is used
return createPhraseNode(contentNode, "quoted", env);
return createPhraseNode(structuralNode, "quoted", env);
}
}

View File

@ -1,7 +1,8 @@
package org.owasp.webgoat.container.asciidoc;
import java.util.Map;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor;
public class WebGoatVersionMacro extends InlineMacroProcessor {
@ -15,7 +16,8 @@ public class WebGoatVersionMacro extends InlineMacroProcessor {
}
@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");
// see

View File

@ -2,7 +2,8 @@ package org.owasp.webgoat.container.asciidoc;
import java.util.HashMap;
import java.util.Map;
import org.asciidoctor.ast.ContentNode;
import org.asciidoctor.ast.PhraseNode;
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.InlineMacroProcessor;
/**
@ -21,7 +22,8 @@ public class WebWolfMacro extends InlineMacroProcessor {
}
@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 hostname = env.getProperty("webwolf.url");
var target = (String) attributes.getOrDefault("target", "home");
@ -36,7 +38,7 @@ public class WebWolfMacro extends InlineMacroProcessor {
options.put("type", ":link");
options.put("target", href);
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) {

View File

@ -25,68 +25,4 @@
package org.owasp.webgoat.container.assignments;
import lombok.Getter;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.owasp.webgoat.container.lessons.Initializeable;
import org.owasp.webgoat.container.session.UserSessionData;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class AssignmentEndpoint implements Initializeable {
@Autowired private WebSession webSession;
@Autowired private UserSessionData userSessionData;
@Getter @Autowired private PluginMessages messages;
protected WebSession getWebSession() {
return webSession;
}
protected UserSessionData getUserSessionData() {
return userSessionData;
}
/**
* Convenience method for create a successful result:
*
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder success(AssignmentEndpoint assignment) {
return AttackResult.builder(messages)
.lessonCompleted(true)
.attemptWasMade()
.feedback("assignment.solved")
.assignment(assignment);
}
/**
* Convenience method for create a failed result:
*
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
protected AttackResult.AttackResultBuilder failed(AssignmentEndpoint assignment) {
return AttackResult.builder(messages)
.lessonCompleted(false)
.attemptWasMade()
.feedback("assignment.not.solved")
.assignment(assignment);
}
protected AttackResult.AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return AttackResult.builder(messages).lessonCompleted(false).assignment(assignment);
}
@Override
public void initialize(WebGoatUser user) {}
}
public interface AssignmentEndpoint {}

View File

@ -1,19 +0,0 @@
package org.owasp.webgoat.container.assignments;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.web.bind.annotation.RequestMethod;
/** Created by nbaars on 1/14/17. */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AssignmentPath {
String[] path() default {};
RequestMethod[] method() default {};
String value() default "";
}

View File

@ -30,82 +30,18 @@ import static org.apache.commons.text.StringEscapeUtils.escapeJson;
import lombok.Getter;
import org.owasp.webgoat.container.i18n.PluginMessages;
@Getter
public class AttackResult {
public static class AttackResultBuilder {
private boolean lessonCompleted;
private String feedback;
private Object[] feedbackArgs;
private String output;
private Object[] outputArgs;
private final String assignment;
private boolean attemptWasMade;
private boolean lessonCompleted;
private PluginMessages messages;
private Object[] feedbackArgs;
private String feedbackResourceBundleKey;
private String output;
private Object[] outputArgs;
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
public AttackResultBuilder(PluginMessages messages) {
this.messages = messages;
}
public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = "lesson.completed";
return this;
}
public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder feedbackArgs(Object... args) {
this.feedbackArgs = args;
return this;
}
public AttackResultBuilder feedback(String resourceBundleKey) {
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder output(String output) {
this.output = output;
return this;
}
public AttackResultBuilder outputArgs(Object... args) {
this.outputArgs = args;
return this;
}
public AttackResultBuilder attemptWasMade() {
this.attemptWasMade = true;
return this;
}
public AttackResult build() {
return new AttackResult(
lessonCompleted,
messages.getMessage(feedbackResourceBundleKey, feedbackArgs),
messages.getMessage(output, output, outputArgs),
assignment.getClass().getSimpleName(),
attemptWasMade);
}
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
this.assignment = assignment;
return this;
}
}
@Getter private boolean lessonCompleted;
@Getter private String feedback;
@Getter private String output;
@Getter private final String assignment;
@Getter private boolean attemptWasMade;
public AttackResult(
private AttackResult(
boolean lessonCompleted,
String feedback,
String output,
@ -118,11 +54,33 @@ public class AttackResult {
this.attemptWasMade = attemptWasMade;
}
public static AttackResultBuilder builder(PluginMessages messages) {
return new AttackResultBuilder(messages);
public AttackResult(
boolean lessonCompleted,
String feedback,
Object[] feedbackArgs,
String output,
Object[] outputArgs,
String assignment,
boolean attemptWasMade) {
this.lessonCompleted = lessonCompleted;
this.feedback = feedback;
this.feedbackArgs = feedbackArgs;
this.output = output;
this.outputArgs = outputArgs;
this.assignment = assignment;
this.attemptWasMade = attemptWasMade;
}
public boolean assignmentSolved() {
return lessonCompleted;
}
public AttackResult apply(PluginMessages pluginMessages) {
return new AttackResult(
lessonCompleted,
pluginMessages.getMessage(feedback, feedback, feedbackArgs),
pluginMessages.getMessage(output, output, outputArgs),
assignment,
attemptWasMade);
}
}

View File

@ -0,0 +1,130 @@
package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.i18n.PluginMessages;
public class AttackResultBuilder {
private PluginMessages messages;
private boolean lessonCompleted;
private Object[] feedbackArgs;
private String feedbackResourceBundleKey;
private String output;
private Object[] outputArgs;
private AssignmentEndpoint assignment;
private boolean attemptWasMade = false;
private boolean assignmentCompleted;
public AttackResultBuilder(PluginMessages messages) {
this.messages = messages;
}
public AttackResultBuilder() {}
public AttackResultBuilder lessonCompleted(boolean lessonCompleted) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = "lesson.completed";
return this;
}
public AttackResultBuilder lessonCompleted(boolean lessonCompleted, String resourceBundleKey) {
this.lessonCompleted = lessonCompleted;
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder assignmentCompleted(boolean assignmentCompleted) {
this.assignmentCompleted = assignmentCompleted;
this.feedbackResourceBundleKey = "assignment.completed";
return this;
}
public AttackResultBuilder assignmentCompleted(
boolean assignmentCompleted, String resourceBundleKey) {
this.assignmentCompleted = assignmentCompleted;
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder feedbackArgs(Object... args) {
this.feedbackArgs = args;
return this;
}
public AttackResultBuilder feedback(String resourceBundleKey) {
this.feedbackResourceBundleKey = resourceBundleKey;
return this;
}
public AttackResultBuilder output(String output) {
this.output = output;
return this;
}
public AttackResultBuilder outputArgs(Object... args) {
this.outputArgs = args;
return this;
}
public AttackResultBuilder attemptWasMade() {
this.attemptWasMade = true;
return this;
}
public AttackResult build() {
return new AttackResult(
lessonCompleted,
feedbackResourceBundleKey,
feedbackArgs,
output,
outputArgs,
assignment.getClass().getSimpleName(),
attemptWasMade);
}
public AttackResultBuilder assignment(AssignmentEndpoint assignment) {
this.assignment = assignment;
return this;
}
/**
* Convenience method for create a successful result:
*
* <p>- Assignment is set to solved - Feedback message is set to 'assignment.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
public static AttackResultBuilder success(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.lessonCompleted(true)
.assignmentCompleted(true)
.attemptWasMade()
.feedback("assignment.solved")
.assignment(assignment);
}
/**
* Convenience method for create a failed result:
*
* <p>- Assignment is set to not solved - Feedback message is set to 'assignment.not.solved'
*
* <p>Of course you can overwrite these values in a specific lesson
*
* @return a builder for creating a result from a lesson
* @param assignment
*/
public static AttackResultBuilder failed(AssignmentEndpoint assignment) {
return new AttackResultBuilder()
.lessonCompleted(false)
.assignmentCompleted(true)
.attemptWasMade()
.feedback("assignment.not.solved")
.assignment(assignment);
}
public static AttackResultBuilder informationMessage(AssignmentEndpoint assignment) {
return new AttackResultBuilder().lessonCompleted(false).assignment(assignment);
}
}

View File

@ -0,0 +1,41 @@
package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.i18n.PluginMessages;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/** This class intercepts the response body and applies the plugin messages to the attack result. */
@RestControllerAdvice
public class AttackResultMessageResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private final PluginMessages pluginMessages;
public AttackResultMessageResponseBodyAdvice(PluginMessages pluginMessages) {
this.pluginMessages = pluginMessages;
}
@Override
public boolean supports(
MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(
Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
if (body instanceof AttackResult a) {
return a.apply(pluginMessages);
}
return body;
}
}

View File

@ -22,27 +22,30 @@
package org.owasp.webgoat.container.assignments;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice
public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
private UserTrackerRepository userTrackerRepository;
private WebSession webSession;
private final Course course;
private final UserProgressRepository userProgressRepository;
public LessonTrackerInterceptor(
UserTrackerRepository userTrackerRepository, WebSession webSession) {
this.userTrackerRepository = userTrackerRepository;
this.webSession = webSession;
public LessonTrackerInterceptor(Course course, UserProgressRepository userProgressRepository) {
this.course = course;
this.userProgressRepository = userProgressRepository;
}
@Override
@ -65,18 +68,30 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice<Object> {
return o;
}
protected AttackResult trackProgress(AttackResult attackResult) {
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
if (userTracker == null) {
userTracker = new UserTracker(webSession.getUserName());
}
if (attackResult.assignmentSolved()) {
userTracker.assignmentSolved(webSession.getCurrentLesson(), attackResult.getAssignment());
} else {
userTracker.assignmentFailed(webSession.getCurrentLesson());
}
userTrackerRepository.save(userTracker);
private void trackProgress(AttackResult attackResult) {
var user = (WebGoatUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Assert.notNull(user, "User not found in SecurityContext");
var username = realUsername(user);
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 org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class StartLesson {
private final WebSession ws;
private final Course course;
public StartLesson(WebSession ws, Course course) {
this.ws = ws;
public StartLesson(Course course) {
this.course = course;
}
/**
* start.
*
* @return a {@link ModelAndView} object.
*/
@RequestMapping(
path = "startlesson.mvc",
method = {RequestMethod.GET, RequestMethod.POST})
public ModelAndView start() {
var model = new ModelAndView();
model.addObject("course", course);
model.addObject("lesson", ws.getCurrentLesson());
model.setViewName("lesson_content");
return model;
}
@RequestMapping(
@GetMapping(
value = {"*.lesson"},
produces = "text/html")
public ModelAndView lessonPage(HttpServletRequest request) {
@ -81,8 +59,7 @@ public class StartLesson {
.findFirst()
.ifPresent(
lesson -> {
ws.setCurrentLesson(lesson);
model.addObject("lesson", lesson);
request.setAttribute("lesson", lesson);
});
return model;

View File

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

View File

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

View File

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

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

View File

@ -35,6 +35,5 @@ package org.owasp.webgoat.container.lessons;
*/
public enum LessonMenuItemType {
CATEGORY,
LESSON,
STAGE
LESSON
}

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

@ -1,42 +0,0 @@
package org.owasp.webgoat.container.lessons;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class LessonScanner {
private static final Pattern lessonPattern = Pattern.compile("^.*/lessons/([^/]*)/.*$");
@Getter private final Set<String> lessons = new HashSet<>();
public LessonScanner(ResourcePatternResolver resourcePatternResolver) {
try {
var resources = resourcePatternResolver.getResources("classpath:/lessons/*/*");
for (var resource : resources) {
// WG can run as a fat jar or as directly from file system we need to support both so use
// the URL
var url = resource.getURL();
var matcher = lessonPattern.matcher(url.toString());
if (matcher.matches()) {
lessons.add(matcher.group(1));
}
}
log.debug("Found {} lessons", lessons.size());
} catch (IOException e) {
log.warn("No lessons found...");
}
}
public List<String> applyPattern(String pattern) {
return lessons.stream().map(lesson -> String.format(pattern, lesson)).toList();
}
}

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

View File

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

View File

@ -30,18 +30,16 @@ package org.owasp.webgoat.container.service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import org.owasp.webgoat.container.lessons.Assignment;
import org.owasp.webgoat.container.CurrentUsername;
import org.owasp.webgoat.container.lessons.Category;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.lessons.LessonMenuItem;
import org.owasp.webgoat.container.lessons.LessonMenuItemType;
import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.LessonTracker;
import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.owasp.webgoat.container.users.LessonProgress;
import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@ -59,8 +57,7 @@ public class LessonMenuService {
public static final String URL_LESSONMENU_MVC = "/service/lessonmenu.mvc";
private final Course course;
private final WebSession webSession;
private UserTrackerRepository userTrackerRepository;
private UserProgressRepository userTrackerRepository;
@Value("#{'${exclude.categories}'.split(',')}")
private List<String> excludeCategories;
@ -74,10 +71,13 @@ public class LessonMenuService {
* @return a {@link java.util.List} object.
*/
@RequestMapping(path = URL_LESSONMENU_MVC, produces = "application/json")
public @ResponseBody List<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<Category> categories = course.getCategories();
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
UserProgress userTracker = userTrackerRepository.findByUser(username);
for (Category category : categories) {
if (excludeCategories.contains(category.name())) {
@ -97,28 +97,14 @@ public class LessonMenuService {
lessonItem.setName(lesson.getTitle());
lessonItem.setLink(lesson.getLink());
lessonItem.setType(LessonMenuItemType.LESSON);
LessonTracker lessonTracker = userTracker.getLessonTracker(lesson);
boolean lessonSolved = lessonCompleted(lessonTracker.getLessonOverview(), lesson);
LessonProgress lessonTracker = userTracker.getLessonProgress(lesson);
boolean lessonSolved = lessonTracker.isLessonSolved();
lessonItem.setComplete(lessonSolved);
categoryItem.addChild(lessonItem);
}
categoryItem.getChildren().sort((o1, o2) -> o1.getRanking() - o2.getRanking());
categoryItem.getChildren().sort(Comparator.comparingInt(LessonMenuItem::getRanking));
menu.add(categoryItem);
}
return menu;
}
private boolean lessonCompleted(Map<Assignment, Boolean> map, Lesson currentLesson) {
boolean result = true;
for (Map.Entry<Assignment, Boolean> entry : map.entrySet()) {
Assignment storedAssignment = entry.getKey();
for (Assignment lessonAssignment : currentLesson.getAssignments()) {
if (lessonAssignment.getName().equals(storedAssignment.getName())) {
result = result && entry.getValue();
break;
}
}
}
return result;
}
}

View File

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

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.extern.slf4j.Slf4j;
import org.flywaydb.core.Flyway;
import org.owasp.webgoat.container.lessons.Initializeable;
import org.owasp.webgoat.container.lessons.Lesson;
import org.owasp.webgoat.container.session.WebSession;
import org.owasp.webgoat.container.users.UserTracker;
import org.owasp.webgoat.container.users.UserTrackerRepository;
import org.owasp.webgoat.container.CurrentUser;
import org.owasp.webgoat.container.lessons.Initializable;
import org.owasp.webgoat.container.lessons.LessonName;
import org.owasp.webgoat.container.session.Course;
import org.owasp.webgoat.container.users.UserProgress;
import org.owasp.webgoat.container.users.UserProgressRepository;
import org.owasp.webgoat.container.users.WebGoatUser;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
@ -44,25 +47,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@Slf4j
public class RestartLessonService {
private final WebSession webSession;
private final UserTrackerRepository userTrackerRepository;
private final Course course;
private final UserProgressRepository userTrackerRepository;
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)
public void restartLesson() {
Lesson al = webSession.getCurrentLesson();
log.debug("Restarting lesson: " + al);
public void restartLesson(
@PathVariable("lesson") LessonName lessonName, @CurrentUser WebGoatUser user) {
var lesson = course.getLessonByName(lessonName);
UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName());
userTracker.reset(al);
UserProgress userTracker = userTrackerRepository.findByUser(user.getUsername());
userTracker.reset(lesson);
userTrackerRepository.save(userTracker);
var flyway = flywayLessons.apply(webSession.getUserName());
var flyway = flywayLessons.apply(user.getUsername());
flyway.clean();
flyway.migrate();
lessonsToInitialize.forEach(i -> i.initialize(webSession.getUser()));
lessonsToInitialize.forEach(i -> i.initialize(user));
}
}

View File

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

View File

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

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

View File

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

View File

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

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

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

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.authbypass;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
@ -32,9 +35,7 @@ import java.util.Map;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.container.session.UserSessionData;
import org.owasp.webgoat.container.session.WebSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.owasp.webgoat.container.session.LessonSession;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@ -48,11 +49,13 @@ import org.springframework.web.bind.annotation.RestController;
"auth-bypass.hints.verify.3",
"auth-bypass.hints.verify.4"
})
public class VerifyAccount extends AssignmentEndpoint {
public class VerifyAccount implements AssignmentEndpoint {
@Autowired private WebSession webSession;
private final LessonSession userSessionData;
@Autowired UserSessionData userSessionData;
public VerifyAccount(LessonSession userSessionData) {
this.userSessionData = userSessionData;
}
@PostMapping(
path = "/auth-bypass/verify-account",

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.bypassrestrictions;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BypassRestrictionsFieldRestrictions extends AssignmentEndpoint {
public class BypassRestrictionsFieldRestrictions implements AssignmentEndpoint {
@PostMapping("/BypassRestrictions/FieldRestrictions")
@ResponseBody

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.bypassrestrictions;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BypassRestrictionsFrontendValidation extends AssignmentEndpoint {
public class BypassRestrictionsFrontendValidation implements AssignmentEndpoint {
@PostMapping("/BypassRestrictions/frontendValidation")
@ResponseBody

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.Lesson;
import org.springframework.stereotype.Component;
/**
* @author nbaars
* @since 3/21/17.
*/
@Component
public class ChallengeIntro extends Lesson {
@Override

View File

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

View File

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

View File

@ -1,8 +1,9 @@
package org.owasp.webgoat.lessons.challenges.challenge1;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD;
import lombok.RequiredArgsConstructor;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.owasp.webgoat.lessons.challenges.Flags;
@ -42,11 +43,14 @@ import org.springframework.web.bind.annotation.RestController;
* @since August 11, 2016
*/
@RestController
@RequiredArgsConstructor
public class Assignment1 extends AssignmentEndpoint {
public class Assignment1 implements AssignmentEndpoint {
private final Flags flags;
public Assignment1(Flags flags) {
this.flags = flags;
}
@PostMapping("/challenge/1")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.challenges.challenge5;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import lombok.RequiredArgsConstructor;
@ -39,7 +42,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequiredArgsConstructor
public class Assignment5 extends AssignmentEndpoint {
public class Assignment5 implements AssignmentEndpoint {
private final LessonDataSource dataSource;
private final Flags flags;

View File

@ -1,5 +1,7 @@
package org.owasp.webgoat.lessons.challenges.challenge7;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
@ -29,7 +31,7 @@ import org.springframework.web.client.RestTemplate;
*/
@RestController
@Slf4j
public class Assignment7 extends AssignmentEndpoint {
public class Assignment7 implements AssignmentEndpoint {
public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2";

View File

@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequiredArgsConstructor
public class Assignment8 extends AssignmentEndpoint {
public class Assignment8 implements AssignmentEndpoint {
private static final Map<Integer, Integer> votes = new HashMap<>();

View File

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

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.chromedevtools;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@ -40,7 +43,7 @@ import org.springframework.web.bind.annotation.RestController;
*/
@RestController
@AssignmentHints({"networkHint1", "networkHint2"})
public class NetworkLesson extends AssignmentEndpoint {
public class NetworkLesson implements AssignmentEndpoint {
@PostMapping(
value = "/ChromeDevTools/network",

View File

@ -1,5 +1,8 @@
package org.owasp.webgoat.lessons.cia;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.web.bind.annotation.GetMapping;
@ -9,9 +12,9 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CIAQuiz extends AssignmentEndpoint {
public class CIAQuiz implements AssignmentEndpoint {
String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
private final String[] solutions = {"Solution 3", "Solution 1", "Solution 4", "Solution 2"};
boolean[] guesses = new boolean[solutions.length];
@PostMapping("/cia/quiz")

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.clientsidefiltering;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@ -37,7 +40,7 @@ import org.springframework.web.bind.annotation.RestController;
"ClientSideFilteringHint3",
"ClientSideFilteringHint4"
})
public class ClientSideFilteringAssignment extends AssignmentEndpoint {
public class ClientSideFilteringAssignment implements AssignmentEndpoint {
@PostMapping("/clientSideFiltering/attack1")
@ResponseBody

View File

@ -22,6 +22,9 @@
package org.owasp.webgoat.lessons.clientsidefiltering;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
@ -40,8 +43,7 @@ import org.springframework.web.bind.annotation.RestController;
"client.side.filtering.free.hint2",
"client.side.filtering.free.hint3"
})
public class ClientSideFilteringFreeAssignment extends AssignmentEndpoint {
public class ClientSideFilteringFreeAssignment implements AssignmentEndpoint {
public static final String SUPER_COUPON_CODE = "get_it_for_free";
@PostMapping("/clientSideFiltering/getItForFree")

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