From c0ce72a2bd8279d4dcfe83a5f2ecd50911f18cd2 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 23 May 2020 16:30:35 +0200 Subject: [PATCH 001/155] Remove Github pages, too many things to keep in sync. Let's keep the focus on the OWASP page and the Github README --- docs/index.html | 568 ++++++++++++++++++++++++------------------------ 1 file changed, 290 insertions(+), 278 deletions(-) diff --git a/docs/index.html b/docs/index.html index 9e408e860..5c58e5e04 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,285 +2,297 @@ - - - - - - WebGoat - - - - - - - - - - - - - - - + + - - - - - - - -
-
-
-
-

Learn the hack - Stop the attack

- - -
-
-
-
-

WebGoat is a deliberately insecure application that allows interested developers just like you to test vulnerabilities - commonly found in Java-based applications that use common and popular open source components.

-
- -
-
-
- - -
-
-

Learn in 3 steps

-
- -
-
- - -
-
-

Goals

-
-
-
-

- Web application security is difficult to learn and practice. Not many people have full blown web applications like online book - stores or online banks that can be used to scan for vulnerabilities. In addition, security professionals frequently need to test - tools against a platform known to be vulnerable to ensure that they perform as advertised. All of this needs to happen in a safe - and legal environment. - -

-
-
-

Even if your intentions are good, we believe you should never attempt to find vulnerabilities without - permission. The primary goal of the WebGoat project is simple: create a de-facto interactive teaching environment for web application security. - In the future, the project team hopes to extend WebGoat into becoming a security benchmarking platform and a Java-based Web site Honeypot. -

-
-
-
-
- - -
-
-

More information

-
-
-
-

For more information about running WebGoat / FAQ see our wiki pages. -

-
-
-

Interested in contributing to WebGoat, take a look at our issues.

- -
-
-
-
- - - - - - - - -
- - - -
- - -
-
- - - -
-
-
-

Explain the vulnerability

-
- -

Teaching is now a first class citizen of WebGoat, we explain explain the vulnerability. Instead of 'just hacking' we now focus on explaining from the beginning what for example a SQL injection is. -

- - - Close -
-
-
-
-
- - -
-
- - - -
-
-
-

Learn by doing

-
- -

During the explanation of a vulnerability we build assignments which will help you understand how it works.

- - - Close -
-
-
-
-
- - -
-
- - - -
-
-
-

Explain mitigations

-
- -

At the end of each lesson you will receive an overview of possible mitigations which will help you during your development work.

- - - Close -
-
-
-
-
- - - - - - - - - - - - + +

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

+ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From dc0fc096796b49771bee57da6dc8f97015dbd446 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 14 Jan 2023 18:24:35 +0100 Subject: [PATCH 002/155] Move to main and skip develop Using main and develop imposes a complicated release process with Gitflow etc. To simplify our release process we move our development to the main branch skipping develop. --- .github/workflows/build.yml | 2 - .github/workflows/release.yml | 41 +++++++++-------- .github/workflows/test.yml | 83 +++++++++++++++++------------------ CONTRIBUTING.md | 2 +- CREATE_RELEASE.md | 27 +++--------- 5 files changed, 68 insertions(+), 87 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1d4081804..b58c1f4d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,8 +8,6 @@ on: push: branches: - main - - develop - - release/* tags-ignore: - '*' paths-ignore: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca83dfd2a..3e5686b8e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,10 +13,6 @@ jobs: steps: - uses: actions/checkout@v3 - - name: "Get tag name" - id: tag - uses: dawidd6/action-get-tag@v1 - - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -33,8 +29,8 @@ jobs: - name: "Set labels for ${{ github.ref }}" run: | - echo "WEBGOAT_TAG_VERSION=${{ steps.tag.outputs.tag }}" >> $GITHUB_ENV - WEBGOAT_MAVEN_VERSION=${{ steps.tag.outputs.tag }} + echo "WEBGOAT_TAG_VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + WEBGOAT_MAVEN_VERSION=${{ github.ref_name }} echo "WEBGOAT_MAVEN_VERSION=${WEBGOAT_MAVEN_VERSION:1}" >> $GITHUB_ENV - name: Build with Maven run: | @@ -94,7 +90,7 @@ jobs: with: context: ./ file: ./Dockerfile - push: true + push: true platforms: linux/amd64, linux/arm64, linux/arm/v7 tags: | webgoat/webgoat:${{ env.WEBGOAT_TAG_VERSION }} @@ -105,19 +101,14 @@ jobs: - name: "Image digest" run: echo ${{ steps.docker_build.outputs.digest }} new_version: - permissions: - contents: write # for Git to git push if: github.repository == 'WebGoat/WebGoat' - name: Update development version + name: Update to next SNAPSHOT version needs: [ release ] runs-on: ubuntu-latest - environment: - name: release steps: - uses: actions/checkout@v3 with: - ref: develop - token: ${{ secrets.WEBGOAT_DEPLOYER_TOKEN }} + fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -129,10 +120,18 @@ jobs: run: | mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT versions:commit - - name: Commit pom.xml - run: | - git config user.name webgoat-github - git config user.email owasp.webgoat@gmail.com - find . -name 'pom.xml' | xargs git add - git commit -m "Updating to the new development version" - git push + - name: Push the changes to new branch + uses: devops-infra/action-commit-push@v0.9.2 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + add_timestamp: true + commit_message: "Updating to the new development version" + force: false + + - name: Create PR + uses: devops-infra/action-pull-request@v0.5.3 + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + title: ${{ github.event.commits[0].message }} + target_branch: main + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bea73c6f3..00221ab92 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,14 +2,11 @@ name: "UI-Test" on: pull_request: paths-ignore: - - '.txt' - - '*.MD' - - '*.md' - 'LICENSE' - 'docs/**' push: -# tags-ignore: -# - '*' + tags-ignore: + - 'v*' paths-ignore: - '.txt' - '*.MD' @@ -24,45 +21,45 @@ jobs: name: "Robot framework test" steps: # Uses an default action to checkout the code - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 # Uses an action to add Python to the VM - - name: Setup Pyton - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: x64 + - name: Setup Pyton + uses: actions/setup-python@v4 + 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@v3 - with: - distribution: 'temurin' - java-version: 17 - architecture: x64 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + 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.2.3 - with: - path: ~/.m2 - key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ubuntu-latest-m2- - - uses: BSFishy/pip-action@v1 - with: - packages: | - robotframework - robotframework-SeleniumLibrary - webdriver-manager - - name: Run with Maven - run: mvn --no-transfer-progress spring-boot:run & - - name: Wait to start - uses: ifaxity/wait-on-action@v1 - with: - resource: http://127.0.0.1:8080/WebGoat - - name: Test with Robotframework - run: python3 -m robot --variable HEADLESS:"1" --outputdir robotreport robot/goat.robot + - name: Cache Maven packages + uses: actions/cache@v3.2.3 + with: + path: ~/.m2 + key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ubuntu-latest-m2- + - uses: BSFishy/pip-action@v1 + with: + packages: | + robotframework + robotframework-SeleniumLibrary + webdriver-manager + - name: Run with Maven + run: mvn --no-transfer-progress spring-boot:run & + - name: Wait to start + uses: ifaxity/wait-on-action@v1 + with: + resource: http://127.0.0.1:8080/WebGoat + - name: Test with Robotframework + run: python3 -m robot --variable HEADLESS:"1" --outputdir robotreport robot/goat.robot # send report to forks only due to limits on permission tokens - - name: Send report to commit - if: github.repository != 'WebGoat/WebGoat' && github.event_name == 'push' - uses: joonvena/robotframework-reporter-action@v2.1 - with: - gh_access_token: ${{ secrets.GITHUB_TOKEN }} - report_path: 'robotreport' + - name: Send report to commit + if: github.repository != 'WebGoat/WebGoat' && github.event_name == 'push' + uses: joonvena/robotframework-reporter-action@v2.1 + with: + gh_access_token: ${{ secrets.GITHUB_TOKEN }} + report_path: 'robotreport' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6f530e5f..4a97e18ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ For example: `Fix #545` or `Closes #10` ```bash $ git fetch upstream - $ git merge upstream/develop + $ git merge upstream/main ``` See also the following article for further explanation on "[How to Keep a Downstream git Repository Current with Upstream Repository Changes](https://medium.com/sweetmeat/how-to-keep-a-downstream-git-repository-current-with-upstream-repository-changes-10b76fad6d97 "How to Keep a Downstream git Repository Current with Upstream Repository Changes")". diff --git a/CREATE_RELEASE.md b/CREATE_RELEASE.md index 1c37fd033..9a7531d74 100644 --- a/CREATE_RELEASE.md +++ b/CREATE_RELEASE.md @@ -2,31 +2,18 @@ ### Version numbers -For WebGoat we use milestone releases first before we release the official version, we use `v8.0.0.M3` while tagging -and 8.0.0.M3 in the `pom.xml`. When we create the final release we remove the milestone release and use -`v8.0.0` in the `pom.xml` +For WebGoat we use milestone releases first before we release the official version, we use `v2023.01` while tagging +and 2023.01 in the `pom.xml`. ### Release notes: -Update the release notes with the correct version. Use `git shortlog -s -n --since "SEP 31 2019"` for the list of +Update the release notes with the correct version. Use `git shortlog -s -n --since "JAN 06 2023"` for the list of committers. -At the moment we use Gitflow, for a release you create a new release branch and take the following steps: - ``` -git checkout develop -git flow release start -git flow release publish - -<> -<> - -git flow release finish -git push origin develop -git push origin main +mvn versions:set +<< update release notes >> +git commit .... +git tag v2023.01 git push --tags ``` - -Now Travis takes over and will create the release in Github and on Docker Hub. - -NOTE: the `mvn versions:set` command above is just there to make sure the master branch contains the latest version From f9b810c5ee2d6731eb5e37172af20d276a7dfb98 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sat, 14 Jan 2023 18:29:24 +0100 Subject: [PATCH 003/155] Fix formatting issue --- CREATE_RELEASE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CREATE_RELEASE.md b/CREATE_RELEASE.md index 9a7531d74..7bda7405a 100644 --- a/CREATE_RELEASE.md +++ b/CREATE_RELEASE.md @@ -3,7 +3,7 @@ ### Version numbers For WebGoat we use milestone releases first before we release the official version, we use `v2023.01` while tagging -and 2023.01 in the `pom.xml`. +and 2023.01 in the `pom.xml`. ### Release notes: @@ -17,3 +17,4 @@ git commit .... git tag v2023.01 git push --tags ``` + From 77c91b8df879744982edc16375fdbcf52424aed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Feb 2023 09:57:30 +0000 Subject: [PATCH 004/155] Bump actions/cache from 3.2.3 to 3.2.5 Bumps [actions/cache](https://github.com/actions/cache) from 3.2.3 to 3.2.5. - [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.2.3...v3.2.5) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b58c1f4d9..2e652ee69 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.2.5 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -57,7 +57,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.2.5 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e5686b8e..529988650 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.3 + uses: actions/cache@v3.2.5 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 00221ab92..cc56939fe 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: 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.2.3 + uses: actions/cache@v3.2.5 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} From c9d1653d4f5dea82300559935cda839dd19910db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 09:01:41 +0000 Subject: [PATCH 005/155] Bump docker/build-push-action from 3.2.0 to 4.0.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3.2.0 to 4.0.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v3.2.0...v4.0.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 529988650..a247150e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: "Build and push" - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v4.0.0 with: context: ./ file: ./Dockerfile From bd398e4c09a202d2bbd2b6c2f091b090130cef82 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 15 Feb 2023 11:44:56 +0000 Subject: [PATCH 006/155] #1396 Fix templates path for views --- pom.xml | 4 +- .../passwordreset/ResetLinkAssignment.java | 40 ++++--- .../webgoat/container/plugins/LessonTest.java | 2 +- .../ResetLinkAssignmentTest.java | 103 ++++++++++++++++++ 4 files changed, 125 insertions(+), 24 deletions(-) create mode 100644 src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java diff --git a/pom.xml b/pom.xml index 15851ec3b..edaf48b00 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.owasp.webgoat webgoat - 2023.3 + 2023.4-SNAPSHOT jar WebGoat @@ -675,7 +675,7 @@ ${project.build.directory}/webgoat-${project.version}.jar false - http://localhost:${webgoat.port}/WebGoat/ + http://localhost:${webgoat.port}/WebGoat/actuator/health diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java index ace84be78..4dacce209 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignment.java @@ -57,6 +57,7 @@ import org.springframework.web.servlet.ModelAndView; }) public class ResetLinkAssignment extends AssignmentEndpoint { + private static final String VIEW_FORMATTER = "lessons/passwordreset/templates/%s.html"; static final String PASSWORD_TOM_9 = "somethingVeryRandomWhichNoOneWillEverTypeInAsPasswordForTom"; static final String TOM_EMAIL = "tom@webgoat-cloud.org"; @@ -65,15 +66,18 @@ public class ResetLinkAssignment extends AssignmentEndpoint { static List resetLinks = new ArrayList<>(); static final String TEMPLATE = - "Hi, you requested a password reset link, please use this link to reset your" - + " password.\n" - + " \n\n" - + "If you did not request this password change you can ignore this message.\n" - + "If you have any comments or questions, please do not hesitate to reach us at" - + " support@webgoat-cloud.org\n\n" - + "Kind regards, \n" - + "Team WebGoat"; + """ + Hi, you requested a password reset link, please use this link to reset your + password. + + If you did not request this password change you can ignore this message. + If you have any comments or questions, please do not hesitate to reach us at + support@webgoat-cloud.org + + Kind regards, + Team WebGoat + """; @PostMapping("/PasswordReset/reset/login") @ResponseBody @@ -98,20 +102,14 @@ public class ResetLinkAssignment extends AssignmentEndpoint { form.setResetLink(link); model.addAttribute("form", form); modelAndView.addObject("form", form); - modelAndView.setViewName("password_reset"); // Display html page for changing password + modelAndView.setViewName( + VIEW_FORMATTER.formatted("password_reset")); // Display html page for changing password } else { - modelAndView.setViewName("password_link_not_found"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); } return modelAndView; } - @GetMapping("/PasswordReset/reset/change-password") - public ModelAndView illegalCall() { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("password_link_not_found"); - return modelAndView; - } - @PostMapping("/PasswordReset/reset/change-password") public ModelAndView changePassword( @ModelAttribute("form") PasswordChangeForm form, BindingResult bindingResult) { @@ -120,17 +118,17 @@ public class ResetLinkAssignment extends AssignmentEndpoint { bindingResult.rejectValue("password", "not.empty"); } if (bindingResult.hasErrors()) { - modelAndView.setViewName("password_reset"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_reset")); return modelAndView; } if (!resetLinks.contains(form.getResetLink())) { - modelAndView.setViewName("password_link_not_found"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("password_link_not_found")); return modelAndView; } if (checkIfLinkIsFromTom(form.getResetLink())) { usersToTomPassword.put(getWebSession().getUserName(), form.getPassword()); } - modelAndView.setViewName("lessons/passwordreset/templates/success.html"); + modelAndView.setViewName(VIEW_FORMATTER.formatted("success")); return modelAndView; } diff --git a/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java b/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java index 465046c67..6d7ec5ed2 100644 --- a/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java +++ b/src/test/java/org/owasp/webgoat/container/plugins/LessonTest.java @@ -18,7 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.context.WebApplicationContext; diff --git a/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java b/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java new file mode 100644 index 000000000..6010c432d --- /dev/null +++ b/src/test/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentTest.java @@ -0,0 +1,103 @@ +package org.owasp.webgoat.lessons.passwordreset; + +import static org.mockito.Mockito.when; +import static org.owasp.webgoat.lessons.passwordreset.ResetLinkAssignment.TOM_EMAIL; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.owasp.webgoat.container.plugins.LessonTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +@ExtendWith(SpringExtension.class) +class ResetLinkAssignmentTest extends LessonTest { + + @Value("${webwolf.host}") + private String webWolfHost; + + @Value("${webwolf.port}") + private String webWolfPort; + + @Autowired private ResourceLoader resourceLoader; + + @BeforeEach + public void setup() { + when(webSession.getCurrentLesson()).thenReturn(new PasswordReset()); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + } + + @Test + void wrongResetLink() throws Exception { + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.get("/PasswordReset/reset/reset-password/{link}", "test")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void changePasswordWithoutPasswordShouldReturnPasswordForm() throws Exception { + MvcResult mvcResult = + mockMvc + .perform(MockMvcRequestBuilders.post("/PasswordReset/reset/change-password")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_reset.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void changePasswordWithoutLinkShouldReturnPasswordLinkNotFound() throws Exception { + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.post("/PasswordReset/reset/change-password") + .param("password", "new_password")) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_link_not_found.html")) + .andReturn(); + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } + + @Test + void knownLinkShouldReturnPasswordResetPage() throws Exception { + // Create a reset link + mockMvc + .perform( + MockMvcRequestBuilders.post("/PasswordReset/ForgotPassword/create-password-reset-link") + .param("email", TOM_EMAIL) + .header(HttpHeaders.HOST, webWolfHost + ":" + webWolfPort)) + .andExpect(status().isOk()); + Assertions.assertThat(ResetLinkAssignment.resetLinks).isNotEmpty(); + + // With a known link you should be + MvcResult mvcResult = + mockMvc + .perform( + MockMvcRequestBuilders.get( + "/PasswordReset/reset/reset-password/{link}", + ResetLinkAssignment.resetLinks.get(0))) + .andExpect(status().isOk()) + .andExpect(view().name("lessons/passwordreset/templates/password_reset.html")) + .andReturn(); + + Assertions.assertThat(resourceLoader.getResource(mvcResult.getModelAndView().getViewName())) + .isNotNull(); + } +} From ae081ce3191aec8ee2e33b92a9ee9937eef14aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Tue, 14 Feb 2023 23:45:35 +0100 Subject: [PATCH 007/155] Add fileserver location (test) --- src/test/resources/application-webwolf.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/resources/application-webwolf.properties b/src/test/resources/application-webwolf.properties index 025019b11..b2eedc2ca 100644 --- a/src/test/resources/application-webwolf.properties +++ b/src/test/resources/application-webwolf.properties @@ -6,4 +6,5 @@ spring.main.banner-mode=off spring.jpa.properties.hibernate.default_schema=CONTAINER spring.thymeleaf.prefix=classpath:/webwolf/templates/ +webwolf.fileserver.location=${java.io.tmpdir}/webwolf-fileserver From eb4c8388f89d5fa57bf886d609cde28340ff7072 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 12 Feb 2023 22:17:23 +0000 Subject: [PATCH 008/155] Update Dockerfile --- Dockerfile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f4af6fed2..f89bf98c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,11 @@ FROM docker.io/eclipse-temurin:17-jre-focal +LABEL NAME = "WebGoat: A deliberately insecure Web Application" +MAINTAINER "WebGoat team" -RUN useradd -ms /bin/bash webgoat -RUN chgrp -R 0 /home/webgoat -RUN chmod -R g=u /home/webgoat +RUN \ + useradd -ms /bin/bash webgoat && \ + chgrp -R 0 /home/webgoat && \ + chmod -R g=u /home/webgoat USER webgoat From 3ec34b0df59646eb76038509af820a194f3b07ad Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 15 Feb 2023 17:28:15 +0000 Subject: [PATCH 009/155] fix: challenge test fails sometimes when calling scoreboard endpoint --- .../webgoat/ChallengeIntegrationTest.java | 176 +++++++++--------- .../org/owasp/webgoat/IntegrationTest.java | 2 +- .../webgoat/container/users/Scoreboard.java | 24 +-- 3 files changed, 99 insertions(+), 103 deletions(-) diff --git a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java index f4f8152c7..cc9cd397d 100644 --- a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java @@ -1,112 +1,112 @@ package org.owasp.webgoat; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.restassured.RestAssured; -import org.junit.jupiter.api.Test; - import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertTrue; - +import org.junit.jupiter.api.Test; public class ChallengeIntegrationTest extends IntegrationTest { - @Test - public void testChallenge1() { - startLesson("Challenge1"); + @Test + public void testChallenge1() { + startLesson("Challenge1"); - byte[] resultBytes = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/challenge/logo")) - .then() - .statusCode(200) - .extract().asByteArray(); + byte[] resultBytes = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/challenge/logo")) + .then() + .statusCode(200) + .extract() + .asByteArray(); - String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220)); - Map params = new HashMap<>(); - params.clear(); - params.put("username", "admin"); - params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode)); + String pincode = new String(Arrays.copyOfRange(resultBytes, 81216, 81220)); + Map params = new HashMap<>(); + params.clear(); + params.put("username", "admin"); + params.put("password", "!!webgoat_admin_1234!!".replace("1234", pincode)); + checkAssignment(url("/WebGoat/challenge/1"), params, true); + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .post(url("/WebGoat/challenge/1")) + .then() + .statusCode(200) + .extract() + .asString(); - checkAssignment(url("/WebGoat/challenge/1"), params, true); - String result = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams(params) - .post(url("/WebGoat/challenge/1")) - .then() - .statusCode(200) - .extract().asString(); + String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); + params.clear(); + params.put("flag", flag); + checkAssignment(url("/WebGoat/challenge/flag"), params, true); - String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); - params.clear(); - params.put("flag", flag); - checkAssignment(url("/WebGoat/challenge/flag"), params, true); + checkResults("/challenge/1"); + List capturefFlags = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/scoreboard-data")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); + assertTrue(capturefFlags.contains("Admin lost password")); + } - checkResults("/challenge/1"); + @Test + public void testChallenge5() { + startLesson("Challenge5"); - List capturefFlags = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/scoreboard-data")) - .then() - .statusCode(200) - .extract().jsonPath() - .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); - assertTrue(capturefFlags.contains("Admin lost password")); - } + Map params = new HashMap<>(); + params.clear(); + params.put("username_login", "Larry"); + params.put("password_login", "1' or '1'='1"); - @Test - public void testChallenge5() { - startLesson("Challenge5"); + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .post(url("/WebGoat/challenge/5")) + .then() + .statusCode(200) + .extract() + .asString(); - Map params = new HashMap<>(); - params.clear(); - params.put("username_login", "Larry"); - params.put("password_login", "1' or '1'='1"); + String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); + params.clear(); + params.put("flag", flag); + checkAssignment(url("/WebGoat/challenge/flag"), params, true); - String result = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams(params) - .post(url("/WebGoat/challenge/5")) - .then() - .statusCode(200) - .extract().asString(); - - String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); - params.clear(); - params.put("flag", flag); - checkAssignment(url("/WebGoat/challenge/flag"), params, true); - - - checkResults("/challenge/5"); - - List capturefFlags = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/scoreboard-data")) - .then() - .statusCode(200) - .extract().jsonPath() - .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); - assertTrue(capturefFlags.contains("Without password")); - } + checkResults("/challenge/5"); + List capturefFlags = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/scoreboard-data")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); + assertTrue(capturefFlags.contains("Without password")); + } } diff --git a/src/it/java/org/owasp/webgoat/IntegrationTest.java b/src/it/java/org/owasp/webgoat/IntegrationTest.java index c04c9578d..c3fb3d9ff 100644 --- a/src/it/java/org/owasp/webgoat/IntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IntegrationTest.java @@ -26,7 +26,7 @@ public abstract class IntegrationTest { @Getter private String webWolfCookie; @Getter - private String user = "webgoat"; + private final String user = "webgoat"; protected String url(String url) { url = url.replaceFirst("/WebGoat/", ""); diff --git a/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java index 2f5ddefe2..3d94b056b 100644 --- a/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java +++ b/src/main/java/org/owasp/webgoat/container/users/Scoreboard.java @@ -1,8 +1,8 @@ package org.owasp.webgoat.container.users; -import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.Getter; import org.owasp.webgoat.container.i18n.PluginMessages; @@ -35,19 +35,15 @@ public class Scoreboard { @GetMapping("/scoreboard-data") public List getRankings() { - List allUsers = userRepository.findAll(); - List rankings = new ArrayList<>(); - for (WebGoatUser user : allUsers) { - if (user.getUsername().startsWith("csrf-")) { - // the csrf- assignment specific users do not need to be in the overview - continue; - } - UserTracker userTracker = userTrackerRepository.findByUser(user.getUsername()); - rankings.add(new Ranking(user.getUsername(), challengesSolved(userTracker))); - } - /* sort on number of captured flags to present an ordered ranking */ - rankings.sort((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size()); - return rankings; + return userRepository.findAll().stream() + .filter(user -> !user.getUsername().startsWith("csrf-")) + .map( + user -> + new Ranking( + user.getUsername(), + challengesSolved(userTrackerRepository.findByUser(user.getUsername())))) + .sorted((o1, o2) -> o2.getFlagsCaptured().size() - o1.getFlagsCaptured().size()) + .collect(Collectors.toList()); } private List challengesSolved(UserTracker userTracker) { From 390ff39f1926c43ad435792e018216cb7b48fa9b Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 15 Feb 2023 18:47:52 +0000 Subject: [PATCH 010/155] chore: format src/test/it as well --- pom.xml | 5 + .../webgoat/AccessControlIntegrationTest.java | 132 ++--- .../owasp/webgoat/CSRFIntegrationTest.java | 474 +++++++++--------- .../webgoat/ChallengeIntegrationTest.java | 2 - .../owasp/webgoat/CryptoIntegrationTest.java | 240 +++++---- .../DeserializationIntegrationTest.java | 39 +- .../webgoat/GeneralLessonIntegrationTest.java | 3 +- .../owasp/webgoat/IDORIntegrationTest.java | 158 +++--- .../org/owasp/webgoat/IntegrationTest.java | 428 ++++++++-------- .../webgoat/JWTLessonIntegrationTest.java | 400 ++++++++------- .../webgoat/LabelAndHintIntegrationTest.java | 336 ++++++++----- .../PasswordResetLessonIntegrationTest.java | 219 ++++---- .../webgoat/PathTraversalIntegrationTest.java | 245 ++++----- .../ProgressRaceConditionIntegrationTest.java | 72 +-- .../owasp/webgoat/SSRFIntegrationTest.java | 38 +- .../SessionManagementIntegrationTest.java | 22 +- .../SqlInjectionAdvancedIntegrationTest.java | 78 +-- .../SqlInjectionLessonIntegrationTest.java | 110 ++-- ...SqlInjectionMitigationIntegrationTest.java | 125 +++-- .../owasp/webgoat/WebWolfIntegrationTest.java | 115 +++-- .../org/owasp/webgoat/XSSIntegrationTest.java | 116 +++-- .../org/owasp/webgoat/XXEIntegrationTest.java | 191 ++++--- 22 files changed, 1915 insertions(+), 1633 deletions(-) diff --git a/pom.xml b/pom.xml index edaf48b00..74af4dd62 100644 --- a/pom.xml +++ b/pom.xml @@ -536,6 +536,11 @@ + + src/main/java/**/*.java + src/test/java/**/*.java + src/it/java/**/*.java + diff --git a/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java index d57661f9a..761aa07f7 100644 --- a/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/AccessControlIntegrationTest.java @@ -1,86 +1,86 @@ package org.owasp.webgoat; - import io.restassured.RestAssured; import io.restassured.http.ContentType; +import java.util.Map; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; -import java.util.Map; - class AccessControlIntegrationTest extends IntegrationTest { - @Test - void testLesson() { - startLesson("MissingFunctionAC", true); - assignment1(); - assignment2(); - assignment3(); + @Test + void testLesson() { + startLesson("MissingFunctionAC", true); + assignment1(); + assignment2(); + assignment3(); - checkResults("/access-control"); - } + checkResults("/access-control"); + } - private void assignment3() { - //direct call should fail if user has not been created - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/access-control/users-admin-fix")) - .then() - .statusCode(HttpStatus.SC_FORBIDDEN); + private void assignment3() { + // direct call should fail if user has not been created + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) + .then() + .statusCode(HttpStatus.SC_FORBIDDEN); - //create user - var userTemplate = """ + // create user + var userTemplate = + """ {"username":"%s","password":"%s","admin": "true"} """; + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .body(String.format(userTemplate, this.getUser(), this.getUser())) + .post(url("/WebGoat/access-control/users")) + .then() + .statusCode(HttpStatus.SC_OK); + + // get the users + var userHash = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .body(String.format(userTemplate, this.getUser(), this.getUser())) - .post(url("/WebGoat/access-control/users")) - .then() - .statusCode(HttpStatus.SC_OK); + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users-admin-fix")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"Jerry\" }.userHash"); - //get the users - var userHash = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/access-control/users-admin-fix")) - .then() - .statusCode(200) - .extract() - .jsonPath() - .get("find { it.username == \"Jerry\" }.userHash"); + checkAssignment( + url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true); + } - checkAssignment(url("/WebGoat/access-control/user-hash-fix"), Map.of("userHash", userHash), true); - } + private void assignment2() { + var userHash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/access-control/users")) + .then() + .statusCode(200) + .extract() + .jsonPath() + .get("find { it.username == \"Jerry\" }.userHash"); - private void assignment2() { - var userHash = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/access-control/users")) - .then() - .statusCode(200) - .extract() - .jsonPath() - .get("find { it.username == \"Jerry\" }.userHash"); + checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true); + } - checkAssignment(url("/WebGoat/access-control/user-hash"), Map.of("userHash", userHash), true); - } - - private void assignment1() { - var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); - checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); - } + private void assignment1() { + var params = Map.of("hiddenMenu1", "Users", "hiddenMenu2", "Config"); + checkAssignment(url("/WebGoat/access-control/hidden-menu"), params, true); + } } diff --git a/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java index 01d22d1aa..e590624f2 100644 --- a/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/CSRFIntegrationTest.java @@ -1,8 +1,18 @@ package org.owasp.webgoat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import lombok.Data; import lombok.SneakyThrows; import org.junit.jupiter.api.AfterEach; @@ -11,249 +21,265 @@ import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.owasp.webgoat.container.lessons.Assignment; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.DynamicTest.dynamicTest; - public class CSRFIntegrationTest extends IntegrationTest { - private static final String trickHTML3 = "
\n" + - "\n" + - "\n" + - "
"; + private static final String trickHTML3 = + "
\n" + + "\n" + + "\n" + + "
"; - private static final String trickHTML4 = "
\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "
\n" + - ""; + private static final String trickHTML4 = + "
\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
\n" + + ""; - private static final String trickHTML7 = "
\n" + - "\n" + - "\n" + - "
"; + private static final String trickHTML7 = + "
\n" + + "\n" + + "\n" + + "
"; - private static final String trickHTML8 = "
\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "
"; + private static final String trickHTML8 = + "
\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "
"; - private String webwolfFileDir; + private String webwolfFileDir; - @BeforeEach - @SneakyThrows - public void init() { - startLesson("CSRF"); - webwolfFileDir = getWebWolfFileServerLocation(); - uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag"))); - uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review"))); - uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message"))); - uploadTrickHtml("csrf8.html", trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser())); + @BeforeEach + @SneakyThrows + public void init() { + startLesson("CSRF"); + webwolfFileDir = getWebWolfFileServerLocation(); + uploadTrickHtml("csrf3.html", trickHTML3.replace("WEBGOATURL", url("/csrf/basic-get-flag"))); + uploadTrickHtml("csrf4.html", trickHTML4.replace("WEBGOATURL", url("/csrf/review"))); + uploadTrickHtml("csrf7.html", trickHTML7.replace("WEBGOATURL", url("/csrf/feedback/message"))); + uploadTrickHtml( + "csrf8.html", + trickHTML8.replace("WEBGOATURL", url("/login")).replace("USERNAME", this.getUser())); + } + + @TestFactory + Iterable testCSRFLesson() { + return Arrays.asList( + dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))), + dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))), + dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))), + dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html")))); + } + + @AfterEach + public void shutdown() throws IOException { + // logout(); + login(); // because old cookie got replaced and invalidated + startLesson("CSRF", false); + checkResults("/csrf"); + } + + private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { + + // remove any left over html + Path webWolfFilePath = Paths.get(webwolfFileDir); + if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) { + Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName))); } - @TestFactory - Iterable testCSRFLesson() { - return Arrays.asList( - dynamicTest("assignment 3", () -> checkAssignment3(callTrickHtml("csrf3.html"))), - dynamicTest("assignment 4", () -> checkAssignment4(callTrickHtml("csrf4.html"))), - dynamicTest("assignment 7", () -> checkAssignment7(callTrickHtml("csrf7.html"))), - dynamicTest("assignment 8", () -> checkAssignment8(callTrickHtml("csrf8.html"))) - ); - } + // upload trick html + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .multiPart("file", htmlName, htmlContent.getBytes()) + .post(webWolfUrl("/WebWolf/fileupload")) + .then() + .extract() + .response() + .getBody() + .asString(); + } - @AfterEach - public void shutdown() throws IOException { - //logout(); - login();//because old cookie got replaced and invalidated - startLesson("CSRF", false); - checkResults("/csrf"); - } - - private void uploadTrickHtml(String htmlName, String htmlContent) throws IOException { - - //remove any left over html - Path webWolfFilePath = Paths.get(webwolfFileDir); - if (webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName)).toFile().exists()) { - Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), htmlName))); - } - - //upload trick html + private String callTrickHtml(String htmlName) { + String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .multiPart("file", htmlName, htmlContent.getBytes()) - .post(webWolfUrl("/WebWolf/fileupload")) - .then() - .extract().response().getBody().asString(); - } + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName)) + .then() + .extract() + .response() + .getBody() + .asString(); + result = result.substring(8 + result.indexOf("action=\"")); + result = result.substring(0, result.indexOf("\"")); - private String callTrickHtml(String htmlName) { - String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/files/" + this.getUser() + "/" + htmlName)) - .then() - .extract().response().getBody().asString(); - result = result.substring(8 + result.indexOf("action=\"")); - result = result.substring(0, result.indexOf("\"")); + return result; + } - return result; - } - - private void checkAssignment3(String goatURL) { - String flag = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("/files/fake.html")) - .post(goatURL) - .then() - .extract().path("flag").toString(); - - Map params = new HashMap<>(); - params.clear(); - params.put("confirmFlagVal", flag); - checkAssignment(url("/WebGoat/csrf/confirm-flag-1"), params, true); - } - - private void checkAssignment4(String goatURL) { - - Map params = new HashMap<>(); - params.clear(); - params.put("reviewText", "test review"); - params.put("stars", "5"); - params.put("validateReq", "2aa14227b9a13d0bede0388a7fba9aa9");//always the same token is the weakness - - boolean result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("/files/fake.html")) - .formParams(params) - .post(goatURL) - .then() - .extract().path("lessonCompleted"); - assertEquals(true, result); - - } - - private void checkAssignment7(String goatURL) { - Map params = new HashMap<>(); - params.put("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!", "\"}"); - - String flag = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("/files/fake.html")) - .contentType(ContentType.TEXT) - .body("{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the best!!" + "=\"}") - .post(goatURL) - .then() - .extract().asString(); - flag = flag.substring(9 + flag.indexOf("flag is:")); - flag = flag.substring(0, flag.indexOf("\"")); - - params.clear(); - params.put("confirmFlagVal", flag); - checkAssignment(url("/WebGoat/csrf/feedback"), params, true); - - } - - private void checkAssignment8(String goatURL) { - - //first make sure there is an attack csrf- user - registerCSRFUser(); - - Map params = new HashMap<>(); - params.clear(); - params.put("username", "csrf-" + this.getUser()); - params.put("password", "password"); - - //login and get the new cookie - String newCookie = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("Referer", webWolfUrl("/files/fake.html")) - .params(params) - .post(goatURL) - .then() - .extract().cookie("JSESSIONID"); - - //select the lesson + private void checkAssignment3(String goatURL) { + String flag = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", newCookie) - .get(url("CSRF.lesson.lesson")) - .then() - .statusCode(200); + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("Referer", webWolfUrl("/files/fake.html")) + .post(goatURL) + .then() + .extract() + .path("flag") + .toString(); - //click on the assignment - boolean result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", newCookie) - .post(url("/csrf/login")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"); + Map params = new HashMap<>(); + params.clear(); + params.put("confirmFlagVal", flag); + checkAssignment(url("/WebGoat/csrf/confirm-flag-1"), params, true); + } - assertThat(result).isTrue(); + private void checkAssignment4(String goatURL) { - login(); - startLesson("CSRF", false); - - Overview[] assignments = RestAssured.given() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/service/lessonoverview.mvc")) - .then() - .extract() - .jsonPath() - .getObject("$", Overview[].class); -// assertThat(assignments) -// .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) -// .extracting(o -> o.solved) -// .containsExactly(true); - } - - @Data - private static class Overview { - Assignment assignment; - boolean solved; - } - - /** - * Try to register the new user. Ignore the result. - */ - private void registerCSRFUser() { + Map params = new HashMap<>(); + params.clear(); + params.put("reviewText", "test review"); + params.put("stars", "5"); + params.put( + "validateReq", "2aa14227b9a13d0bede0388a7fba9aa9"); // always the same token is the weakness + boolean result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .formParam("username", "csrf-" + this.getUser()) - .formParam("password", "password") - .formParam("matchingPassword", "password") - .formParam("agree", "agree") - .post(url("register.mvc")); + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("Referer", webWolfUrl("/files/fake.html")) + .formParams(params) + .post(goatURL) + .then() + .extract() + .path("lessonCompleted"); + assertEquals(true, result); + } - } + private void checkAssignment7(String goatURL) { + Map params = new HashMap<>(); + params.put( + "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is the" + + " best!!", + "\"}"); + String flag = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("Referer", webWolfUrl("/files/fake.html")) + .contentType(ContentType.TEXT) + .body( + "{\"name\":\"WebGoat\",\"email\":\"webgoat@webgoat.org\",\"content\":\"WebGoat is" + + " the best!!=\"}") + .post(goatURL) + .then() + .extract() + .asString(); + flag = flag.substring(9 + flag.indexOf("flag is:")); + flag = flag.substring(0, flag.indexOf("\"")); + + params.clear(); + params.put("confirmFlagVal", flag); + checkAssignment(url("/WebGoat/csrf/feedback"), params, true); + } + + private void checkAssignment8(String goatURL) { + + // first make sure there is an attack csrf- user + registerCSRFUser(); + + Map params = new HashMap<>(); + params.clear(); + params.put("username", "csrf-" + this.getUser()); + params.put("password", "password"); + + // login and get the new cookie + String newCookie = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("Referer", webWolfUrl("/files/fake.html")) + .params(params) + .post(goatURL) + .then() + .extract() + .cookie("JSESSIONID"); + + // select the lesson + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", newCookie) + .get(url("CSRF.lesson.lesson")) + .then() + .statusCode(200); + + // click on the assignment + boolean result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", newCookie) + .post(url("/csrf/login")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"); + + assertThat(result).isTrue(); + + login(); + startLesson("CSRF", false); + + Overview[] assignments = + RestAssured.given() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/service/lessonoverview.mvc")) + .then() + .extract() + .jsonPath() + .getObject("$", Overview[].class); + // assertThat(assignments) + // .filteredOn(a -> a.getAssignment().getName().equals("CSRFLogin")) + // .extracting(o -> o.solved) + // .containsExactly(true); + } + + @Data + private static class Overview { + Assignment assignment; + boolean solved; + } + + /** Try to register the new user. Ignore the result. */ + private void registerCSRFUser() { + + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .formParam("username", "csrf-" + this.getUser()) + .formParam("password", "password") + .formParam("matchingPassword", "password") + .formParam("agree", "agree") + .post(url("register.mvc")); + } } diff --git a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java index cc9cd397d..133721eee 100644 --- a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java @@ -1,7 +1,5 @@ package org.owasp.webgoat; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.assertTrue; import io.restassured.RestAssured; diff --git a/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java index 21caef469..ad9d6f625 100644 --- a/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/CryptoIntegrationTest.java @@ -2,6 +2,7 @@ package org.owasp.webgoat; import static org.junit.jupiter.api.Assertions.fail; +import io.restassured.RestAssured; import java.nio.charset.Charset; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -10,125 +11,146 @@ import java.security.spec.InvalidKeySpecException; import java.util.Base64; import java.util.HashMap; import java.util.Map; - import javax.xml.bind.DatatypeConverter; - import org.junit.jupiter.api.Test; import org.owasp.webgoat.lessons.cryptography.CryptoUtil; import org.owasp.webgoat.lessons.cryptography.HashingAssignment; -import io.restassured.RestAssured; - public class CryptoIntegrationTest extends IntegrationTest { - @Test - public void runTests() { - startLesson("Cryptography"); + @Test + public void runTests() { + startLesson("Cryptography"); - checkAssignment2(); - checkAssignment3(); + checkAssignment2(); + checkAssignment3(); - // Assignment 4 - try { - checkAssignment4(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - fail(); - } - - try { - checkAssignmentSigning(); - } catch (Exception e) { - e.printStackTrace(); - fail(); - } - - checkAssignmentDefaults(); - - checkResults("/crypto"); - - } - - private void checkAssignment2() { - - String basicEncoding = RestAssured.given().when().relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()).get(url("/crypto/encoding/basic")).then().extract() - .asString(); - basicEncoding = basicEncoding.substring("Authorization: Basic ".length()); - String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes())); - String answer_user = decodedString.split(":")[0]; - String answer_pwd = decodedString.split(":")[1]; - Map params = new HashMap<>(); - params.clear(); - params.put("answer_user", answer_user); - params.put("answer_pwd", answer_pwd); - checkAssignment(url("/crypto/encoding/basic-auth"), params, true); - } - - private void checkAssignment3() { - String answer_1 = "databasepassword"; - Map params = new HashMap<>(); - params.clear(); - params.put("answer_pwd1", answer_1); - checkAssignment(url("/crypto/encoding/xor"), params, true); - } - - private void checkAssignment4() throws NoSuchAlgorithmException { - - String md5Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/crypto/hashing/md5")).then().extract().asString(); - - String sha256Hash = RestAssured.given().when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/crypto/hashing/sha256")).then().extract().asString(); - - String answer_1 = "unknown"; - String answer_2 = "unknown"; - for (String secret : HashingAssignment.SECRETS) { - if (md5Hash.equals(HashingAssignment.getHash(secret, "MD5"))) { - answer_1 = secret; - } - if (sha256Hash.equals(HashingAssignment.getHash(secret, "SHA-256"))) { - answer_2 = secret; - } - } - - Map params = new HashMap<>(); - params.clear(); - params.put("answer_pwd1", answer_1); - params.put("answer_pwd2", answer_2); - checkAssignment(url("/WebGoat/crypto/hashing"), params, true); - } - - private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException { - - String privatePEM = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/crypto/signing/getprivate")) - .then() - .extract().asString(); - PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM); - - RSAPrivateKey privk = (RSAPrivateKey) privateKey; - String modulus = DatatypeConverter.printHexBinary(privk.getModulus().toByteArray()); - String signature = CryptoUtil.signMessage(modulus, privateKey); - Map params = new HashMap<>(); - params.clear(); - params.put("modulus", modulus); - params.put("signature", signature); - checkAssignment(url("/crypto/signing/verify"), params, true); + // Assignment 4 + try { + checkAssignment4(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + fail(); } - - private void checkAssignmentDefaults() { - - String text = new String(Base64.getDecoder().decode("TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl".getBytes(Charset.forName("UTF-8")))); - Map params = new HashMap<>(); - params.clear(); - params.put("secretText", text); - params.put("secretFileName", "default_secret"); - checkAssignment(url("/crypto/secure/defaults"), params, true); + try { + checkAssignmentSigning(); + } catch (Exception e) { + e.printStackTrace(); + fail(); } - + + checkAssignmentDefaults(); + + checkResults("/crypto"); + } + + private void checkAssignment2() { + + String basicEncoding = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/crypto/encoding/basic")) + .then() + .extract() + .asString(); + basicEncoding = basicEncoding.substring("Authorization: Basic ".length()); + String decodedString = new String(Base64.getDecoder().decode(basicEncoding.getBytes())); + String answer_user = decodedString.split(":")[0]; + String answer_pwd = decodedString.split(":")[1]; + Map params = new HashMap<>(); + params.clear(); + params.put("answer_user", answer_user); + params.put("answer_pwd", answer_pwd); + checkAssignment(url("/crypto/encoding/basic-auth"), params, true); + } + + private void checkAssignment3() { + String answer_1 = "databasepassword"; + Map params = new HashMap<>(); + params.clear(); + params.put("answer_pwd1", answer_1); + checkAssignment(url("/crypto/encoding/xor"), params, true); + } + + private void checkAssignment4() throws NoSuchAlgorithmException { + + String md5Hash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/crypto/hashing/md5")) + .then() + .extract() + .asString(); + + String sha256Hash = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/crypto/hashing/sha256")) + .then() + .extract() + .asString(); + + String answer_1 = "unknown"; + String answer_2 = "unknown"; + for (String secret : HashingAssignment.SECRETS) { + if (md5Hash.equals(HashingAssignment.getHash(secret, "MD5"))) { + answer_1 = secret; + } + if (sha256Hash.equals(HashingAssignment.getHash(secret, "SHA-256"))) { + answer_2 = secret; + } + } + + Map params = new HashMap<>(); + params.clear(); + params.put("answer_pwd1", answer_1); + params.put("answer_pwd2", answer_2); + checkAssignment(url("/WebGoat/crypto/hashing"), params, true); + } + + private void checkAssignmentSigning() throws NoSuchAlgorithmException, InvalidKeySpecException { + + String privatePEM = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/crypto/signing/getprivate")) + .then() + .extract() + .asString(); + PrivateKey privateKey = CryptoUtil.getPrivateKeyFromPEM(privatePEM); + + RSAPrivateKey privk = (RSAPrivateKey) privateKey; + String modulus = DatatypeConverter.printHexBinary(privk.getModulus().toByteArray()); + String signature = CryptoUtil.signMessage(modulus, privateKey); + Map params = new HashMap<>(); + params.clear(); + params.put("modulus", modulus); + params.put("signature", signature); + checkAssignment(url("/crypto/signing/verify"), params, true); + } + + private void checkAssignmentDefaults() { + + String text = + new String( + Base64.getDecoder() + .decode( + "TGVhdmluZyBwYXNzd29yZHMgaW4gZG9ja2VyIGltYWdlcyBpcyBub3Qgc28gc2VjdXJl" + .getBytes(Charset.forName("UTF-8")))); + + Map params = new HashMap<>(); + params.clear(); + params.put("secretText", text); + params.put("secretFileName", "default_secret"); + checkAssignment(url("/crypto/secure/defaults"), params, true); + } } diff --git a/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java index 496d6cfa8..29572be45 100644 --- a/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/DeserializationIntegrationTest.java @@ -1,34 +1,33 @@ package org.owasp.webgoat; -import org.dummy.insecure.framework.VulnerableTaskHolder; -import org.junit.jupiter.api.Test; -import org.owasp.webgoat.lessons.deserialization.SerializationHelper; - import java.io.IOException; import java.util.HashMap; import java.util.Map; +import org.dummy.insecure.framework.VulnerableTaskHolder; +import org.junit.jupiter.api.Test; +import org.owasp.webgoat.lessons.deserialization.SerializationHelper; public class DeserializationIntegrationTest extends IntegrationTest { - private static String OS = System.getProperty("os.name").toLowerCase(); + private static String OS = System.getProperty("os.name").toLowerCase(); - @Test - public void runTests() throws IOException { - startLesson("InsecureDeserialization"); + @Test + public void runTests() throws IOException { + startLesson("InsecureDeserialization"); - Map params = new HashMap<>(); - params.clear(); - - if (OS.indexOf("win") > -1) { - params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5"))); - } else { - params.put("token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5"))); - } - checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true); - - checkResults("/InsecureDeserialization/"); + Map params = new HashMap<>(); + params.clear(); + if (OS.indexOf("win") > -1) { + params.put( + "token", + SerializationHelper.toString(new VulnerableTaskHolder("wait", "ping localhost -n 5"))); + } else { + params.put( + "token", SerializationHelper.toString(new VulnerableTaskHolder("wait", "sleep 5"))); } + checkAssignment(url("/WebGoat/InsecureDeserialization/task"), params, true); - + checkResults("/InsecureDeserialization/"); + } } diff --git a/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java index 8522681a5..274ce639c 100644 --- a/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/GeneralLessonIntegrationTest.java @@ -61,7 +61,8 @@ public class GeneralLessonIntegrationTest extends IntegrationTest { params.clear(); params.put( "question_0_solution", - "Solution 3: By stealing a database where names and emails are stored and uploading it to a website."); + "Solution 3: By stealing a database where names and emails are stored and uploading it to a" + + " website."); params.put( "question_1_solution", "Solution 1: By changing the names and emails of one or more users stored in a database."); diff --git a/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java index 56308d92d..dca27650b 100644 --- a/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IDORIntegrationTest.java @@ -1,13 +1,14 @@ package org.owasp.webgoat; - import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; - +import lombok.SneakyThrows; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; @@ -15,84 +16,81 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import lombok.SneakyThrows; - public class IDORIntegrationTest extends IntegrationTest { - - @BeforeEach - @SneakyThrows - public void init() { - startLesson("IDOR"); - } - @TestFactory - Iterable testIDORLesson() { - return Arrays.asList( - dynamicTest("login",()-> loginIDOR()), - dynamicTest("profile", () -> profile()) - ); - } - - @AfterEach - public void shutdown() throws IOException { - checkResults("/IDOR"); - } - - private void loginIDOR() throws IOException { - - Map params = new HashMap<>(); - params.clear(); - params.put("username", "tom"); - params.put("password", "cat"); - - - checkAssignment(url("/WebGoat/IDOR/login"), params, true); - - } - - private void profile() { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/IDOR/profile")) - .then() - .statusCode(200) - .extract().path("userId"), CoreMatchers.is("2342384")); - Map params = new HashMap<>(); - params.clear(); - params.put("attributes", "userId,role"); - checkAssignment(url("/WebGoat/IDOR/diff-attributes"), params, true); - params.clear(); - params.put("url", "WebGoat/IDOR/profile/2342384"); - checkAssignment(url("/WebGoat/IDOR/profile/alt-path"), params, true); - - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/IDOR/profile/2342388")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) //part of the lesson - .body("{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\", \"userId\":\"2342388\"}") - .put(url("/WebGoat/IDOR/profile/2342388")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - - - } - + @BeforeEach + @SneakyThrows + public void init() { + startLesson("IDOR"); + } + + @TestFactory + Iterable testIDORLesson() { + return Arrays.asList( + dynamicTest("login", () -> loginIDOR()), dynamicTest("profile", () -> profile())); + } + + @AfterEach + public void shutdown() throws IOException { + checkResults("/IDOR"); + } + + private void loginIDOR() throws IOException { + + Map params = new HashMap<>(); + params.clear(); + params.put("username", "tom"); + params.put("password", "cat"); + + checkAssignment(url("/WebGoat/IDOR/login"), params, true); + } + + private void profile() { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/IDOR/profile")) + .then() + .statusCode(200) + .extract() + .path("userId"), + CoreMatchers.is("2342384")); + Map params = new HashMap<>(); + params.clear(); + params.put("attributes", "userId,role"); + checkAssignment(url("/WebGoat/IDOR/diff-attributes"), params, true); + params.clear(); + params.put("url", "WebGoat/IDOR/profile/2342384"); + checkAssignment(url("/WebGoat/IDOR/profile/alt-path"), params, true); + + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/IDOR/profile/2342388")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) // part of the lesson + .body( + "{\"role\":\"1\", \"color\":\"red\", \"size\":\"large\", \"name\":\"Buffalo Bill\"," + + " \"userId\":\"2342388\"}") + .put(url("/WebGoat/IDOR/profile/2342388")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } } diff --git a/src/it/java/org/owasp/webgoat/IntegrationTest.java b/src/it/java/org/owasp/webgoat/IntegrationTest.java index c3fb3d9ff..db551afeb 100644 --- a/src/it/java/org/owasp/webgoat/IntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IntegrationTest.java @@ -1,231 +1,255 @@ package org.owasp.webgoat; +import static io.restassured.RestAssured.given; + import io.restassured.RestAssured; import io.restassured.http.ContentType; +import java.util.Map; +import java.util.Objects; import lombok.Getter; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import java.util.Map; -import java.util.Objects; - -import static io.restassured.RestAssured.given; - public abstract class IntegrationTest { - private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport")); - @Getter - private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport")); - private static boolean useSSL = false; - private static String webgoatUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/"; - private static String webWolfUrl = (useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/"; - @Getter - private String webGoatCookie; - @Getter - private String webWolfCookie; - @Getter - private final String user = "webgoat"; + private static String webGoatPort = Objects.requireNonNull(System.getProperty("webgoatport")); - protected String url(String url) { - url = url.replaceFirst("/WebGoat/", ""); - url = url.replaceFirst("/WebGoat", ""); - url = url.startsWith("/") ? url.replaceFirst("/", "") : url; - return webgoatUrl + url; + @Getter + private static String webWolfPort = Objects.requireNonNull(System.getProperty("webwolfport")); + + private static boolean useSSL = false; + private static String webgoatUrl = + (useSSL ? "https:" : "http:") + "//localhost:" + webGoatPort + "/WebGoat/"; + private static String webWolfUrl = + (useSSL ? "https:" : "http:") + "//localhost:" + webWolfPort + "/"; + @Getter private String webGoatCookie; + @Getter private String webWolfCookie; + @Getter private final String user = "webgoat"; + + protected String url(String url) { + url = url.replaceFirst("/WebGoat/", ""); + url = url.replaceFirst("/WebGoat", ""); + url = url.startsWith("/") ? url.replaceFirst("/", "") : url; + return webgoatUrl + url; + } + + protected String webWolfUrl(String url) { + url = url.replaceFirst("/WebWolf/", ""); + url = url.replaceFirst("/WebWolf", ""); + url = url.startsWith("/") ? url.replaceFirst("/", "") : url; + return webWolfUrl + url; + } + + @BeforeEach + public void login() { + String location = + given() + .when() + .relaxedHTTPSValidation() + .formParam("username", user) + .formParam("password", "password") + .post(url("login")) + .then() + .cookie("JSESSIONID") + .statusCode(302) + .extract() + .header("Location"); + if (location.endsWith("?error")) { + webGoatCookie = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .formParam("username", user) + .formParam("password", "password") + .formParam("matchingPassword", "password") + .formParam("agree", "agree") + .post(url("register.mvc")) + .then() + .cookie("JSESSIONID") + .statusCode(302) + .extract() + .cookie("JSESSIONID"); + } else { + webGoatCookie = + given() + .when() + .relaxedHTTPSValidation() + .formParam("username", user) + .formParam("password", "password") + .post(url("login")) + .then() + .cookie("JSESSIONID") + .statusCode(302) + .extract() + .cookie("JSESSIONID"); } - protected String webWolfUrl(String url) { - url = url.replaceFirst("/WebWolf/", ""); - url = url.replaceFirst("/WebWolf", ""); - url = url.startsWith("/") ? url.replaceFirst("/", "") : url; - return webWolfUrl + url; - } - - @BeforeEach - public void login() { - String location = given() - .when() - .relaxedHTTPSValidation() - .formParam("username", user) - .formParam("password", "password") - .post(url("login")).then() - .cookie("JSESSIONID") - .statusCode(302) - .extract().header("Location"); - if (location.endsWith("?error")) { - webGoatCookie = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .formParam("username", user) - .formParam("password", "password") - .formParam("matchingPassword", "password") - .formParam("agree", "agree") - .post(url("register.mvc")) - .then() - .cookie("JSESSIONID") - .statusCode(302) - .extract() - .cookie("JSESSIONID"); - } else { - webGoatCookie = given() - .when() - .relaxedHTTPSValidation() - .formParam("username", user) - .formParam("password", "password") - .post(url("login")).then() - .cookie("JSESSIONID") - .statusCode(302) - .extract().cookie("JSESSIONID"); - } - - webWolfCookie = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .formParam("username", user) - .formParam("password", "password") - .post(webWolfUrl("login")) - .then() - .statusCode(302) - .cookie("WEBWOLFSESSION") - .extract() - .cookie("WEBWOLFSESSION"); - } - - @AfterEach - public void logout() { + webWolfCookie = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .get(url("logout")) - .then() - .statusCode(200); - } + .when() + .relaxedHTTPSValidation() + .formParam("username", user) + .formParam("password", "password") + .post(webWolfUrl("login")) + .then() + .statusCode(302) + .cookie("WEBWOLFSESSION") + .extract() + .cookie("WEBWOLFSESSION"); + } - public void startLesson(String lessonName) { - startLesson(lessonName, false); - } + @AfterEach + public void logout() { + RestAssured.given().when().relaxedHTTPSValidation().get(url("logout")).then().statusCode(200); + } - public void startLesson(String lessonName, boolean restart) { + public void startLesson(String lessonName) { + startLesson(lessonName, false); + } + + public void startLesson(String lessonName, boolean restart) { + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url(lessonName + ".lesson.lesson")) + .then() + .statusCode(200); + + if (restart) { + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/restartlesson.mvc")) + .then() + .statusCode(200); + } + } + + public void checkAssignment(String url, Map params, boolean expectedResult) { + MatcherAssert.assertThat( RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url(lessonName + ".lesson.lesson")) - .then() - .statusCode(200); + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .post(url) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(expectedResult)); + } - if (restart) { - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/restartlesson.mvc")) - .then() - .statusCode(200); - } - } + public void checkAssignmentWithPUT(String url, Map params, boolean expectedResult) { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(params) + .put(url) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(expectedResult)); + } - public void checkAssignment(String url, Map params, boolean expectedResult) { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams(params) - .post(url) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); - } + // TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are + // only required to be in the same package) + public void checkResults(String prefix) { + checkResults(); - public void checkAssignmentWithPUT(String url, Map params, boolean expectedResult) { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams(params) - .put(url) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); - } + MatcherAssert.assertThat( + 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))); + } - //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(); + public void checkResults() { + var result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/lessonoverview.mvc")) + .andReturn(); - MatcherAssert.assertThat(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))); + MatcherAssert.assertThat( + result.then().statusCode(200).extract().jsonPath().getList("solved"), + CoreMatchers.everyItem(CoreMatchers.is(true))); + } - } + public void checkAssignment( + String url, ContentType contentType, String body, boolean expectedResult) { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(contentType) + .cookie("JSESSIONID", getWebGoatCookie()) + .body(body) + .post(url) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(expectedResult)); + } - public void checkResults() { - var result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/lessonoverview.mvc")) - .andReturn(); + public void checkAssignmentWithGet(String url, Map params, boolean expectedResult) { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .queryParams(params) + .get(url) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(expectedResult)); + } - MatcherAssert.assertThat(result.then() - .statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true))); - } - - public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(contentType) - .cookie("JSESSIONID", getWebGoatCookie()) - .body(body) - .post(url) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); - } - - public void checkAssignmentWithGet(String url, Map params, boolean expectedResult) { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .queryParams(params) - .get(url) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); - } - - public String getWebWolfFileServerLocation() { - String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/file-server-location")) - .then() - .extract().response().getBody().asString(); - result = result.replace("%20", " "); - return result; - } - - public String webGoatServerDirectory() { - return RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/server-directory")) - .then() - .extract().response().getBody().asString(); - } + public String getWebWolfFileServerLocation() { + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/file-server-location")) + .then() + .extract() + .response() + .getBody() + .asString(); + result = result.replace("%20", " "); + return result; + } + public String webGoatServerDirectory() { + return RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/server-directory")) + .then() + .extract() + .response() + .getBody() + .asString(); + } } - diff --git a/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java index 536eec117..58b981470 100644 --- a/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/JWTLessonIntegrationTest.java @@ -1,5 +1,16 @@ package org.owasp.webgoat; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.impl.TextCodec; +import io.restassured.RestAssured; import java.io.IOException; import java.nio.charset.Charset; import java.security.InvalidKeyException; @@ -10,207 +21,230 @@ import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Map; - import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import io.jsonwebtoken.Header; -import io.jsonwebtoken.JwsHeader; -import io.jsonwebtoken.Jwt; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.impl.TextCodec; -import io.restassured.RestAssured; import org.owasp.webgoat.lessons.jwt.JWTSecretKeyEndpoint; public class JWTLessonIntegrationTest extends IntegrationTest { - - @Test - public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { - startLesson("JWT"); - decodingToken(); - - resetVotes(); - - findPassword(); - - buyAsTom(); - - deleteTom(); + @Test + public void solveAssignment() throws IOException, InvalidKeyException, NoSuchAlgorithmException { + startLesson("JWT"); - quiz(); - - checkResults("/JWT/"); - } - - private String generateToken(String key) { - - return Jwts.builder() - .setIssuer("WebGoat Token Builder") - .setAudience("webgoat.org") - .setIssuedAt(Calendar.getInstance().getTime()) - .setExpiration(Date.from(Instant.now().plusSeconds(60))) - .setSubject("tom@webgoat.org") - .claim("username", "WebGoat") - .claim("Email", "tom@webgoat.org") - .claim("Role", new String[] {"Manager", "Project Administrator"}) - .signWith(SignatureAlgorithm.HS256, key).compact(); - } - - private String getSecretToken(String token) { - for (String key : JWTSecretKeyEndpoint.SECRETS) { - try { - Jwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token); - } catch (JwtException e) { - continue; - } - return TextCodec.BASE64.encode(key); - } - return null; + decodingToken(); + + resetVotes(); + + findPassword(); + + buyAsTom(); + + deleteTom(); + + quiz(); + + checkResults("/JWT/"); + } + + private String generateToken(String key) { + + return Jwts.builder() + .setIssuer("WebGoat Token Builder") + .setAudience("webgoat.org") + .setIssuedAt(Calendar.getInstance().getTime()) + .setExpiration(Date.from(Instant.now().plusSeconds(60))) + .setSubject("tom@webgoat.org") + .claim("username", "WebGoat") + .claim("Email", "tom@webgoat.org") + .claim("Role", new String[] {"Manager", "Project Administrator"}) + .signWith(SignatureAlgorithm.HS256, key) + .compact(); + } + + private String getSecretToken(String token) { + for (String key : JWTSecretKeyEndpoint.SECRETS) { + try { + Jwt jwt = Jwts.parser().setSigningKey(TextCodec.BASE64.encode(key)).parse(token); + } catch (JwtException e) { + continue; + } + return TextCodec.BASE64.encode(key); } + return null; + } - private void decodingToken() { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParam("jwt-encode-user", "user") - .post(url("/WebGoat/JWT/decode")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); + private void decodingToken() { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParam("jwt-encode-user", "user") + .post(url("/WebGoat/JWT/decode")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } - } - - private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { - - String accessToken = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/JWT/secret/gettoken")) - .then() - .extract().response().asString(); - - String secret = getSecretToken(accessToken); - - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParam("token", generateToken(secret)) - .post(url("/WebGoat/JWT/secret")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - - } - - private void resetVotes() throws IOException { - String accessToken = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("/WebGoat/JWT/votings/login?user=Tom")) - .then() - .extract().cookie("access_token"); + private void findPassword() throws IOException, NoSuchAlgorithmException, InvalidKeyException { - String header = accessToken.substring(0, accessToken.indexOf(".")); - header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset()))); - - String body = accessToken.substring(1+accessToken.indexOf("."), accessToken.lastIndexOf(".")); - body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset()))); + String accessToken = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/JWT/secret/gettoken")) + .then() + .extract() + .response() + .asString(); - ObjectMapper mapper = new ObjectMapper(); - JsonNode headerNode = mapper.readTree(header); - headerNode = ((ObjectNode) headerNode).put("alg","NONE"); + String secret = getSecretToken(accessToken); - JsonNode bodyObject = mapper.readTree(body); - bodyObject = ((ObjectNode) bodyObject).put("admin","true"); - - String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())) - .concat(".") - .concat(new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())).toString()) - .concat(".").replace("=", ""); - - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .cookie("access_token", replacedToken) - .post(url("/WebGoat/JWT/votings")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - private void buyAsTom() throws IOException { - - String header = new String(Base64.getUrlDecoder().decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset()))); + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParam("token", generateToken(secret)) + .post(url("/WebGoat/JWT/secret")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } - String body = new String(Base64.getUrlDecoder().decode("eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0".getBytes(Charset.defaultCharset()))); + private void resetVotes() throws IOException { + String accessToken = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/JWT/votings/login?user=Tom")) + .then() + .extract() + .cookie("access_token"); - body = body.replace("Jerry", "Tom"); - - ObjectMapper mapper = new ObjectMapper(); - JsonNode headerNode = mapper.readTree(header); - headerNode = ((ObjectNode) headerNode).put("alg", "NONE"); + String header = accessToken.substring(0, accessToken.indexOf(".")); + header = new String(Base64.getUrlDecoder().decode(header.getBytes(Charset.defaultCharset()))); - String replacedToken = new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())).concat(".") - .concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString()) - .concat(".").replace("=", ""); + String body = accessToken.substring(1 + accessToken.indexOf("."), accessToken.lastIndexOf(".")); + body = new String(Base64.getUrlDecoder().decode(body.getBytes(Charset.defaultCharset()))); - MatcherAssert.assertThat(RestAssured.given() - .when().relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("Authorization","Bearer "+replacedToken) - .post(url("/WebGoat/JWT/refresh/checkout")) - .then().statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - private void deleteTom() { - - Map header = new HashMap(); - header.put(Header.TYPE, Header.JWT_TYPE); - header.put(JwsHeader.KEY_ID, "hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --"); - String token = Jwts.builder() - .setHeader(header) - .setIssuer("WebGoat Token Builder") - .setAudience("webgoat.org") - .setIssuedAt(Calendar.getInstance().getTime()) - .setExpiration(Date.from(Instant.now().plusSeconds(60))) - .setSubject("tom@webgoat.org") - .claim("username", "Tom") - .claim("Email", "tom@webgoat.org") - .claim("Role", new String[] {"Manager", "Project Administrator"}) - .signWith(SignatureAlgorithm.HS256, "deletingTom").compact(); - - MatcherAssert.assertThat(RestAssured.given() - .when().relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .post(url("/WebGoat/JWT/final/delete?token="+token)) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } + ObjectMapper mapper = new ObjectMapper(); + JsonNode headerNode = mapper.readTree(header); + headerNode = ((ObjectNode) headerNode).put("alg", "NONE"); - private void quiz() { - Map params = new HashMap<>(); - params.put("question_0_solution", "Solution 1"); - params.put("question_1_solution", "Solution 2"); + JsonNode bodyObject = mapper.readTree(body); + bodyObject = ((ObjectNode) bodyObject).put("admin", "true"); - checkAssignment(url("/WebGoat/JWT/quiz"), params, true); - } - + String replacedToken = + new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())) + .concat(".") + .concat( + new String(Base64.getUrlEncoder().encode(bodyObject.toString().getBytes())) + .toString()) + .concat(".") + .replace("=", ""); + + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .cookie("access_token", replacedToken) + .post(url("/WebGoat/JWT/votings")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void buyAsTom() throws IOException { + + String header = + new String( + Base64.getUrlDecoder() + .decode("eyJhbGciOiJIUzUxMiJ9".getBytes(Charset.defaultCharset()))); + + String body = + new String( + Base64.getUrlDecoder() + .decode( + "eyJhZG1pbiI6ImZhbHNlIiwidXNlciI6IkplcnJ5In0" + .getBytes(Charset.defaultCharset()))); + + body = body.replace("Jerry", "Tom"); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode headerNode = mapper.readTree(header); + headerNode = ((ObjectNode) headerNode).put("alg", "NONE"); + + String replacedToken = + new String(Base64.getUrlEncoder().encode(headerNode.toString().getBytes())) + .concat(".") + .concat(new String(Base64.getUrlEncoder().encode(body.getBytes())).toString()) + .concat(".") + .replace("=", ""); + + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("Authorization", "Bearer " + replacedToken) + .post(url("/WebGoat/JWT/refresh/checkout")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void deleteTom() { + + Map header = new HashMap(); + header.put(Header.TYPE, Header.JWT_TYPE); + header.put( + JwsHeader.KEY_ID, + "hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS --"); + String token = + Jwts.builder() + .setHeader(header) + .setIssuer("WebGoat Token Builder") + .setAudience("webgoat.org") + .setIssuedAt(Calendar.getInstance().getTime()) + .setExpiration(Date.from(Instant.now().plusSeconds(60))) + .setSubject("tom@webgoat.org") + .claim("username", "Tom") + .claim("Email", "tom@webgoat.org") + .claim("Role", new String[] {"Manager", "Project Administrator"}) + .signWith(SignatureAlgorithm.HS256, "deletingTom") + .compact(); + + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .post(url("/WebGoat/JWT/final/delete?token=" + token)) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void quiz() { + Map params = new HashMap<>(); + params.put("question_0_solution", "Solution 1"); + params.put("question_1_solution", "Solution 2"); + + checkAssignment(url("/WebGoat/JWT/quiz"), params, true); + } } diff --git a/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java index 1f99c15e7..ae6feb803 100644 --- a/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/LabelAndHintIntegrationTest.java @@ -3,168 +3,228 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import java.io.FileInputStream; import java.io.InputStream; import java.util.List; import java.util.Properties; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class LabelAndHintIntegrationTest extends IntegrationTest { - final static String ESCAPE_JSON_PATH_CHAR = "\'"; + static final String ESCAPE_JSON_PATH_CHAR = "\'"; - @Test - public void testSingleLabel() { - Assertions.assertTrue(true); - JsonPath jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language","en") - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath(); + @Test + public void testSingleLabel() { + Assertions.assertTrue(true); + JsonPath jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", "en") + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/labels.mvc")) + .then() + .statusCode(200) + .extract() + .jsonPath(); - Assertions.assertEquals("Try again: but this time enter a value before hitting go.", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"http-basics.close"+ESCAPE_JSON_PATH_CHAR)); + Assertions.assertEquals( + "Try again: but this time enter a value before hitting go.", + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "http-basics.close" + ESCAPE_JSON_PATH_CHAR)); - // check if lang parameter overrules Accept-Language parameter - jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language","en") - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/labels.mvc?lang=nl")).then().statusCode(200).extract().jsonPath(); - Assertions.assertEquals("Gebruikersnaam", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); + // check if lang parameter overrules Accept-Language parameter + jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", "en") + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/labels.mvc?lang=nl")) + .then() + .statusCode(200) + .extract() + .jsonPath(); + Assertions.assertEquals( + "Gebruikersnaam", + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); - jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language","en") - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/labels.mvc?lang=de")).then().statusCode(200).extract().jsonPath(); - Assertions.assertEquals("Benutzername", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); + jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", "en") + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/labels.mvc?lang=de")) + .then() + .statusCode(200) + .extract() + .jsonPath(); + Assertions.assertEquals( + "Benutzername", + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); - // check if invalid language returns english - jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language","nl") - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/labels.mvc?lang=xx")).then().statusCode(200).extract().jsonPath(); - Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); + // check if invalid language returns english + jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", "nl") + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/labels.mvc?lang=xx")) + .then() + .statusCode(200) + .extract() + .jsonPath(); + Assertions.assertEquals( + "Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); - // check if invalid language returns english - jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language","xx_YY") - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/labels.mvc")).then().statusCode(200).extract().jsonPath(); - Assertions.assertEquals("Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR+"username"+ESCAPE_JSON_PATH_CHAR)); + // check if invalid language returns english + jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", "xx_YY") + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/labels.mvc")) + .then() + .statusCode(200) + .extract() + .jsonPath(); + Assertions.assertEquals( + "Username", jsonPath.getString(ESCAPE_JSON_PATH_CHAR + "username" + ESCAPE_JSON_PATH_CHAR)); + } + @Test + public void testHints() { + JsonPath jsonPathLabels = getLabels("en"); + List allLessons = + List.of( + "HttpBasics", + "HttpProxies", + "CIA", + "InsecureLogin", + "Cryptography", + "PathTraversal", + "XXE", + "JWT", + "IDOR", + "SSRF", + "WebWolfIntroduction", + "CrossSiteScripting", + "CSRF", + "HijackSession", + "SqlInjection", + "SqlInjectionMitigations", + "SqlInjectionAdvanced", + "Challenge1"); + for (String lesson : allLessons) { + startLesson(lesson); + List hintKeys = getHints(); + for (String key : hintKeys) { + String keyValue = + jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR); + // System.out.println("key: " + key + " ,value: " + keyValue); + Assertions.assertNotNull(keyValue); + Assertions.assertNotEquals(key, keyValue); + } } + // Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", + // ""+jsonPath.getList("hint").get(0)); + } - @Test - public void testHints() { - JsonPath jsonPathLabels = getLabels("en"); - List allLessons = List.of( - "HttpBasics", - "HttpProxies", "CIA", "InsecureLogin", "Cryptography", "PathTraversal", - "XXE", "JWT", "IDOR", "SSRF", "WebWolfIntroduction", "CrossSiteScripting", "CSRF", "HijackSession", - "SqlInjection", "SqlInjectionMitigations" ,"SqlInjectionAdvanced", - "Challenge1"); - for (String lesson: allLessons) { - startLesson(lesson); - List hintKeys = getHints(); - for (String key : hintKeys) { - String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR); - //System.out.println("key: " + key + " ,value: " + keyValue); - Assertions.assertNotNull(keyValue); - Assertions.assertNotEquals(key, keyValue); - } - } - //Assertions.assertEquals("http-basics.hints.http_basics_lesson.1", ""+jsonPath.getList("hint").get(0)); + @Test + public void testLabels() { + + JsonPath jsonPathLabels = getLabels("en"); + Properties propsDefault = getProperties(""); + for (String key : propsDefault.stringPropertyNames()) { + String keyValue = + jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR); + Assertions.assertNotNull(keyValue); } + checkLang(propsDefault, "nl"); + checkLang(propsDefault, "de"); + checkLang(propsDefault, "fr"); + checkLang(propsDefault, "ru"); + } - @Test - public void testLabels() { - - JsonPath jsonPathLabels = getLabels("en"); - Properties propsDefault = getProperties(""); - for (String key: propsDefault.stringPropertyNames()) { - String keyValue = jsonPathLabels.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR); - Assertions.assertNotNull(keyValue); - } - checkLang(propsDefault,"nl"); - checkLang(propsDefault,"de"); - checkLang(propsDefault,"fr"); - checkLang(propsDefault,"ru"); - + private Properties getProperties(String lang) { + Properties prop = null; + if (lang == null || lang.equals("")) { + lang = ""; + } else { + lang = "_" + lang; } + try (InputStream input = + new FileInputStream("src/main/resources/i18n/messages" + lang + ".properties")) { - private Properties getProperties(String lang) { - Properties prop = null; - if (lang == null || lang.equals("")) { lang = ""; } else { lang = "_"+lang; } - try (InputStream input = new FileInputStream("src/main/resources/i18n/messages"+lang+".properties")) { - - prop = new Properties(); - // load a properties file - prop.load(input); - } catch (Exception e) { - e.printStackTrace(); - } - return prop; + prop = new Properties(); + // load a properties file + prop.load(input); + } catch (Exception e) { + e.printStackTrace(); } + return prop; + } - private void checkLang(Properties propsDefault, String lang) { - JsonPath jsonPath = getLabels(lang); - Properties propsLang = getProperties(lang); + private void checkLang(Properties propsDefault, String lang) { + JsonPath jsonPath = getLabels(lang); + Properties propsLang = getProperties(lang); - for (String key: propsLang.stringPropertyNames()) { - if (!propsDefault.containsKey(key)) { - System.err.println("key: " + key + " in (" +lang+") is missing from default properties"); - Assertions.fail(); - } - if (!jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR).equals(propsLang.get(key))) { - System.out.println("key: " + key + " in (" +lang+") has incorrect translation in label service"); - System.out.println("actual:"+jsonPath.getString(ESCAPE_JSON_PATH_CHAR+key+ESCAPE_JSON_PATH_CHAR)); - System.out.println("expected: "+propsLang.getProperty(key)); - System.out.println(); - Assertions.fail(); - } - } + for (String key : propsLang.stringPropertyNames()) { + if (!propsDefault.containsKey(key)) { + System.err.println("key: " + key + " in (" + lang + ") is missing from default properties"); + Assertions.fail(); + } + if (!jsonPath + .getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR) + .equals(propsLang.get(key))) { + System.out.println( + "key: " + key + " in (" + lang + ") has incorrect translation in label service"); + System.out.println( + "actual:" + jsonPath.getString(ESCAPE_JSON_PATH_CHAR + key + ESCAPE_JSON_PATH_CHAR)); + System.out.println("expected: " + propsLang.getProperty(key)); + System.out.println(); + Assertions.fail(); + } } + } - private JsonPath getLabels(String lang) { - return RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .header("Accept-Language",lang) - .cookie("JSESSIONID", getWebGoatCookie()) - //.log().headers() - .get(url("service/labels.mvc")) - .then() - //.log().all() - .statusCode(200).extract().jsonPath(); - } - - private List getHints() { - JsonPath jsonPath = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .contentType(ContentType.JSON) - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/hint.mvc")) - .then() - //.log().all() - .statusCode(200).extract().jsonPath(); - return jsonPath.getList("hint"); - } + private JsonPath getLabels(String lang) { + return RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .header("Accept-Language", lang) + .cookie("JSESSIONID", getWebGoatCookie()) + // .log().headers() + .get(url("service/labels.mvc")) + .then() + // .log().all() + .statusCode(200) + .extract() + .jsonPath(); + } + private List getHints() { + JsonPath jsonPath = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .contentType(ContentType.JSON) + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/hint.mvc")) + .then() + // .log().all() + .statusCode(200) + .extract() + .jsonPath(); + return jsonPath.getList("hint"); + } } diff --git a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java index 6e030d039..e791634ea 100644 --- a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java @@ -1,8 +1,11 @@ package org.owasp.webgoat; -import io.restassured.RestAssured; -import lombok.SneakyThrows; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import io.restassured.RestAssured; +import java.util.Arrays; +import java.util.Map; +import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -10,109 +13,137 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; -import static org.junit.jupiter.api.DynamicTest.dynamicTest; - -import java.util.Arrays; -import java.util.Map; - public class PasswordResetLessonIntegrationTest extends IntegrationTest { - @BeforeEach - @SneakyThrows - public void init() { - startLesson("/PasswordReset"); - } - - @TestFactory - Iterable passwordResetLesson() { - return Arrays.asList( - dynamicTest("assignment 6 - check email link",()-> sendEmailShouldBeAvailableInWebWolf()), - dynamicTest("assignment 6 - solve assignment",()-> solveAssignment()), - dynamicTest("assignment 2 - simple reset",()-> assignment2()), - dynamicTest("assignment 4 - guess questions",()-> assignment4()), - dynamicTest("assignment 5 - simple questions",()-> assignment5()) - ); - } - public void assignment2() { - checkAssignment(url("PasswordReset/simple-mail/reset"), Map.of("emailReset", this.getUser()+"@webgoat.org"), false); - checkAssignment(url("PasswordReset/simple-mail"), Map.of("email", this.getUser()+"@webgoat.org", "password", StringUtils.reverse(this.getUser())), true); - } - - public void assignment4() { - checkAssignment(url("PasswordReset/questions"), Map.of("username", "tom", "securityQuestion", "purple"), true); - } - - public void assignment5() { - checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite animal?"), false); - checkAssignment(url("PasswordReset/SecurityQuestions"), Map.of("question", "What is your favorite color?"), true); - } + @BeforeEach + @SneakyThrows + public void init() { + startLesson("/PasswordReset"); + } - - public void solveAssignment() { - //WebGoat - clickForgotEmailLink("tom@webgoat-cloud.org"); + @TestFactory + Iterable passwordResetLesson() { + return Arrays.asList( + dynamicTest("assignment 6 - check email link", () -> sendEmailShouldBeAvailableInWebWolf()), + dynamicTest("assignment 6 - solve assignment", () -> solveAssignment()), + dynamicTest("assignment 2 - simple reset", () -> assignment2()), + dynamicTest("assignment 4 - guess questions", () -> assignment4()), + dynamicTest("assignment 5 - simple questions", () -> assignment5())); + } - //WebWolf - var link = getPasswordResetLinkFromLandingPage(); + public void assignment2() { + checkAssignment( + url("PasswordReset/simple-mail/reset"), + Map.of("emailReset", this.getUser() + "@webgoat.org"), + false); + checkAssignment( + url("PasswordReset/simple-mail"), + Map.of( + "email", + this.getUser() + "@webgoat.org", + "password", + StringUtils.reverse(this.getUser())), + true); + } - //WebGoat - changePassword(link); - checkAssignment(url("PasswordReset/reset/login"), Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), true); - } + public void assignment4() { + checkAssignment( + url("PasswordReset/questions"), + Map.of("username", "tom", "securityQuestion", "purple"), + true); + } - public void sendEmailShouldBeAvailableInWebWolf() { - clickForgotEmailLink(this.getUser() + "@webgoat.org"); + public void assignment5() { + checkAssignment( + url("PasswordReset/SecurityQuestions"), + Map.of("question", "What is your favorite animal?"), + false); + checkAssignment( + url("PasswordReset/SecurityQuestions"), + Map.of("question", "What is your favorite color?"), + true); + } - var responseBody = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/WebWolf/mail")) - .then() - .extract().response().getBody().asString(); + public void solveAssignment() { + // WebGoat + clickForgotEmailLink("tom@webgoat-cloud.org"); - Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link"); - } - - @AfterEach - 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"); - } + // WebWolf + var link = getPasswordResetLinkFromLandingPage(); - private void changePassword(String link) { + // WebGoat + changePassword(link); + checkAssignment( + url("PasswordReset/reset/login"), + Map.of("email", "tom@webgoat-cloud.org", "password", "123456"), + true); + } + + public void sendEmailShouldBeAvailableInWebWolf() { + clickForgotEmailLink(this.getUser() + "@webgoat.org"); + + var responseBody = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams("resetLink", link, "password", "123456") - .post(url("PasswordReset/reset/change-password")) - .then() - .statusCode(200); - } + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/mail")) + .then() + .extract() + .response() + .getBody() + .asString(); - private String getPasswordResetLinkFromLandingPage() { - var responseBody = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/WebWolf/requests")) - .then() - .extract().response().getBody().asString(); - int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/"); - var link = responseBody.substring(startIndex + "/PasswordReset/reset/reset-password/".length(), responseBody.indexOf(",", startIndex) - 1); - return link; - } + Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link"); + } - private void clickForgotEmailLink(String user) { + @AfterEach + 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"); + } + + private void changePassword(String link) { + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams("resetLink", link, "password", "123456") + .post(url("PasswordReset/reset/change-password")) + .then() + .statusCode(200); + } + + private String getPasswordResetLinkFromLandingPage() { + var responseBody = RestAssured.given() - .when() - .header("host", String.format("%s:%s", "localhost", getWebWolfPort())) - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams("email", user) - .post(url("PasswordReset/ForgotPassword/create-password-reset-link")) - .then() - .statusCode(200); - } + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/requests")) + .then() + .extract() + .response() + .getBody() + .asString(); + int startIndex = responseBody.lastIndexOf("/PasswordReset/reset/reset-password/"); + var link = + responseBody.substring( + startIndex + "/PasswordReset/reset/reset-password/".length(), + responseBody.indexOf(",", startIndex) - 1); + return link; + } + + private void clickForgotEmailLink(String user) { + RestAssured.given() + .when() + .header("host", String.format("%s:%s", "localhost", getWebWolfPort())) + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams("email", user) + .post(url("PasswordReset/ForgotPassword/create-password-reset-link")) + .then() + .statusCode(200); + } } diff --git a/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java index 3eb53ee8e..cc00818f8 100644 --- a/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/PathTraversalIntegrationTest.java @@ -1,16 +1,8 @@ package org.owasp.webgoat; -import io.restassured.RestAssured; -import lombok.SneakyThrows; -import org.hamcrest.CoreMatchers; -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.TestFactory; -import org.junit.jupiter.api.io.TempDir; -import org.springframework.security.core.token.Sha512DigestUtils; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import io.restassured.RestAssured; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -21,117 +13,138 @@ import java.util.Arrays; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; - -import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import lombok.SneakyThrows; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.security.core.token.Sha512DigestUtils; class PathTraversalIT extends IntegrationTest { - @TempDir - Path tempDir; + @TempDir Path tempDir; - private File fileToUpload = null; + private File fileToUpload = null; - @BeforeEach - @SneakyThrows - public void init() { - fileToUpload = Files.createFile(tempDir.resolve("test.jpg")).toFile(); - Files.write(fileToUpload.toPath(), "This is a test".getBytes()); - startLesson("PathTraversal"); + @BeforeEach + @SneakyThrows + public void init() { + fileToUpload = Files.createFile(tempDir.resolve("test.jpg")).toFile(); + Files.write(fileToUpload.toPath(), "This is a test".getBytes()); + startLesson("PathTraversal"); + } + + @TestFactory + Iterable testPathTraversal() { + return Arrays.asList( + dynamicTest("assignment 1 - profile upload", () -> assignment1()), + dynamicTest("assignment 2 - profile upload fix", () -> assignment2()), + dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()), + dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()), + dynamicTest("assignment 5 - zip slip", () -> assignment5())); + } + + private void assignment1() throws IOException { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) + .param("fullName", "../John Doe") + .post(url("/WebGoat/PathTraversal/profile-upload")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void assignment2() throws IOException { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) + .param("fullNameFix", "..././John Doe") + .post(url("/WebGoat/PathTraversal/profile-upload-fix")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void assignment3() throws IOException { + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .multiPart( + "uploadedFileRemoveUserInput", + "../test.jpg", + Files.readAllBytes(fileToUpload.toPath())) + .post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } + + private void assignment4() throws IOException { + var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; + RestAssured.given() + .urlEncodingEnabled(false) + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url(uri)) + .then() + .statusCode(200) + .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); + + checkAssignment( + url("/WebGoat/PathTraversal/random"), + Map.of("secret", Sha512DigestUtils.shaHex(this.getUser())), + true); + } + + private void assignment5() throws IOException { + var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser(); + webGoatHome = + webGoatHome.replaceAll("^[a-zA-Z]:", ""); // Remove C: from the home directory on Windows + + var webGoatDirectory = new File(webGoatHome); + var zipFile = new File(tempDir.toFile(), "upload.zip"); + try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) { + ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory + "/image.jpg"); + zos.putNextEntry(e); + zos.write("test".getBytes(StandardCharsets.UTF_8)); } + MatcherAssert.assertThat( + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath())) + .post(url("/WebGoat/PathTraversal/zip-slip")) + .then() + .statusCode(200) + .extract() + .path("lessonCompleted"), + CoreMatchers.is(true)); + } - @TestFactory - Iterable testPathTraversal() { - return Arrays.asList( - dynamicTest("assignment 1 - profile upload", () -> assignment1()), - dynamicTest("assignment 2 - profile upload fix", () -> assignment2()), - dynamicTest("assignment 3 - profile upload remove user input", () -> assignment3()), - dynamicTest("assignment 4 - profile upload random pic", () -> assignment4()), - dynamicTest("assignment 5 - zip slip", () -> assignment5()) - ); - } - - private void assignment1() throws IOException { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .multiPart("uploadedFile", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) - .param("fullName", "../John Doe") - .post(url("/WebGoat/PathTraversal/profile-upload")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - private void assignment2() throws IOException { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .multiPart("uploadedFileFix", "test.jpg", Files.readAllBytes(fileToUpload.toPath())) - .param("fullNameFix", "..././John Doe") - .post(url("/WebGoat/PathTraversal/profile-upload-fix")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - private void assignment3() throws IOException { - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .multiPart("uploadedFileRemoveUserInput", "../test.jpg", Files.readAllBytes(fileToUpload.toPath())) - .post(url("/WebGoat/PathTraversal/profile-upload-remove-user-input")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - private void assignment4() throws IOException { - var uri = "/WebGoat/PathTraversal/random-picture?id=%2E%2E%2F%2E%2E%2Fpath-traversal-secret"; - RestAssured.given().urlEncodingEnabled(false) - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url(uri)) - .then() - .statusCode(200) - .body(CoreMatchers.is("You found it submit the SHA-512 hash of your username as answer")); - - checkAssignment(url("/WebGoat/PathTraversal/random"), Map.of("secret", - Sha512DigestUtils.shaHex(this.getUser())), true); - } - - private void assignment5() throws IOException { - var webGoatHome = webGoatServerDirectory() + "PathTraversal/" + this.getUser(); - webGoatHome = webGoatHome.replaceAll("^[a-zA-Z]:", ""); //Remove C: from the home directory on Windows - - var webGoatDirectory = new File(webGoatHome); - var zipFile = new File(tempDir.toFile(), "upload.zip"); - try (var zos = new ZipOutputStream(new FileOutputStream(zipFile))) { - ZipEntry e = new ZipEntry("../../../../../../../../../../" + webGoatDirectory + "/image.jpg"); - zos.putNextEntry(e); - zos.write("test".getBytes(StandardCharsets.UTF_8)); - } - MatcherAssert.assertThat( - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .multiPart("uploadedFileZipSlip", "upload.zip", Files.readAllBytes(zipFile.toPath())) - .post(url("/WebGoat/PathTraversal/zip-slip")) - .then() - .statusCode(200) - .extract().path("lessonCompleted"), CoreMatchers.is(true)); - } - - @AfterEach - 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"); - } + @AfterEach + 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"); + } } diff --git a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java index 8b8b870ea..016f6b35d 100644 --- a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java @@ -2,10 +2,6 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.response.Response; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - import java.util.List; import java.util.Map; import java.util.concurrent.Callable; @@ -14,40 +10,48 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; public class ProgressRaceConditionIntegrationTest extends IntegrationTest { - @Test - public void runTests() throws InterruptedException { - int NUMBER_OF_CALLS = 40; - int NUMBER_OF_PARALLEL_THREADS = 5; - startLesson("Challenge1"); - - Callable call = () -> { - //System.out.println("thread "+Thread.currentThread().getName()); - return RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .formParams(Map.of("flag", "test")) - .post(url("/challenge/flag/")); + @Test + public void runTests() throws InterruptedException { + int NUMBER_OF_CALLS = 40; + int NUMBER_OF_PARALLEL_THREADS = 5; + startLesson("Challenge1"); + Callable call = + () -> { + // System.out.println("thread "+Thread.currentThread().getName()); + return RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams(Map.of("flag", "test")) + .post(url("/challenge/flag/")); }; - ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); - List> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList()); - var responses = executorService.invokeAll(flagCalls); + ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); + List> flagCalls = + IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList()); + var responses = executorService.invokeAll(flagCalls); - //A certain amount of parallel calls should fail as optimistic locking in DB is applied - long countStatusCode500 = responses.stream().filter(r -> { - try { - //System.err.println(r.get().getStatusCode()); - return r.get().getStatusCode() != 200; - } catch (InterruptedException | ExecutionException e) { - //System.err.println(e); - throw new IllegalStateException(e); - } - }).count(); - System.err.println("counted status 500: "+countStatusCode500); - Assertions.assertThat(countStatusCode500).isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS/NUMBER_OF_PARALLEL_THREADS))); - } + // A certain amount of parallel calls should fail as optimistic locking in DB is applied + long countStatusCode500 = + responses.stream() + .filter( + r -> { + try { + // System.err.println(r.get().getStatusCode()); + return r.get().getStatusCode() != 200; + } catch (InterruptedException | ExecutionException e) { + // System.err.println(e); + throw new IllegalStateException(e); + } + }) + .count(); + System.err.println("counted status 500: " + countStatusCode500); + Assertions.assertThat(countStatusCode500) + .isLessThanOrEqualTo((NUMBER_OF_CALLS - (NUMBER_OF_CALLS / NUMBER_OF_PARALLEL_THREADS))); + } } diff --git a/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java index e59499108..0bdd74e1f 100644 --- a/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SSRFIntegrationTest.java @@ -3,28 +3,24 @@ package org.owasp.webgoat; import java.io.IOException; import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.Test; public class SSRFIntegrationTest extends IntegrationTest { - - @Test - public void runTests() throws IOException { - startLesson("SSRF"); - - Map params = new HashMap<>(); - params.clear(); - params.put("url", "images/jerry.png"); - - checkAssignment(url("/WebGoat/SSRF/task1"),params,true); - params.clear(); - params.put("url", "http://ifconfig.pro"); - - checkAssignment(url("/WebGoat/SSRF/task2"),params,true); - - checkResults("/SSRF/"); - - } - - + + @Test + public void runTests() throws IOException { + startLesson("SSRF"); + + Map params = new HashMap<>(); + params.clear(); + params.put("url", "images/jerry.png"); + + checkAssignment(url("/WebGoat/SSRF/task1"), params, true); + params.clear(); + params.put("url", "http://ifconfig.pro"); + + checkAssignment(url("/WebGoat/SSRF/task2"), params, true); + + checkResults("/SSRF/"); + } } diff --git a/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java b/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java index ad641212b..127490106 100644 --- a/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SessionManagementIntegrationTest.java @@ -24,24 +24,22 @@ package org.owasp.webgoat; import java.util.Map; - import org.junit.jupiter.api.Test; /** - * * @author Angel Olle Blazquez - * */ - class SessionManagementIT extends IntegrationTest { - - private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login"; + private static final String HIJACK_LOGIN_CONTEXT_PATH = "/WebGoat/HijackSession/login"; - @Test - void hijackSessionTest() { - startLesson("HijackSession"); - - checkAssignment(url(HIJACK_LOGIN_CONTEXT_PATH), Map.of("username", "webgoat", "password", "webgoat"), false); - } + @Test + void hijackSessionTest() { + startLesson("HijackSession"); + + checkAssignment( + url(HIJACK_LOGIN_CONTEXT_PATH), + Map.of("username", "webgoat", "password", "webgoat"), + false); + } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java index 6ae9f838b..64caf659d 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionAdvancedIntegrationTest.java @@ -2,48 +2,60 @@ package org.owasp.webgoat; import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.Test; public class SqlInjectionAdvancedIntegrationTest extends IntegrationTest { - @Test - public void runTests() { - startLesson("SqlInjectionAdvanced"); + @Test + public void runTests() { + startLesson("SqlInjectionAdvanced"); - Map params = new HashMap<>(); - params.clear(); - params.put("username_reg", "tom' AND substring(password,1,1)='t"); - params.put("password_reg", "password"); - params.put("email_reg", "someone@microsoft.com"); - params.put("confirm_password", "password"); - checkAssignmentWithPUT(url("/WebGoat/SqlInjectionAdvanced/challenge"), params, true); + Map params = new HashMap<>(); + params.clear(); + params.put("username_reg", "tom' AND substring(password,1,1)='t"); + params.put("password_reg", "password"); + params.put("email_reg", "someone@microsoft.com"); + params.put("confirm_password", "password"); + checkAssignmentWithPUT(url("/WebGoat/SqlInjectionAdvanced/challenge"), params, true); - params.clear(); - params.put("username_login", "tom"); - params.put("password_login", "thisisasecretfortomonly"); - checkAssignment(url("/WebGoat/SqlInjectionAdvanced/challenge_Login"), params, true); + params.clear(); + params.put("username_login", "tom"); + params.put("password_login", "thisisasecretfortomonly"); + checkAssignment(url("/WebGoat/SqlInjectionAdvanced/challenge_Login"), params, true); - params.clear(); - params.put("userid_6a", "'; SELECT * FROM user_system_data;--"); - checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); + params.clear(); + params.put("userid_6a", "'; SELECT * FROM user_system_data;--"); + checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); - params.clear(); - params.put("userid_6a", "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from user_system_data --"); - checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); + params.clear(); + params.put( + "userid_6a", + "Smith' union select userid,user_name, user_name,user_name,password,cookie,userid from" + + " user_system_data --"); + checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6a"), params, true); - params.clear(); - params.put("userid_6b", "passW0rD"); - checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true); + params.clear(); + params.put("userid_6b", "passW0rD"); + checkAssignment(url("/WebGoat/SqlInjectionAdvanced/attack6b"), params, true); - params.clear(); - params.put("question_0_solution", "Solution 4: A statement has got values instead of a prepared statement"); - params.put("question_1_solution", "Solution 3: ?"); - params.put("question_2_solution", "Solution 2: Prepared statements are compiled once by the database management system waiting for input and are pre-compiled this way."); - params.put("question_3_solution", "Solution 3: Placeholders can prevent that the users input gets attached to the SQL query resulting in a seperation of code and data."); - params.put("question_4_solution", "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); - checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true); + params.clear(); + params.put( + "question_0_solution", + "Solution 4: A statement has got values instead of a prepared statement"); + params.put("question_1_solution", "Solution 3: ?"); + params.put( + "question_2_solution", + "Solution 2: Prepared statements are compiled once by the database management system" + + " waiting for input and are pre-compiled this way."); + params.put( + "question_3_solution", + "Solution 3: Placeholders can prevent that the users input gets attached to the SQL query" + + " resulting in a seperation of code and data."); + params.put( + "question_4_solution", + "Solution 4: The database registers 'Robert' ); DROP TABLE Students;--'."); + checkAssignment(url("/WebGoat/SqlInjectionAdvanced/quiz"), params, true); - checkResults("/SqlInjectionAdvanced/"); - } + checkResults("/SqlInjectionAdvanced/"); + } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java index 6c8c446af..c18fb8b0e 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionLessonIntegrationTest.java @@ -2,77 +2,77 @@ package org.owasp.webgoat; import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.Test; public class SqlInjectionLessonIntegrationTest extends IntegrationTest { - public static final String sql_2 = "select department from employees where last_name='Franco'"; - public static final String sql_3 = "update employees set department='Sales' where last_name='Barnett'"; - public static final String sql_4_drop = "alter table employees drop column phone"; - public static final String sql_4_add = "alter table employees add column phone varchar(20)"; - public static final String sql_5 = "grant select on grant_rights to unauthorized_user"; - public static final String sql_9_account = " ' "; - public static final String sql_9_operator = "or"; - public static final String sql_9_injection = "'1'='1"; - public static final String sql_10_login_count = "2"; - public static final String sql_10_userid = "1 or 1=1"; + public static final String sql_2 = "select department from employees where last_name='Franco'"; + public static final String sql_3 = + "update employees set department='Sales' where last_name='Barnett'"; + public static final String sql_4_drop = "alter table employees drop column phone"; + public static final String sql_4_add = "alter table employees add column phone varchar(20)"; + public static final String sql_5 = "grant select on grant_rights to unauthorized_user"; + public static final String sql_9_account = " ' "; + public static final String sql_9_operator = "or"; + public static final String sql_9_injection = "'1'='1"; + public static final String sql_10_login_count = "2"; + public static final String sql_10_userid = "1 or 1=1"; - public static final String sql_11_a = "Smith' or '1' = '1"; - public static final String sql_11_b = "3SL99A' or '1'='1"; + public static final String sql_11_a = "Smith' or '1' = '1"; + public static final String sql_11_b = "3SL99A' or '1'='1"; - public static final String sql_12_a = "Smith"; - public static final String sql_12_b = "3SL99A' ; update employees set salary= '100000' where last_name='Smith"; + public static final String sql_12_a = "Smith"; + public static final String sql_12_b = + "3SL99A' ; update employees set salary= '100000' where last_name='Smith"; - public static final String sql_13 = "%update% '; drop table access_log ; --'"; + public static final String sql_13 = "%update% '; drop table access_log ; --'"; - @Test - public void runTests() { - startLesson("SqlInjection"); + @Test + public void runTests() { + startLesson("SqlInjection"); - Map params = new HashMap<>(); - params.clear(); - params.put("query", sql_2); - checkAssignment(url("/WebGoat/SqlInjection/attack2"), params, true); + Map params = new HashMap<>(); + params.clear(); + params.put("query", sql_2); + checkAssignment(url("/WebGoat/SqlInjection/attack2"), params, true); - params.clear(); - params.put("query", sql_3); - checkAssignment(url("/WebGoat/SqlInjection/attack3"), params, true); + params.clear(); + params.put("query", sql_3); + checkAssignment(url("/WebGoat/SqlInjection/attack3"), params, true); - params.clear(); - params.put("query", sql_4_add); - checkAssignment(url("/WebGoat/SqlInjection/attack4"), params, true); + params.clear(); + params.put("query", sql_4_add); + checkAssignment(url("/WebGoat/SqlInjection/attack4"), params, true); - params.clear(); - params.put("query", sql_5); - checkAssignment(url("/WebGoat/SqlInjection/attack5"), params, true); + params.clear(); + params.put("query", sql_5); + checkAssignment(url("/WebGoat/SqlInjection/attack5"), params, true); - params.clear(); - params.put("operator", sql_9_operator); - params.put("account", sql_9_account); - params.put("injection", sql_9_injection); - checkAssignment(url("/WebGoat/SqlInjection/assignment5a"), params, true); + params.clear(); + params.put("operator", sql_9_operator); + params.put("account", sql_9_account); + params.put("injection", sql_9_injection); + checkAssignment(url("/WebGoat/SqlInjection/assignment5a"), params, true); - params.clear(); - params.put("login_count", sql_10_login_count); - params.put("userid", sql_10_userid); - checkAssignment(url("/WebGoat/SqlInjection/assignment5b"), params, true); + params.clear(); + params.put("login_count", sql_10_login_count); + params.put("userid", sql_10_userid); + checkAssignment(url("/WebGoat/SqlInjection/assignment5b"), params, true); - params.clear(); - params.put("name", sql_11_a); - params.put("auth_tan", sql_11_b); - checkAssignment(url("/WebGoat/SqlInjection/attack8"), params, true); + params.clear(); + params.put("name", sql_11_a); + params.put("auth_tan", sql_11_b); + checkAssignment(url("/WebGoat/SqlInjection/attack8"), params, true); - params.clear(); - params.put("name", sql_12_a); - params.put("auth_tan", sql_12_b); - checkAssignment(url("/WebGoat/SqlInjection/attack9"), params, true); + params.clear(); + params.put("name", sql_12_a); + params.put("auth_tan", sql_12_b); + checkAssignment(url("/WebGoat/SqlInjection/attack9"), params, true); - params.clear(); - params.put("action_string", sql_13); - checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true); + params.clear(); + params.put("action_string", sql_13); + checkAssignment(url("/WebGoat/SqlInjection/attack10"), params, true); - checkResults("/SqlInjection/"); - - } + checkResults("/SqlInjection/"); + } } diff --git a/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java index 6d9394674..b09bbc4d4 100644 --- a/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/SqlInjectionMitigationIntegrationTest.java @@ -1,70 +1,85 @@ package org.owasp.webgoat; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.Test; - import static org.hamcrest.CoreMatchers.containsString; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + public class SqlInjectionMitigationIntegrationTest extends IntegrationTest { - @Test - public void runTests() { - startLesson("SqlInjectionMitigations"); + @Test + public void runTests() { + startLesson("SqlInjectionMitigations"); - Map params = new HashMap<>(); - params.clear(); - params.put("field1", "getConnection"); - params.put("field2", "PreparedStatement prep"); - params.put("field3", "prepareStatement"); - params.put("field4", "?"); - params.put("field5", "?"); - params.put("field6", "prep.setString(1,\"\")"); - params.put("field7", "prep.setString(2,\\\"\\\")"); - checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true); + Map params = new HashMap<>(); + params.clear(); + params.put("field1", "getConnection"); + params.put("field2", "PreparedStatement prep"); + params.put("field3", "prepareStatement"); + params.put("field4", "?"); + params.put("field5", "?"); + params.put("field6", "prep.setString(1,\"\")"); + params.put("field7", "prep.setString(2,\\\"\\\")"); + checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10a"), params, true); - params.put("editor", "try {\r\n" + - " Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n" + - " PreparedStatement prep = conn.prepareStatement(\"select id from users where name = ?\");\r\n" + - " prep.setString(1,\"me\");\r\n" + - " prep.execute();\r\n" + - " System.out.println(conn); //should output 'null'\r\n" + - "} catch (Exception e) {\r\n" + - " System.out.println(\"Oops. Something went wrong!\");\r\n" + - "}"); - checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true); + params.put( + "editor", + "try {\r\n" + + " Connection conn = DriverManager.getConnection(DBURL,DBUSER,DBPW);\r\n" + + " PreparedStatement prep = conn.prepareStatement(\"select id from users where name" + + " = ?\");\r\n" + + " prep.setString(1,\"me\");\r\n" + + " prep.execute();\r\n" + + " System.out.println(conn); //should output 'null'\r\n" + + "} catch (Exception e) {\r\n" + + " System.out.println(\"Oops. Something went wrong!\");\r\n" + + "}"); + checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true); - params.clear(); - params.put("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--"); - checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true); + params.clear(); + params.put( + "userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--"); + checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true); - params.clear(); - params.put("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--"); - checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true); + params.clear(); + params.put( + "userid_sql_only_input_validation_on_keywords", + "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--"); + checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true); - RestAssured.given() - .when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname else id end)")) - .then() - .statusCode(200); + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get( + url( + "/WebGoat/SqlInjectionMitigations/servers?column=(case when (true) then hostname" + + " else id end)")) + .then() + .statusCode(200); - RestAssured.given() - .when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) - .contentType(ContentType.JSON) - .get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown")) - .then() - .statusCode(500) - .body("trace", containsString("select id, hostname, ip, mac, status, description from SERVERS where status <> 'out of order' order by")); + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .contentType(ContentType.JSON) + .get(url("/WebGoat/SqlInjectionMitigations/servers?column=unknown")) + .then() + .statusCode(500) + .body( + "trace", + containsString( + "select id, hostname, ip, mac, status, description from SERVERS where status <>" + + " 'out of order' order by")); - params.clear(); - params.put("ip", "104.130.219.202"); - checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true); + params.clear(); + params.put("ip", "104.130.219.202"); + checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true); - checkResults(); - } + checkResults(); + } } diff --git a/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java index 041f5157f..6c01aa361 100644 --- a/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/WebWolfIntegrationTest.java @@ -2,71 +2,78 @@ package org.owasp.webgoat; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.restassured.RestAssured; import java.io.IOException; import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.Test; -import io.restassured.RestAssured; - public class WebWolfIntegrationTest extends IntegrationTest { - - @Test - public void runTests() throws IOException { - startLesson("WebWolfIntroduction"); - - //Assignment 3 - Map params = new HashMap<>(); - params.clear(); - params.put("email", this.getUser()+"@webgoat.org"); - checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false); - - String responseBody = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/WebWolf/mail")) - .then() - .extract().response().getBody().asString(); - - String uniqueCode = responseBody.replace("%20", " "); - uniqueCode = uniqueCode.substring(21+uniqueCode.lastIndexOf("your unique code is: "),uniqueCode.lastIndexOf("your unique code is: ")+(21+ this.getUser().length())); - params.clear(); - params.put("uniqueCode", uniqueCode); - checkAssignment(url("/WebGoat/WebWolf/mail"), params, true); - - //Assignment 4 - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .queryParams(params) - .get(url("/WebGoat/WebWolf/landing/password-reset")) - .then() - .statusCode(200); + + @Test + public void runTests() throws IOException { + startLesson("WebWolfIntroduction"); + + // Assignment 3 + Map params = new HashMap<>(); + params.clear(); + params.put("email", this.getUser() + "@webgoat.org"); + checkAssignment(url("/WebGoat/WebWolf/mail/send"), params, false); + + String responseBody = RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/mail")) + .then() + .extract() + .response() + .getBody() + .asString(); + + String uniqueCode = responseBody.replace("%20", " "); + uniqueCode = + uniqueCode.substring( + 21 + uniqueCode.lastIndexOf("your unique code is: "), + uniqueCode.lastIndexOf("your unique code is: ") + (21 + this.getUser().length())); + params.clear(); + params.put("uniqueCode", uniqueCode); + checkAssignment(url("/WebGoat/WebWolf/mail"), params, true); + + // Assignment 4 + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .queryParams(params) + .get(url("/WebGoat/WebWolf/landing/password-reset")) + .then() + .statusCode(200); + RestAssured.given() .when() .relaxedHTTPSValidation() .cookie("WEBWOLFSESSION", getWebWolfCookie()) .queryParams(params) - .get(webWolfUrl("/landing")) + .get(webWolfUrl("/landing")) .then() .statusCode(200); - responseBody = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/WebWolf/requests")) - .then() - .extract().response().getBody().asString(); - assertTrue(responseBody.contains(uniqueCode)); - params.clear(); - params.put("uniqueCode", uniqueCode); - checkAssignment(url("/WebGoat/WebWolf/landing"), params, true); - - checkResults("/WebWolf"); - - } - + responseBody = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/requests")) + .then() + .extract() + .response() + .getBody() + .asString(); + assertTrue(responseBody.contains(uniqueCode)); + params.clear(); + params.put("uniqueCode", uniqueCode); + checkAssignment(url("/WebGoat/WebWolf/landing"), params, true); + + checkResults("/WebWolf"); + } } diff --git a/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java index adae15d2c..64fc792d9 100644 --- a/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/XSSIntegrationTest.java @@ -1,68 +1,80 @@ package org.owasp.webgoat; import io.restassured.RestAssured; - import java.util.HashMap; import java.util.Map; - import org.junit.jupiter.api.Test; public class XSSIntegrationTest extends IntegrationTest { - - @Test - public void crossSiteScriptingAssignments() { - startLesson("CrossSiteScripting"); + @Test + public void crossSiteScriptingAssignments() { + startLesson("CrossSiteScripting"); - Map params = new HashMap<>(); - params.clear(); - params.put("checkboxAttack1", "value"); - checkAssignment(url("/CrossSiteScripting/attack1"), params, true); + Map params = new HashMap<>(); + params.clear(); + params.put("checkboxAttack1", "value"); + checkAssignment(url("/CrossSiteScripting/attack1"), params, true); - params.clear(); - params.put("QTY1", "1"); - params.put("QTY2", "1"); - params.put("QTY3", "1"); - params.put("QTY4", "1"); - params.put("field1", ""); - params.put("field2", "111"); - checkAssignmentWithGet(url("/CrossSiteScripting/attack5a"), params, true); + params.clear(); + params.put("QTY1", "1"); + params.put("QTY2", "1"); + params.put("QTY3", "1"); + params.put("QTY4", "1"); + params.put("field1", ""); + params.put("field2", "111"); + checkAssignmentWithGet(url("/CrossSiteScripting/attack5a"), params, true); - params.clear(); - params.put("DOMTestRoute", "start.mvc#test"); - checkAssignment(url("/CrossSiteScripting/attack6a"), params, true); - - params.clear(); - params.put("param1", "42"); - params.put("param2", "24"); + params.clear(); + params.put("DOMTestRoute", "start.mvc#test"); + checkAssignment(url("/CrossSiteScripting/attack6a"), params, true); - String result = - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .header("webgoat-requested-by", "dom-xss-vuln") - .header("X-Requested-With", "XMLHttpRequest") - .formParams(params) - .post(url("/CrossSiteScripting/phone-home-xss")) - .then() - .statusCode(200) - .extract().path("output"); - String secretNumber = result.substring("phoneHome Response is ".length()); - - params.clear(); - params.put("successMessage", secretNumber); - checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true); - - params.clear(); - params.put("question_0_solution", "Solution 4: No because the browser trusts the website if it is acknowledged trusted, then the browser does not know that the script is malicious."); - params.put("question_1_solution", "Solution 3: The data is included in dynamic content that is sent to a web user without being validated for malicious content."); - params.put("question_2_solution", "Solution 1: The script is permanently stored on the server and the victim gets the malicious script when requesting information from the server."); - params.put("question_3_solution", "Solution 2: They reflect the injected script off the web server. That occurs when input sent to the web server is part of the request."); - params.put("question_4_solution", "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that the browser executes."); - checkAssignment(url("/CrossSiteScripting/quiz"), params, true); + params.clear(); + params.put("param1", "42"); + params.put("param2", "24"); - checkResults("/CrossSiteScripting/"); + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .header("webgoat-requested-by", "dom-xss-vuln") + .header("X-Requested-With", "XMLHttpRequest") + .formParams(params) + .post(url("/CrossSiteScripting/phone-home-xss")) + .then() + .statusCode(200) + .extract() + .path("output"); + String secretNumber = result.substring("phoneHome Response is ".length()); - } + params.clear(); + params.put("successMessage", secretNumber); + checkAssignment(url("/CrossSiteScripting/dom-follow-up"), params, true); + + params.clear(); + params.put( + "question_0_solution", + "Solution 4: No because the browser trusts the website if it is acknowledged trusted, then" + + " the browser does not know that the script is malicious."); + params.put( + "question_1_solution", + "Solution 3: The data is included in dynamic content that is sent to a web user without" + + " being validated for malicious content."); + params.put( + "question_2_solution", + "Solution 1: The script is permanently stored on the server and the victim gets the" + + " malicious script when requesting information from the server."); + params.put( + "question_3_solution", + "Solution 2: They reflect the injected script off the web server. That occurs when input" + + " sent to the web server is part of the request."); + params.put( + "question_4_solution", + "Solution 4: No there are many other ways. Like HTML, Flash or any other type of code that" + + " the browser executes."); + checkAssignment(url("/CrossSiteScripting/quiz"), params, true); + + checkResults("/CrossSiteScripting/"); + } } diff --git a/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java index e7c2a5497..fdb06d8ee 100644 --- a/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/XXEIntegrationTest.java @@ -2,98 +2,125 @@ package org.owasp.webgoat; import io.restassured.RestAssured; import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; - 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 { - private static final String xxe3 = """ - ]>&xxe;test"""; - private static final String xxe4 = """ - ]>&xxe;test"""; - private static final String dtd7 = """ - ">%all;"""; - private static final String xxe7 = """ - %remote;]>test&send;"""; + private static final String xxe3 = + """ + ]>&xxe;test + """; + private static final String xxe4 = + """ + ]>&xxe;test + """; + private static final String dtd7 = + """ + ">%all; + """; + private static final String xxe7 = + """ + %remote;]>test&send; + """; - private String webGoatHomeDirectory; - private String webWolfFileServerLocation; + 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(); + /* + * 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("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false); + checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false); + checkAssignment( + url("/WebGoat/xxe/blind"), + ContentType.XML, + "" + getSecret() + "", + false); + } + + /** + * This performs the steps of the exercise before the secret can be committed in the final step. + * + * @return + * @throws IOException + */ + private String getSecret() throws IOException { + // remove any left over DTD + Path webWolfFilePath = Paths.get(webWolfFileServerLocation); + if (webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd")).toFile().exists()) { + Files.delete(webWolfFilePath.resolve(Paths.get(this.getUser(), "blind.dtd"))); + } + String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); + String dtd7String = + dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile); + + // upload DTD + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .multiPart("file", "blind.dtd", dtd7String.getBytes()) + .post(webWolfUrl("/fileupload")) + .then() + .extract() + .response() + .getBody() + .asString(); + // upload attack + String xxe7String = + xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser()); + checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false); + + // read results from WebWolf + String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/enable-security.mvc")) - .then() - .statusCode(200); - checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, false); - checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, false); - checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "" + getSecret() + "", false); + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/WebWolf/requests")) + .then() + .extract() + .response() + .getBody() + .asString(); + result = result.replace("%20", " "); + if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) { + result = + result.substring( + result.lastIndexOf("WebGoat 8.0 rocks... ("), + result.lastIndexOf("WebGoat 8.0 rocks... (") + 33); } + return result; + } - /** - * 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"))); - } - String secretFile = webGoatHomeDirectory.concat("/XXE/" + getUser() + "/secret.txt"); - String dtd7String = dtd7.replace("WEBWOLFURL", webWolfUrl("/landing")).replace("SECRET", secretFile); - - //upload DTD - RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .multiPart("file", "blind.dtd", dtd7String.getBytes()) - .post(webWolfUrl("/fileupload")) - .then() - .extract().response().getBody().asString(); - //upload attack - String xxe7String = xxe7.replace("WEBWOLFURL", webWolfUrl("/files")).replace("USERNAME", this.getUser()); - checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, xxe7String, false); - - //read results from WebWolf - String result = RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("WEBWOLFSESSION", getWebWolfCookie()) - .get(webWolfUrl("/WebWolf/requests")) - .then() - .extract().response().getBody().asString(); - result = result.replace("%20", " "); - if (-1 != result.lastIndexOf("WebGoat 8.0 rocks... (")) { - result = result.substring(result.lastIndexOf("WebGoat 8.0 rocks... ("), result.lastIndexOf("WebGoat 8.0 rocks... (") + 33); - } - return result; - } - - @Test - public void runTests() throws IOException { - startLesson("XXE", true); - webGoatHomeDirectory = webGoatServerDirectory(); - webWolfFileServerLocation = getWebWolfFileServerLocation(); - checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true); - checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true); - checkAssignment(url("/WebGoat/xxe/blind"), ContentType.XML, "" + getSecret() + "", true); - checkResults("xxe/"); - } + @Test + public void runTests() throws IOException { + startLesson("XXE", true); + webGoatHomeDirectory = webGoatServerDirectory(); + webWolfFileServerLocation = getWebWolfFileServerLocation(); + checkAssignment(url("/WebGoat/xxe/simple"), ContentType.XML, xxe3, true); + checkAssignment(url("/WebGoat/xxe/content-type"), ContentType.XML, xxe4, true); + checkAssignment( + url("/WebGoat/xxe/blind"), + ContentType.XML, + "" + getSecret() + "", + true); + checkResults("xxe/"); + } } From 075b1ab30a45dfa6493b7a0f2a9494bef1b92a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Wed, 15 Feb 2023 23:25:25 +0100 Subject: [PATCH 011/155] Fix WebWolf JWT tool --- src/main/resources/webwolf/static/js/jwt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/webwolf/static/js/jwt.js b/src/main/resources/webwolf/static/js/jwt.js index 093b8b55d..d8e83c4c0 100644 --- a/src/main/resources/webwolf/static/js/jwt.js +++ b/src/main/resources/webwolf/static/js/jwt.js @@ -19,7 +19,7 @@ $(document).ready(() => { function call(encode) { return () => { - var url = encode ? '/WebWolf/jwt/encode' : '/WebWolf/jwt/decode'; + var url = encode ? '/jwt/encode' : '/jwt/decode'; var formData = encode ? $('#encodeForm').getFormData() : $('#decodeForm').getFormData(); formData["secretKey"] = $('#secretKey').val(); From 693771220c10a4544048abfc5b64997ff13f13f6 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 10:11:17 +0000 Subject: [PATCH 012/155] fix: change url in JavaScript for JWT endpoint The JavaScript pointed to the context root /WebWolf/ which is no longer in use. --- .../org/owasp/webgoat/webwolf/WebSecurityConfig.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java index 7afa030af..740a34856 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java +++ b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java @@ -48,14 +48,15 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = http.authorizeRequests() - .antMatchers("/css/**", "/images/**", "/js/**", "/fonts/**", "/webjars/**", "/home") - .permitAll() - .antMatchers(HttpMethod.GET, "/mail/**", "/requests/**") + .antMatchers(HttpMethod.POST, "/fileupload") .authenticated() - .antMatchers("/files") + .antMatchers(HttpMethod.GET, "/files", "/mail", "/requests") .authenticated() + .and() + .authorizeRequests() .anyRequest() .permitAll(); + security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true"); security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll(); security.and().logout().permitAll(); From 1a2855afcd9d0b13a76d89349678e6e31199ffa3 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 11:44:10 +0000 Subject: [PATCH 013/155] chore: set directories explicitly when running IT tests --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 74af4dd62..7dc376ec7 100644 --- a/pom.xml +++ b/pom.xml @@ -655,8 +655,9 @@ java -jar -Dlogging.pattern.console= + -Dwebgoat.server.directory=${java.io.tmpdir}/webgoat_${webgoat.port} + -Dwebgoat.user.directory=${java.io.tmpdir}/webgoat_${webgoat.port} -Dspring.main.banner-mode=off - -Dspring.datasource.url=jdbc:hsqldb:file:${java.io.tmpdir}/webgoat -Dwebgoat.port=${webgoat.port} -Dwebwolf.port=${webwolf.port} --add-opens From b68adfbc7cbbf83ef340d2e507f2c42696fa444e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 09:58:14 +0000 Subject: [PATCH 014/155] Bump devops-infra/action-pull-request from 0.5.3 to 0.5.5 Bumps [devops-infra/action-pull-request](https://github.com/devops-infra/action-pull-request) from 0.5.3 to 0.5.5. - [Release notes](https://github.com/devops-infra/action-pull-request/releases) - [Commits](https://github.com/devops-infra/action-pull-request/compare/v0.5.3...v0.5.5) --- updated-dependencies: - dependency-name: devops-infra/action-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a247150e0..c045b1543 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -129,7 +129,7 @@ jobs: force: false - name: Create PR - uses: devops-infra/action-pull-request@v0.5.3 + uses: devops-infra/action-pull-request@v0.5.5 with: github_token: "${{ secrets.GITHUB_TOKEN }}" title: ${{ github.event.commits[0].message }} From 73b8c431fcb5470e6bdfacf85cd1ae8228435350 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 14:11:59 +0000 Subject: [PATCH 015/155] chore: use constructor instead of field dependency injection --- .../java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java index 721e649ea..a6e97f8d4 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsEndpoint.java @@ -23,7 +23,7 @@ package org.owasp.webgoat.lessons.xxe; import java.util.Collection; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.AllArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -36,9 +36,10 @@ import org.springframework.web.bind.annotation.RestController; */ @RestController @RequestMapping("xxe/comments") +@AllArgsConstructor public class CommentsEndpoint { - @Autowired private CommentsCache comments; + private final CommentsCache comments; @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody From ecfc321f14693c06f6a788aa02dcca011b071e06 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 17:32:13 +0000 Subject: [PATCH 016/155] feature: Add extra feedback once someone solves JWT refresh lesson differently One can solve this lesson by using `alg:none` instead of using the refresh token flow. Instead of adding a check to force using the refresh token we opt for giving the user extra feedback. --- .../webgoat/lessons/jwt/JWTFinalEndpoint.java | 39 +++++----------- .../lessons/jwt/JWTRefreshEndpoint.java | 11 ++--- .../lessons/jwt/JWTSecretKeyEndpoint.java | 4 -- .../webgoat/lessons/jwt/JWTVotesEndpoint.java | 4 -- .../lessons/jwt/i18n/WebGoatLabels.properties | 3 +- .../lessons/jwt/JWTRefreshEndpointTest.java | 44 ++++++++++++++----- 6 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java index e84bbf809..dfebaa03d 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java @@ -22,7 +22,12 @@ package org.owasp.webgoat.lessons.jwt; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SigningKeyResolverAdapter; import io.jsonwebtoken.impl.TextCodec; import java.sql.ResultSet; import java.sql.SQLException; @@ -31,34 +36,12 @@ import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; -/** - * - * - *
- *  {
- *      "typ": "JWT",
- *      "kid": "webgoat_key",
- *      "alg": "HS256"
- *  }
- *  {
- *       "iss": "WebGoat Token Builder",
- *       "iat": 1524210904,
- *       "exp": 1618905304,
- *       "aud": "webgoat.org",
- *       "sub": "jerry@webgoat.com",
- *       "username": "Jerry",
- *       "Email": "jerry@webgoat.com",
- *       "Role": [
- *       "Cat"
- *       ]
- *  }
- * 
- * - * @author nbaars - * @since 4/23/17. - */ @RestController @AssignmentHints({ "jwt-final-hint1", diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java index 945bb57da..4efc9db09 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java @@ -49,10 +49,6 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 4/23/17. - */ @RestController @AssignmentHints({ "jwt-refresh-hint1", @@ -85,9 +81,7 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint { } private Map createNewTokens(String user) { - Map claims = new HashMap<>(); - claims.put("admin", "false"); - claims.put("user", user); + Map claims = Map.of("admin", "false", "user", user); String token = Jwts.builder() .setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10))) @@ -114,6 +108,9 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint { Claims claims = (Claims) jwt.getBody(); String user = (String) claims.get("user"); if ("Tom".equals(user)) { + if ("none".equals(jwt.getHeader().get("alg"))) { + return ok(success(this).feedback("jwt-refresh-alg-none").build()); + } return ok(success(this).build()); } return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build()); diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java index dac1ef5cc..0e688c049 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java @@ -42,10 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 4/23/17. - */ @RestController @AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"}) public class JWTSecretKeyEndpoint extends AssignmentEndpoint { diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java index 632449822..02a935498 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java @@ -58,10 +58,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 4/23/17. - */ @RestController @AssignmentHints({ "jwt-change-token-hint1", diff --git a/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties index 70ac7a4a1..ed05f46b2 100644 --- a/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties +++ b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties @@ -21,6 +21,7 @@ jwt-refresh-hint2=The token from the access log is no longer valid, can you find jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken' jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token jwt-refresh-not-tom=User is not Tom but {0}, please try again +jwt-refresh-alg-none=Nicely found! You solved the assignment with 'alg: none' can you also solve it by using the refresh token? jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom jwt-final-not-tom=Username is not Tom try to pass a token for Tom @@ -30,4 +31,4 @@ jwt-final-hint2=The 'kid' (key ID) header parameter is a hint indicating which k jwt-final-hint3=The key can be located on the filesystem in memory or even reside in the database jwt-final-hint4=The key is stored in the database and loaded while verifying a token jwt-final-hint5=Using a SQL injection you might be able to manipulate the key to something you know and create a new token. -jwt-final-hint6=Use: hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS -- as the kid in the header and change the contents of the token to Tom and hit the endpoint with the new token \ No newline at end of file +jwt-final-hint6=Use: hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS -- as the kid in the header and change the contents of the token to Tom and hit the endpoint with the new token diff --git a/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java b/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java index 36eb18305..028717706 100644 --- a/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java +++ b/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java @@ -29,6 +29,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.Jwts; import java.util.HashMap; import java.util.Map; import org.hamcrest.CoreMatchers; @@ -43,14 +44,14 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; public class JWTRefreshEndpointTest extends LessonTest { @BeforeEach - public void setup() { + void setup() { when(webSession.getCurrentLesson()).thenReturn(new JWT()); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); when(webSession.getUserName()).thenReturn("unit-test"); } @Test - public void solveAssignment() throws Exception { + void solveAssignment() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); // First login to obtain tokens for Jerry @@ -96,7 +97,26 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception { + void solutionWithAlgNone() throws Exception { + String tokenWithNoneAlgorithm = + Jwts.builder() + .setHeaderParam("alg", "none") + .addClaims(Map.of("admin", "true", "user", "Tom")) + .compact(); + + // Now checkout with the new token from Tom + mockMvc + .perform( + MockMvcRequestBuilders.post("/JWT/refresh/checkout") + .header("Authorization", "Bearer " + tokenWithNoneAlgorithm)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", is(true))) + .andExpect( + jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-refresh-alg-none")))); + } + + @Test + void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception { String accessTokenTom = "eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q"; mockMvc @@ -108,7 +128,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void checkoutWitRandomTokenShouldFail() throws Exception { + void checkoutWitRandomTokenShouldFail() throws Exception { String accessTokenTom = "eyJhbGciOiJIUzUxMiJ9.eyJpLXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q"; mockMvc @@ -121,7 +141,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void flowForJerryAlwaysWorks() throws Exception { + void flowForJerryAlwaysWorks() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); var loginJson = Map.of("user", "Jerry", "password", PASSWORD); @@ -146,7 +166,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void loginShouldNotWorkForJerryWithWrongPassword() throws Exception { + void loginShouldNotWorkForJerryWithWrongPassword() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); var loginJson = Map.of("user", "Jerry", "password", PASSWORD + "wrong"); @@ -159,7 +179,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void loginShouldNotWorkForTom() throws Exception { + void loginShouldNotWorkForTom() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); var loginJson = Map.of("user", "Tom", "password", PASSWORD); @@ -172,7 +192,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void newTokenShouldWorkForJerry() throws Exception { + void newTokenShouldWorkForJerry() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Map loginJson = new HashMap<>(); loginJson.put("user", "Jerry"); @@ -201,7 +221,7 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void unknownRefreshTokenShouldGiveUnauthorized() throws Exception { + void unknownRefreshTokenShouldGiveUnauthorized() throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Map loginJson = new HashMap<>(); loginJson.put("user", "Jerry"); @@ -229,21 +249,21 @@ public class JWTRefreshEndpointTest extends LessonTest { } @Test - public void noTokenWhileCheckoutShouldReturn401() throws Exception { + void noTokenWhileCheckoutShouldReturn401() throws Exception { mockMvc .perform(MockMvcRequestBuilders.post("/JWT/refresh/checkout")) .andExpect(status().isUnauthorized()); } @Test - public void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception { + void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception { mockMvc .perform(MockMvcRequestBuilders.post("/JWT/refresh/newToken")) .andExpect(status().isUnauthorized()); } @Test - public void noTokenWhileLoginShouldReturn401() throws Exception { + void noTokenWhileLoginShouldReturn401() throws Exception { mockMvc .perform(MockMvcRequestBuilders.post("/JWT/refresh/login")) .andExpect(status().isUnauthorized()); From f1012c85d689b3a18c8eefb2142afb455ab2ba9d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 21:34:26 +0100 Subject: [PATCH 017/155] feat: add Docker desktop version of WebGoat with all tools installed The new Docker image uses linuxserver/webtop giving users the opportunity to run a Linux desktop in their browser without installing any tools on their local machine. --- .dockerignore | 1 + Dockerfile_desktop | 29 +++++++++++++++++++++++++++++ config/desktop/WebGoat.txt | 10 ++++++++++ config/desktop/start_webgoat.sh | 17 +++++++++++++++++ config/desktop/start_zap.sh | 3 +++ 5 files changed, 60 insertions(+) create mode 100644 Dockerfile_desktop create mode 100644 config/desktop/WebGoat.txt create mode 100644 config/desktop/start_webgoat.sh create mode 100644 config/desktop/start_zap.sh diff --git a/.dockerignore b/.dockerignore index 35b2f7ce0..0d54d2e34 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ ** !/target +!/config/desktop diff --git a/Dockerfile_desktop b/Dockerfile_desktop new file mode 100644 index 000000000..ffc3837f6 --- /dev/null +++ b/Dockerfile_desktop @@ -0,0 +1,29 @@ +FROM lscr.io/linuxserver/webtop:ubuntu-xfce +LABEL NAME = "WebGoat: A deliberately insecure Web Application" +MAINTAINER "WebGoat team" + +WORKDIR /config + +COPY target/webgoat-*.jar /config/webgoat.jar +COPY config/desktop/start_webgoat.sh /config/start_webgoat.sh +COPY config/desktop/start_zap.sh /config/start_zap.sh +COPY config/desktop/WebGoat.txt /config/Desktop/ + +RUN \ + curl -LO https://github.com/zaproxy/zaproxy/releases/download/v2.12.0/ZAP_2.12.0_Linux.tar.gz && \ + tar zfxv ZAP_2.12.0_Linux.tar.gz && \ + rm -rf ZAP_2.12.0_Linux.tar.gz && \ + curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.6%2B10/OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \ + tar zfxv OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \ + rm -rf OpenJDK17U-jre_aarch64_linux_hotspot_17.0.6_10.tar.gz && \ + chmod +x /config/start_webgoat.sh && \ + chmod +x /config/start_zap.sh && \ + apt-get update && \ + apt-get --yes install vim nano && \ + echo "JAVA_HOME=/config/jdk-17.0.6+10-jre/" >> .bash_aliases && \ + echo "PATH=$PATH:$JAVA_HOME/bin" >> .bash_aliases + + +ENV JAVA_HOME=/home/webgoat/jdk-17.0.6+10-jre + +WORKDIR /config/Desktop diff --git a/config/desktop/WebGoat.txt b/config/desktop/WebGoat.txt new file mode 100644 index 000000000..66499f309 --- /dev/null +++ b/config/desktop/WebGoat.txt @@ -0,0 +1,10 @@ +** Welcome to WebGoat desktop image + +With this image you have WebGoat and ZAP and a browser available to you in a browser running on Ubuntu. +You can start WebGoat and ZAP by opening a terminal and type: + +./start-webgoat.sh +./start_zap.sh + +Happy hacking, +Team WebGoat diff --git a/config/desktop/start_webgoat.sh b/config/desktop/start_webgoat.sh new file mode 100644 index 000000000..fe2fa042d --- /dev/null +++ b/config/desktop/start_webgoat.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +/config/jdk-17.0.6+10-jre/bin/java \ + -Duser.home=/config \ + -Dfile.encoding=UTF-8 \ + -DTZ=Europe/Amsterdam \ + --add-opens java.base/java.lang=ALL-UNNAMED \ + --add-opens java.base/java.util=ALL-UNNAMED \ + --add-opens java.base/java.lang.reflect=ALL-UNNAMED \ + --add-opens java.base/java.text=ALL-UNNAMED \ + --add-opens java.desktop/java.beans=ALL-UNNAMED \ + --add-opens java.desktop/java.awt.font=ALL-UNNAMED \ + --add-opens java.base/sun.nio.ch=ALL-UNNAMED \ + --add-opens java.base/java.io=ALL-UNNAMED \ + --add-opens java.base/java.util=ALL-UNNAMED \ + -Drunning.in.docker=false \ + -jar /config/webgoat.jar diff --git a/config/desktop/start_zap.sh b/config/desktop/start_zap.sh new file mode 100644 index 000000000..5a0cb999b --- /dev/null +++ b/config/desktop/start_zap.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/config/jdk-17.0.6+10-jre/bin/java -jar /config/ZAP_2.12.0/zap-2.12.0.jar From f6c7a54931226eb4266e1ae123585e59209a050d Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 21:39:06 +0100 Subject: [PATCH 018/155] docs: add screenshot to README and add Docker WebGoat desktop text --- README.md | 26 +++++++++++++++++--------- docs/images/webgoat.png | Bin 0 -> 326307 bytes 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 docs/images/webgoat.png diff --git a/README.md b/README.md index 84391b8c6..8401d8d41 100644 --- a/README.md +++ b/README.md @@ -27,24 +27,26 @@ you are caught engaging in unauthorized hacking, most companies will fire you. Claiming that you were doing security research will not work as that is the first thing that all hackers claim.* +![WebGoat](docs/images/webgoat.png) + # Installation instructions: For more details check [the Contribution guide](/CONTRIBUTING.md) ## 1. Run using Docker +Already have a browser and ZAP and/or Burp installed on your machine in this case you can run the WebGoat image directly using Docker. + Every release is also published on [DockerHub](https://hub.docker.com/r/webgoat/webgoat). -The easiest way to start WebGoat as a Docker container is to use the all-in-one docker container. This is a docker image that has WebGoat and WebWolf running inside. - ```shell -docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat +docker run -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat ``` If you want to reuse the container, give it a name: ```shell -docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e TZ=Europe/Amsterdam webgoat/webgoat +docker run --name webgoat -it -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 webgoat/webgoat ``` As long as you don't remove the container you can use: @@ -55,9 +57,15 @@ docker start webgoat This way, you can start where you left off. If you remove the container, you need to use `docker run` again. -**Important**: *Choose the correct timezone, so that the docker container and your host are in the same timezone. As it is important for the validity of JWT tokens used in certain exercises.* +## 2. Run using Docker with complete Linux Desktop -## 2. Standalone +Instead of installing tools locally we have a complete Docker image based on running a desktop in your browser. This way you only have to run a Docker image which will give you the best user experience. + +```shell +docker run -p 127.0.0.1:3000:3000 webgoat/webgoat-desktop +``` + +## 3. Standalone Download the latest WebGoat release from [https://github.com/WebGoat/WebGoat/releases](https://github.com/WebGoat/WebGoat/releases) @@ -67,7 +75,7 @@ java -Dfile.encoding=UTF-8 -Dwebgoat.port=8080 -Dwebwolf.port=9090 -jar webgoat- Click the link in the log to start WebGoat. -## 3. Run from the sources +## 4. Run from the sources ### Prerequisites: @@ -96,7 +104,7 @@ git checkout <> docker build -f Dockerfile . -t webgoat/webgoat ``` -Now we are ready to run the project. WebGoat 8.x is using Spring-Boot. +Now we are ready to run the project. WebGoat is using Spring Boot. ```Shell # On Linux/Mac: @@ -131,6 +139,6 @@ java -jar target/webgoat-2023.3-SNAPSHOT.jar Or in a docker run it would (once this version is pushed into docker hub) look like this: ```Shell -docker run -d -p 8080:8080 -p 9090:9090 -e TZ=Europe/Amsterdam -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat +docker run -d -p 127.0.0.1:8080:8080 -p 127.0.0.1:9090:9090 -e EXCLUDE_CATEGORIES="CLIENT_SIDE,GENERAL,CHALLENGE" -e EXCLUDE_LESSONS="SqlInjectionAdvanced,SqlInjectionMitigations" webgoat/webgoat ``` diff --git a/docs/images/webgoat.png b/docs/images/webgoat.png new file mode 100644 index 0000000000000000000000000000000000000000..4ae812ece377732aa9dd61234d4b88b5b08b8dd8 GIT binary patch literal 326307 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 |yg#c5&K(@ucwg2VWUi9WqCe`V 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

UJ_XHItNvhAOj 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

Success!!

" + "" + "

Here is your flag: " - + "" - + flags.getFlag(7) - + ""); + + flags.getFlag(7)); } return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT) .body("That is not the reset link for admin"); @@ -99,6 +98,6 @@ public class Assignment7 extends AssignmentEndpoint { @GetMapping(value = "/challenge/7/.git", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ClassPathResource git() { - return new ClassPathResource("challenge7/git.zip"); + return new ClassPathResource("lessons/challenges/challenge7/git.zip"); } } diff --git a/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java index 6a3640bfd..e7a42214f 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java @@ -23,26 +23,27 @@ package org.owasp.webgoat.webwolf.mailbox; import java.util.List; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController -@AllArgsConstructor +@RequiredArgsConstructor @Slf4j public class MailboxController { private final MailboxRepository mailboxRepository; - @GetMapping(value = "/mail") + @GetMapping("/mail") public ModelAndView mail() { UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); @@ -56,9 +57,15 @@ public class MailboxController { return modelAndView; } - @PostMapping(value = "/mail") - public ResponseEntity sendEmail(@RequestBody Email email) { + @PostMapping("/mail") + @ResponseStatus(HttpStatus.CREATED) + public void sendEmail(@RequestBody Email email) { mailboxRepository.save(email); - return ResponseEntity.status(HttpStatus.CREATED).build(); + } + + @DeleteMapping("/mail") + @ResponseStatus(HttpStatus.ACCEPTED) + public void deleteAllMail() { + mailboxRepository.deleteAll(); } } From a509e8e24eeaf6d993f12d8e76b53b981934b16d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 09:32:04 +0000 Subject: [PATCH 037/155] chore: bump commons-text from 1.9 to 1.10.0 Bumps commons-text from 1.9 to 1.10.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 75e55b8cb..da38a3e32 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ 3.2.1 2.6 3.12.0 - 1.9 + 1.10.0 30.1-jre 0.8.8 17 From 491fe2d84d9613400a624d06c5dc30d005e97b0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 09:32:13 +0000 Subject: [PATCH 038/155] chore: bump maven-enforcer-plugin from 3.0.0 to 3.2.1 Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.0.0 to 3.2.1. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.0.0...enforcer-3.2.1) --- 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] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da38a3e32..5b79ccada 100644 --- a/pom.xml +++ b/pom.xml @@ -575,7 +575,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0 + 3.2.1 restrict-log4j-versions From d7cdfeec2ad660fbbaec3037bfbf00b6abd99215 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 09:32:17 +0000 Subject: [PATCH 039/155] chore: bump webdrivermanager from 4.3.1 to 5.3.2 Bumps [webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 4.3.1 to 5.3.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-4.3.1...webdrivermanager-5.3.2) --- updated-dependencies: - dependency-name: io.github.bonigarcia:webdrivermanager dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5b79ccada..36802f109 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ UTF-8 UTF-8 3.0.15.RELEASE - 4.3.1 + 5.3.2 8080 9090 2.27.2 From 0795ff0fc51a8084f5261ccf218480eb29a1df6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 09:32:09 +0000 Subject: [PATCH 040/155] chore: bump commons-io from 2.6 to 2.11.0 Bumps commons-io from 2.6 to 2.11.0. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 36802f109..6be94ce92 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 3.1.2 3.2.1 - 2.6 + 2.11.0 3.12.0 1.10.0 30.1-jre From cbf2e153d93a9abba89b7ee8e6d18c298f3a767f Mon Sep 17 00:00:00 2001 From: Loris Sierra Date: Tue, 7 Mar 2023 17:37:30 +0100 Subject: [PATCH 041/155] Restrict SSRF Regexes --- src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java | 4 ++-- src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java index 210c98421..3a07664f3 100644 --- a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask1.java @@ -44,12 +44,12 @@ public class SSRFTask1 extends AssignmentEndpoint { try { StringBuilder html = new StringBuilder(); - if (url.matches("images/tom.png")) { + if (url.matches("images/tom\\.png")) { html.append( "\"Tom\""); return failed(this).feedback("ssrf.tom").output(html.toString()).build(); - } else if (url.matches("images/jerry.png")) { + } else if (url.matches("images/jerry\\.png")) { html.append( "\"Jerry\""); diff --git a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java index cb58bd63d..35f9491f7 100644 --- a/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java +++ b/src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java @@ -46,7 +46,7 @@ public class SSRFTask2 extends AssignmentEndpoint { } protected AttackResult furBall(String url) { - if (url.matches("http://ifconfig.pro")) { + if (url.matches("http://ifconfig\\.pro")) { String html; try (InputStream in = new URL(url).openStream()) { html = From d3e2164716c24440a0f20f62370695ff7bd6270d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:53:50 +0200 Subject: [PATCH 042/155] chore: bump asm from 9.1 to 9.5 (#1460) Bumps asm from 9.1 to 9.5. --- updated-dependencies: - dependency-name: org.ow2.asm:asm dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6be94ce92..6f3d75a61 100644 --- a/pom.xml +++ b/pom.xml @@ -154,7 +154,7 @@ org.ow2.asm asm - 9.1 + 9.5 From a43a6125e8d3667d75885b3d6130e4423522d3e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:54:05 +0200 Subject: [PATCH 043/155] chore: bump actions/cache from 3.2.6 to 3.3.1 (#1453) Bumps [actions/cache](https://github.com/actions/cache) from 3.2.6 to 3.3.1. - [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.2.6...v3.3.1) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e2bf031a..00911f6ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.6 + uses: actions/cache@v3.3.1 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a5011282..3a9d5360e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.6 + uses: actions/cache@v3.3.1 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b3344596..44be0e39a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: 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.2.6 + uses: actions/cache@v3.3.1 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} From e720eec5f9658340a8d5759bb033a648d31401ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:54:51 +0200 Subject: [PATCH 044/155] chore: bump jruby from 9.3.6.0 to 9.4.2.0 (#1454) Bumps jruby from 9.3.6.0 to 9.4.2.0. --- updated-dependencies: - dependency-name: org.jruby:jruby dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f3d75a61..a49e54222 100644 --- a/pom.xml +++ b/pom.xml @@ -246,7 +246,7 @@ org.jruby jruby - 9.3.6.0 + 9.4.2.0 From f7b4af502312165d15ef22fa4457e2618f324c89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:55:08 +0200 Subject: [PATCH 045/155] chore: bump bootstrap from 3.3.7 to 5.2.3 (#1441) Bumps [bootstrap](https://github.com/webjars/bootstrap) from 3.3.7 to 5.2.3. - [Release notes](https://github.com/webjars/bootstrap/releases) - [Commits](https://github.com/webjars/bootstrap/compare/bootstrap-3.3.7...bootstrap-5.2.3) --- updated-dependencies: - dependency-name: org.webjars:bootstrap dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a49e54222..e6b080c94 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ 2.5.3 - 3.3.7 + 5.2.3 2.2 3.1.2 From f6855bf6a5ee2d42d55952ae1e20f006f4012dd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:55:22 +0200 Subject: [PATCH 046/155] chore: bump guava from 30.1-jre to 31.1-jre (#1448) Bumps [guava](https://github.com/google/guava) from 30.1-jre to 31.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 ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e6b080c94..060810806 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ 2.11.0 3.12.0 1.10.0 - 30.1-jre + 31.1-jre 0.8.8 17 0.9.1 From ac6de9d78828b7279a6844c4977a84991a0f4353 Mon Sep 17 00:00:00 2001 From: caputdraconis Date: Sat, 1 Apr 2023 16:00:28 +0900 Subject: [PATCH 047/155] Fix typo of HijackSession_content0.adoc --- .../hijacksession/documentation/HijackSession_content0.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc b/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc index 8b260b0da..8df15c7f0 100644 --- a/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc +++ b/src/main/resources/lessons/hijacksession/documentation/HijackSession_content0.adoc @@ -1,4 +1,4 @@ = Hijack a Session -In this lesson we are trying to predict the 'hijack_cookie' value. THe 'hijack_cookie' is used to differentiate authenticated and anonymous users of WebGoat. +In this lesson we are trying to predict the 'hijack_cookie' value. The 'hijack_cookie' is used to differentiate authenticated and anonymous users of WebGoat. From 6eafa45e4cad934fc2ef5e43bed0efc5b4db7197 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 10:10:21 +0000 Subject: [PATCH 048/155] chore: bump jacoco-maven-plugin from 0.8.8 to 0.8.10 Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.8 to 0.8.10. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.8...v0.8.10) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 060810806..04f250aa6 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ 3.12.0 1.10.0 31.1-jre - 0.8.8 + 0.8.10 17 0.9.1 0.9.3 From 61d5fb9ece90b34e9eda09458d54785bb0a1c45f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:43:43 +0200 Subject: [PATCH 049/155] chore: bump jsoup from 1.15.4 to 1.16.1 (#1484) Bumps [jsoup](https://github.com/jhy/jsoup) from 1.15.4 to 1.16.1. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.15.4...jsoup-1.16.1) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04f250aa6..e9b12eaf2 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ 0.9.1 0.9.3 3.5.1 - 1.15.4 + 1.16.1 3.8.0 2.22.0 3.1.2 From 4bc53a66662ddd80e15afe579bd44d0fc1e36283 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 08:54:21 +0200 Subject: [PATCH 050/155] chore: bump maven-checkstyle-plugin from 3.1.2 to 3.2.1 (#1472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.1.2 to 3.2.1. - [Release notes](https://github.com/apache/maven-checkstyle-plugin/releases) - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.1.2...maven-checkstyle-plugin-3.2.1) --- 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Zubcevic --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e9b12eaf2..a99989dea 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ 5.2.3 2.2 - 3.1.2 + 3.2.1 3.2.1 2.11.0 3.12.0 From 9d9fb092becf9b3a9770048ff2e14b333c8b7e38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 09:01:54 +0200 Subject: [PATCH 051/155] chore: bump maven-enforcer-plugin from 3.2.1 to 3.3.0 (#1468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.2.1...enforcer-3.3.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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Zubcevic --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a99989dea..ae45f5439 100644 --- a/pom.xml +++ b/pom.xml @@ -575,7 +575,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.2.1 + 3.3.0 restrict-log4j-versions From 6f0b88f9b6b35c78fecc6aac4812e3e60ddc64ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:06:55 +0200 Subject: [PATCH 052/155] chore: bump cglib-nodep from 2.2 to 3.3.0 (#1470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [cglib-nodep](https://github.com/cglib/cglib) from 2.2 to 3.3.0. - [Release notes](https://github.com/cglib/cglib/releases) - [Commits](https://github.com/cglib/cglib/commits) --- updated-dependencies: - dependency-name: cglib:cglib-nodep dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Zubcevic --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ae45f5439..d20c9b5f3 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ 2.5.3 5.2.3 - 2.2 + 3.3.0 3.2.1 3.2.1 From ff3a2983e244d17bb774e9933fa923f284909c4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:21:17 +0200 Subject: [PATCH 053/155] chore: bump zxcvbn from 1.5.2 to 1.7.0 (#1471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [zxcvbn](https://github.com/nulab/zxcvbn4j) from 1.5.2 to 1.7.0. - [Release notes](https://github.com/nulab/zxcvbn4j/releases) - [Changelog](https://github.com/nulab/zxcvbn4j/blob/master/CHANGELOG.md) - [Commits](https://github.com/nulab/zxcvbn4j/compare/1.5.2...1.7.0) --- updated-dependencies: - dependency-name: com.nulab-inc:zxcvbn dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Zubcevic --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d20c9b5f3..299853095 100644 --- a/pom.xml +++ b/pom.xml @@ -145,7 +145,7 @@ 1.2 1.4.5 - 1.5.2 + 1.7.0 From ca886b481820f873e3d64d2a235ad258b7743440 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Sun, 4 Jun 2023 11:19:47 +0200 Subject: [PATCH 054/155] feat: upgrade to Spring Boot version 3 (#1477) --- Dockerfile | 2 + FAQ.md | 8 +++ pom.xml | 44 +++++++++++---- .../PasswordResetLessonIntegrationTest.java | 2 - .../ProgressRaceConditionIntegrationTest.java | 4 +- .../AjaxAuthenticationEntryPoint.java | 6 +- .../AsciiDoctorTemplateResolver.java | 4 +- .../container/DatabaseConfiguration.java | 5 +- .../webgoat/container/MvcConfiguration.java | 8 +-- .../webgoat/container/WebSecurityConfig.java | 56 +++++++++---------- .../container/asciidoc/WebWolfMacro.java | 2 +- .../assignments/LessonTrackerInterceptor.java | 3 +- .../container/controller/StartLesson.java | 2 +- .../webgoat/container/controller/Welcome.java | 6 +- .../webgoat/container/lessons/Assignment.java | 11 +++- .../LessonConnectionInvocationHandler.java | 4 +- .../container/users/LessonTracker.java | 19 ++++++- .../users/RegistrationController.java | 8 +-- .../webgoat/container/users/UserForm.java | 6 +- .../webgoat/container/users/UserTracker.java | 13 ++++- .../webgoat/container/users/WebGoatUser.java | 6 +- .../lessons/authbypass/VerifyAccount.java | 4 +- .../lessons/challenges/FlagController.java | 14 +---- .../challenges/challenge7/Assignment7.java | 2 +- .../challenges/challenge8/Assignment8.java | 2 +- .../lessons/clientsidefiltering/Salaries.java | 2 +- .../cryptography/EncodingAssignment.java | 2 +- .../cryptography/HashingAssignment.java | 2 +- .../cryptography/SigningAssignment.java | 2 +- .../webgoat/lessons/csrf/CSRFFeedback.java | 4 +- .../webgoat/lessons/csrf/CSRFGetFlag.java | 2 +- .../owasp/webgoat/lessons/csrf/CSRFLogin.java | 2 +- .../webgoat/lessons/csrf/ForgedReviews.java | 2 +- .../HijackSessionAssignment.java | 4 +- .../HttpBasicsInterceptRequest.java | 2 +- .../lessons/idor/IDORViewOtherProfile.java | 2 +- .../webgoat/lessons/jwt/JWTVotesEndpoint.java | 6 +- .../lessons/logging/LogBleedingTask.java | 2 +- .../ResetLinkAssignmentForgotPassword.java | 2 +- .../resetlink/PasswordChangeForm.java | 4 +- .../pathtraversal/ProfileUploadRetrieval.java | 4 +- .../spoofcookie/SpoofCookieAssignment.java | 4 +- .../introduction/SqlInjectionLesson5.java | 2 +- .../introduction/SqlInjectionLesson5b.java | 2 +- .../LandingAssignment.java | 2 +- .../lessons/xss/DOMCrossSiteScripting.java | 2 +- .../owasp/webgoat/lessons/xxe/Comment.java | 6 +- .../webgoat/lessons/xxe/CommentsCache.java | 6 +- .../lessons/xxe/ContentTypeAssignment.java | 5 +- .../owasp/webgoat/lessons/xxe/SimpleXXE.java | 2 +- .../org/owasp/webgoat/webwolf/FileServer.java | 2 +- .../webgoat/webwolf/MvcConfiguration.java | 2 +- .../webgoat/webwolf/WebSecurityConfig.java | 47 +++++++--------- .../org/owasp/webgoat/webwolf/WebWolf.java | 4 +- .../owasp/webgoat/webwolf/mailbox/Email.java | 6 +- .../webwolf/mailbox/MailboxController.java | 2 - .../webgoat/webwolf/requests/LandingPage.java | 2 +- .../webgoat/webwolf/requests/Requests.java | 11 ++-- .../requests/WebWolfTraceRepository.java | 14 ++--- .../webgoat/webwolf/user/WebGoatUser.java | 6 +- .../resources/application-webgoat.properties | 14 +++-- .../resources/application-webwolf.properties | 3 +- src/main/resources/db/container/V3__id.sql | 4 ++ .../lessons/authbypass/html/AuthBypass.html | 8 +-- .../html/BypassRestrictions.html | 6 +- .../lessons/challenges/html/Challenge.html | 2 +- .../lessons/challenges/html/Challenge1.html | 2 +- .../lessons/challenges/html/Challenge5.html | 2 +- .../lessons/challenges/html/Challenge6.html | 2 +- .../lessons/challenges/html/Challenge7.html | 2 +- .../lessons/challenges/html/Challenge8.html | 2 +- .../chromedevtools/html/ChromeDevTools.html | 12 ++-- src/main/resources/lessons/cia/html/CIA.html | 10 ++-- .../html/ClientSideFiltering.html | 6 +- .../cryptography/html/Cryptography.html | 18 +++--- .../resources/lessons/csrf/html/CSRF.html | 20 +++---- .../html/InsecureDeserialization.html | 10 ++-- .../hijacksession/html/HijackSession.html | 4 +- .../lessonSolutions/html/HijackSession.html | 2 +- .../htmltampering/html/HtmlTampering.html | 6 +- .../lessons/httpbasics/html/HttpBasics.html | 6 +- .../lessons/httpproxies/html/HttpProxies.html | 18 +++--- .../resources/lessons/idor/html/IDOR.html | 18 +++--- .../insecurelogin/html/InsecureLogin.html | 4 +- src/main/resources/lessons/jwt/html/JWT.html | 28 +++++----- .../documentation/lesson-template-attack.adoc | 2 +- .../documentation/lesson-template-glue.adoc | 12 ++-- .../lesson-template-video-more.adoc | 6 +- .../lessontemplate/html/LessonTemplate.html | 16 +++--- .../lessons/logging/html/LogSpoofing.html | 10 ++-- .../missingac/html/MissingFunctionAC.html | 8 +-- .../passwordreset/html/PasswordReset.html | 14 ++--- .../pathtraversal/html/PathTraversal.html | 16 +++--- .../securepasswords/html/SecurePasswords.html | 12 ++-- .../lessons/spoofcookie/html/SpoofCookie.html | 4 +- .../lessonSolutions/html/SpoofCookie.html | 4 +- .../sqlinjection/html/SqlInjection.html | 28 +++++----- .../html/SqlInjectionAdvanced.html | 12 ++-- .../html/SqlInjectionMitigations.html | 26 ++++----- .../resources/lessons/ssrf/html/SSRF.html | 8 +-- .../html/VulnerableComponents.html | 28 +++++----- .../html/WebGoatIntroduction.html | 2 +- .../html/WebWolfIntroduction.html | 8 +-- .../lessons/xss/html/CrossSiteScripting.html | 24 ++++---- .../html/CrossSiteScriptingMitigation.html | 12 ++-- .../xss/html/CrossSiteScriptingStored.html | 8 +-- src/main/resources/lessons/xxe/html/XXE.html | 26 ++++----- .../webgoat/templates/lesson_content.html | 2 +- .../resources/webgoat/templates/main_new.html | 2 +- .../resources/webwolf/templates/error.html | 8 +-- .../resources/webwolf/templates/files.html | 4 +- .../webwolf/templates/fragments/footer.html | 4 +- .../resources/webwolf/templates/home.html | 6 +- src/main/resources/webwolf/templates/jwt.html | 6 +- .../resources/webwolf/templates/mailbox.html | 6 +- .../webwolf/templates/registration.html | 6 +- .../resources/webwolf/templates/requests.html | 6 +- .../webwolf/templates/webwolf-login.html | 9 ++- .../webgoat/container/plugins/LessonTest.java | 2 +- .../lessons/csrf/CSRFFeedbackTest.java | 2 +- .../HijackSessionAssignmentTest.java | 2 +- .../lessons/jwt/JWTVotesEndpointTest.java | 4 +- .../SpoofCookieAssignmentTest.java | 2 +- .../xxe/BlindSendFileAssignmentTest.java | 8 +-- .../webgoat/lessons/xxe/SimpleXXETest.java | 7 +-- .../mailbox/MailboxControllerTest.java | 6 +- 126 files changed, 520 insertions(+), 479 deletions(-) create mode 100644 FAQ.md create mode 100644 src/main/resources/db/container/V3__id.sql diff --git a/Dockerfile b/Dockerfile index fdc0f4ff2..5d530644c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,8 @@ ENTRYPOINT [ "java", \ "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \ "--add-opens", "java.base/java.io=ALL-UNNAMED", \ "--add-opens", "java.base/java.util=ALL-UNNAMED", \ + "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", \ + "--add-opens", "java.base/java.io=ALL-UNNAMED", \ "-Drunning.in.docker=true", \ "-Dwebgoat.host=0.0.0.0", \ "-Dwebwolf.host=0.0.0.0", \ diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 000000000..3e2968344 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,8 @@ +# FAQ for development + +## Introduction + +### Integration tests fail + +Try to run the command in the console `java -jar ...` and remove `-Dlogging.pattern.console=` from the command line. + diff --git a/pom.xml b/pom.xml index 299853095..10e64c12a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,13 @@ - - + 4.0.0 org.springframework.boot spring-boot-starter-parent - 2.7.1 + 3.0.5 + org.owasp.webgoat webgoat 2023.5-SNAPSHOT @@ -27,6 +27,7 @@ https://www.gnu.org/licenses/gpl-2.0.txt + mayhew64 @@ -94,7 +95,6 @@ http://lists.owasp.org/pipermail/owasp-webgoat/ - scm:git:git@github.com:WebGoat/WebGoat.git scm:git:git@github.com:WebGoat/WebGoat.git @@ -110,7 +110,8 @@ 2.5.3 - 5.2.3 + + 3.3.7 3.3.0 3.2.1 @@ -121,6 +122,7 @@ 31.1-jre 0.8.10 17 + 2.3.1 0.9.1 0.9.3 3.5.1 @@ -137,7 +139,7 @@ UTF-8 UTF-8 - 3.0.15.RELEASE + 3.1.1.RELEASE 5.3.2 8080 9090 @@ -250,7 +252,6 @@ - org.apache.commons @@ -269,6 +270,7 @@ javax.xml.bind jaxb-api + ${jaxb.version} org.springframework.boot @@ -310,7 +312,11 @@ org.thymeleaf.extras - thymeleaf-extras-springsecurity5 + thymeleaf-extras-springsecurity6 + + + jakarta.servlet + jakarta.servlet-api org.hsqldb @@ -369,8 +375,13 @@ jquery - org.glassfish.jaxb - jaxb-runtime + jakarta.xml.bind + jakarta.xml.bind-api + + + com.sun.xml.bind + jaxb-impl + runtime @@ -386,6 +397,7 @@ com.github.tomakehurst wiremock + 3.0.0-beta-2 test @@ -393,6 +405,11 @@ rest-assured test + + org.springframework.boot + spring-boot-properties-migrator + runtime + @@ -490,7 +507,8 @@ --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED - --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED **/*IntegrationTest.java src/it/java @@ -678,6 +696,10 @@ java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED + --add-opens + java.base/sun.nio.ch=ALL-UNNAMED + --add-opens + java.base/java.io=ALL-UNNAMED ${project.build.directory}/webgoat-${project.version}.jar false diff --git a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java index e791634ea..c53be61f8 100644 --- a/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/PasswordResetLessonIntegrationTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest; import io.restassured.RestAssured; import java.util.Arrays; import java.util.Map; -import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -16,7 +15,6 @@ import org.junit.jupiter.api.TestFactory; public class PasswordResetLessonIntegrationTest extends IntegrationTest { @BeforeEach - @SneakyThrows public void init() { startLesson("/PasswordReset"); } diff --git a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java index 016f6b35d..1228f913c 100644 --- a/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ProgressRaceConditionIntegrationTest.java @@ -29,9 +29,9 @@ public class ProgressRaceConditionIntegrationTest extends IntegrationTest { .relaxedHTTPSValidation() .cookie("JSESSIONID", getWebGoatCookie()) .formParams(Map.of("flag", "test")) - .post(url("/challenge/flag/")); + .post(url("/challenge/flag")); }; - ExecutorService executorService = Executors.newWorkStealingPool(NUMBER_OF_PARALLEL_THREADS); + ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_PARALLEL_THREADS); List> flagCalls = IntStream.range(0, NUMBER_OF_CALLS).mapToObj(i -> call).collect(Collectors.toList()); var responses = executorService.invokeAll(flagCalls); diff --git a/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java b/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java index 1ed96e146..98a3eab0f 100644 --- a/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java +++ b/src/main/java/org/owasp/webgoat/container/AjaxAuthenticationEntryPoint.java @@ -27,10 +27,10 @@ */ package org.owasp.webgoat.container; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; diff --git a/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java index 723e8cb7c..a496a0acb 100644 --- a/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java +++ b/src/main/java/org/owasp/webgoat/container/AsciiDoctorTemplateResolver.java @@ -33,6 +33,7 @@ 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; @@ -41,7 +42,6 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.asciidoctor.Asciidoctor; import org.asciidoctor.extension.JavaExtensionRegistry; @@ -60,7 +60,7 @@ import org.thymeleaf.templateresource.StringTemplateResource; * Thymeleaf resolver for AsciiDoc used in the lesson, can be used as follows inside a lesson file: * *

- *

+ *
*
*/ @Slf4j diff --git a/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java index ef54ff007..65d0b144e 100644 --- a/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java +++ b/src/main/java/org/owasp/webgoat/container/DatabaseConfiguration.java @@ -50,12 +50,13 @@ public class DatabaseConfiguration { } @Bean - public Function flywayLessons(LessonDataSource lessonDataSource) { + public Function flywayLessons() { return schema -> Flyway.configure() .configuration(Map.of("driver", properties.getDriverClassName())) .schemas(schema) - .dataSource(lessonDataSource) + .cleanDisabled(false) + .dataSource(dataSource()) .locations("lessons") .load(); } diff --git a/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java index 114157a90..94353be2f 100644 --- a/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java +++ b/src/main/java/org/owasp/webgoat/container/MvcConfiguration.java @@ -56,10 +56,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; import org.thymeleaf.IEngineConfiguration; -import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; -import org.thymeleaf.spring5.view.ThymeleafViewResolver; +import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring6.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.FileTemplateResolver; import org.thymeleaf.templateresolver.ITemplateResolver; diff --git a/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java index 59084aa2f..3621ce707 100644 --- a/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java +++ b/src/main/java/org/owasp/webgoat/container/WebSecurityConfig.java @@ -37,50 +37,49 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; /** Security configuration for WebGoat. */ @Configuration @AllArgsConstructor @EnableWebSecurity -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { +public class WebSecurityConfig { private final UserService userDetailsService; - @Override - protected void configure(HttpSecurity http) throws Exception { - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = - http.authorizeRequests() - .antMatchers( - "/css/**", - "/images/**", - "/js/**", - "fonts/**", - "/plugins/**", - "/registration", - "/register.mvc", - "/actuator/**") - .permitAll() - .anyRequest() - .authenticated(); - security - .and() - .formLogin() + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests( + auth -> + auth.requestMatchers( + "/css/**", + "/images/**", + "/js/**", + "fonts/**", + "/plugins/**", + "/registration", + "/register.mvc", + "/actuator/**") + .permitAll() + .anyRequest() + .authenticated()); + http.formLogin() .loginPage("/login") .defaultSuccessUrl("/welcome.mvc", true) .usernameParameter("username") .passwordParameter("password") .permitAll(); - security.and().logout().deleteCookies("JSESSIONID").invalidateHttpSession(true); - security.and().csrf().disable(); + http.logout().deleteCookies("JSESSIONID").invalidateHttpSession(true); + http.csrf().disable(); http.headers().cacheControl().disable(); http.exceptionHandling().authenticationEntryPoint(new AjaxAuthenticationEntryPoint("/login")); + return http.build(); } @Autowired @@ -89,15 +88,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } @Bean - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { + public UserDetailsService userDetailsServiceBean() { return userDetailsService; } - @Override @Bean - protected AuthenticationManager authenticationManager() throws Exception { - return super.authenticationManager(); + public AuthenticationManager authenticationManager( + AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); } @SuppressWarnings("deprecation") diff --git a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java index 9ab0fac86..8456d6dbe 100644 --- a/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java +++ b/src/main/java/org/owasp/webgoat/container/asciidoc/WebWolfMacro.java @@ -1,8 +1,8 @@ package org.owasp.webgoat.container.asciidoc; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import org.asciidoctor.ast.ContentNode; import org.asciidoctor.extension.InlineMacroProcessor; import org.springframework.web.context.request.RequestContextHolder; diff --git a/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java index 4e76af9d6..aa3cd40ce 100644 --- a/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java +++ b/src/main/java/org/owasp/webgoat/container/assignments/LessonTrackerInterceptor.java @@ -75,7 +75,8 @@ public class LessonTrackerInterceptor implements ResponseBodyAdvice { } else { userTracker.assignmentFailed(webSession.getCurrentLesson()); } - userTrackerRepository.saveAndFlush(userTracker); + userTrackerRepository.save(userTracker); + return attackResult; } } diff --git a/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java index 7d94f6044..3cdd5e8d6 100644 --- a/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java +++ b/src/main/java/org/owasp/webgoat/container/controller/StartLesson.java @@ -31,7 +31,7 @@ */ package org.owasp.webgoat.container.controller; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.session.Course; import org.owasp.webgoat.container.session.WebSession; import org.springframework.stereotype.Controller; diff --git a/src/main/java/org/owasp/webgoat/container/controller/Welcome.java b/src/main/java/org/owasp/webgoat/container/controller/Welcome.java index fddc5f640..0bebc9e70 100644 --- a/src/main/java/org/owasp/webgoat/container/controller/Welcome.java +++ b/src/main/java/org/owasp/webgoat/container/controller/Welcome.java @@ -29,8 +29,8 @@ */ package org.owasp.webgoat.container.controller; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.ModelAndView; @@ -49,7 +49,7 @@ public class Welcome { /** * welcome. * - * @param request a {@link javax.servlet.http.HttpServletRequest} object. + * @param request a {@link jakarta.servlet.http.HttpServletRequest} object. * @return a {@link org.springframework.web.servlet.ModelAndView} object. */ @GetMapping(path = {"welcome.mvc"}) diff --git a/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java index 92e8d0e9e..3c3c89d6d 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/Assignment.java @@ -1,9 +1,14 @@ package org.owasp.webgoat.container.lessons; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; import java.util.ArrayList; import java.util.List; -import javax.persistence.*; -import lombok.*; +import lombok.EqualsAndHashCode; +import lombok.Getter; /** * ************************************************************************************************ @@ -41,7 +46,7 @@ import lombok.*; public class Assignment { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; diff --git a/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java b/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java index 3b90c963d..a8f586d56 100644 --- a/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java +++ b/src/main/java/org/owasp/webgoat/container/lessons/LessonConnectionInvocationHandler.java @@ -4,15 +4,13 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; -import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.users.WebGoatUser; import org.springframework.security.core.context.SecurityContextHolder; /** * Handler which sets the correct schema for the currently bounded user. This way users are not - * seeing each other data and we can reset data for just one particular user. + * seeing each other data, and we can reset data for just one particular user. */ -@Slf4j public class LessonConnectionInvocationHandler implements InvocationHandler { private final Connection targetConnection; diff --git a/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java b/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java index 2cc0c58af..fd9af4dcf 100644 --- a/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java +++ b/src/main/java/org/owasp/webgoat/container/users/LessonTracker.java @@ -1,8 +1,20 @@ package org.owasp.webgoat.container.users; -import java.util.*; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Version; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; -import javax.persistence.*; +import lombok.EqualsAndHashCode; import lombok.Getter; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Lesson; @@ -39,10 +51,11 @@ import org.owasp.webgoat.container.lessons.Lesson; * @since October 29, 2003 */ @Entity +@EqualsAndHashCode public class LessonTracker { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Getter private String lessonName; diff --git a/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java b/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java index 4dc628f86..1678385de 100644 --- a/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java +++ b/src/main/java/org/owasp/webgoat/container/users/RegistrationController.java @@ -1,11 +1,10 @@ package org.owasp.webgoat.container.users; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.validation.Valid; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; @@ -23,7 +22,6 @@ public class RegistrationController { private UserValidator userValidator; private UserService userService; - private AuthenticationManager authenticationManager; @GetMapping("/registration") public String showForm(UserForm userForm) { diff --git a/src/main/java/org/owasp/webgoat/container/users/UserForm.java b/src/main/java/org/owasp/webgoat/container/users/UserForm.java index 416bba094..d0fad3626 100644 --- a/src/main/java/org/owasp/webgoat/container/users/UserForm.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserForm.java @@ -1,8 +1,8 @@ package org.owasp.webgoat.container.users; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/org/owasp/webgoat/container/users/UserTracker.java b/src/main/java/org/owasp/webgoat/container/users/UserTracker.java index 86bdf4c14..72450f69e 100644 --- a/src/main/java/org/owasp/webgoat/container/users/UserTracker.java +++ b/src/main/java/org/owasp/webgoat/container/users/UserTracker.java @@ -1,11 +1,19 @@ package org.owasp.webgoat.container.users; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +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 javax.persistence.*; +import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.lessons.Assignment; import org.owasp.webgoat.container.lessons.Lesson; @@ -43,10 +51,11 @@ import org.owasp.webgoat.container.lessons.Lesson; */ @Slf4j @Entity +@EqualsAndHashCode public class UserTracker { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "username") diff --git a/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java b/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java index 517e50a60..bbeec3b98 100644 --- a/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java +++ b/src/main/java/org/owasp/webgoat/container/users/WebGoatUser.java @@ -1,10 +1,10 @@ package org.owasp.webgoat.container.users; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; import java.util.Collection; import java.util.Collections; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Transient; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; diff --git a/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java index ed7988b13..761d40aa0 100644 --- a/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java +++ b/src/main/java/org/owasp/webgoat/lessons/authbypass/VerifyAccount.java @@ -22,13 +22,13 @@ package org.owasp.webgoat.lessons.authbypass; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java index 1b2c497bd..5e423cecd 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java @@ -26,8 +26,6 @@ import lombok.AllArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -38,25 +36,17 @@ import org.springframework.web.bind.annotation.RestController; @AllArgsConstructor public class FlagController extends AssignmentEndpoint { - private final UserTrackerRepository userTrackerRepository; private final WebSession webSession; private final Flags flags; @PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public AttackResult postFlag(@RequestParam String flag) { - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson()); - final AttackResult attackResult; if (expectedFlag.isCorrect(flag)) { - userTracker.assignmentSolved( - webSession.getCurrentLesson(), "Assignment" + expectedFlag.number()); - attackResult = success(this).feedback("challenge.flag.correct").build(); + return success(this).feedback("challenge.flag.correct").build(); } else { - userTracker.assignmentFailed(webSession.getCurrentLesson()); - attackResult = failed(this).feedback("challenge.flag.incorrect").build(); + return failed(this).feedback("challenge.flag.incorrect").build(); } - userTrackerRepository.save(userTracker); - return attackResult; } } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java index 31260e8e1..a641bff28 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java @@ -1,9 +1,9 @@ package org.owasp.webgoat.lessons.challenges.challenge7; +import jakarta.servlet.http.HttpServletRequest; import java.net.URI; import java.net.URISyntaxException; import java.time.LocalDateTime; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java index 507b7b4bd..6623ea1a0 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java @@ -1,9 +1,9 @@ package org.owasp.webgoat.lessons.challenges.challenge8; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; diff --git a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java index bd4de62fc..9f5b42b32 100644 --- a/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java +++ b/src/main/java/org/owasp/webgoat/lessons/clientsidefiltering/Salaries.java @@ -22,6 +22,7 @@ package org.owasp.webgoat.lessons.clientsidefiltering; +import jakarta.annotation.PostConstruct; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -31,7 +32,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.PostConstruct; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java index 65c115c41..437e89959 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/EncodingAssignment.java @@ -22,9 +22,9 @@ package org.owasp.webgoat.lessons.cryptography; +import jakarta.servlet.http.HttpServletRequest; import java.util.Base64; import java.util.Random; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.MediaType; diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java index b83f931a8..266c53ffa 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/HashingAssignment.java @@ -22,10 +22,10 @@ package org.owasp.webgoat.lessons.cryptography; +import jakarta.servlet.http.HttpServletRequest; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; -import javax.servlet.http.HttpServletRequest; import javax.xml.bind.DatatypeConverter; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java index 382ee3b16..ffcb739a5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/cryptography/SigningAssignment.java @@ -22,11 +22,11 @@ package org.owasp.webgoat.lessons.cryptography; +import jakarta.servlet.http.HttpServletRequest; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPublicKey; -import javax.servlet.http.HttpServletRequest; import javax.xml.bind.DatatypeConverter; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java index a5387efd0..4f4beb91a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFFeedback.java @@ -24,11 +24,11 @@ package org.owasp.webgoat.lessons.csrf; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Map; import java.util.UUID; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java index e2cbc90c7..2a929817b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFGetFlag.java @@ -22,10 +22,10 @@ package org.owasp.webgoat.lessons.csrf; +import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import java.util.Random; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.i18n.PluginMessages; import org.owasp.webgoat.container.session.UserSessionData; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java index 08d226245..e41409457 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/CSRFLogin.java @@ -22,7 +22,7 @@ package org.owasp.webgoat.lessons.csrf; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java index c11d43c5e..e82a46cc7 100644 --- a/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java +++ b/src/main/java/org/owasp/webgoat/lessons/csrf/ForgedReviews.java @@ -25,6 +25,7 @@ package org.owasp.webgoat.lessons.csrf; import static org.springframework.http.MediaType.ALL_VALUE; import com.google.common.collect.Lists; +import jakarta.servlet.http.HttpServletRequest; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -32,7 +33,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java index 00416b964..8fae4e89d 100644 --- a/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/hijacksession/HijackSessionAssignment.java @@ -22,8 +22,8 @@ package org.owasp.webgoat.lessons.hijacksession; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java index b3ad85e95..7330c747b 100644 --- a/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java +++ b/src/main/java/org/owasp/webgoat/lessons/httpproxies/HttpBasicsInterceptRequest.java @@ -22,7 +22,7 @@ package org.owasp.webgoat.lessons.httpproxies; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.http.HttpMethod; diff --git a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java index f216cb580..b4e8a3cbd 100644 --- a/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java +++ b/src/main/java/org/owasp/webgoat/lessons/idor/IDORViewOtherProfile.java @@ -22,9 +22,9 @@ package org.owasp.webgoat.lessons.idor; +import jakarta.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletResponse; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java index 02a935498..e1ac1a0d2 100644 --- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java +++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java @@ -31,14 +31,14 @@ import io.jsonwebtoken.Jwt; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.impl.TextCodec; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import java.time.Duration; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; -import javax.annotation.PostConstruct; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java index 710f22f1a..a338407bf 100644 --- a/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java +++ b/src/main/java/org/owasp/webgoat/lessons/logging/LogBleedingTask.java @@ -22,10 +22,10 @@ package org.owasp.webgoat.lessons.logging; +import jakarta.annotation.PostConstruct; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; -import javax.annotation.PostConstruct; import org.apache.logging.log4j.util.Strings; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java index 34b8ee856..c53931418 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/ResetLinkAssignmentForgotPassword.java @@ -22,8 +22,8 @@ package org.owasp.webgoat.lessons.passwordreset; +import jakarta.servlet.http.HttpServletRequest; import java.util.UUID; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java b/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java index 604c51fd3..4601cc78a 100644 --- a/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java +++ b/src/main/java/org/owasp/webgoat/lessons/passwordreset/resetlink/PasswordChangeForm.java @@ -1,7 +1,7 @@ package org.owasp.webgoat.lessons.passwordreset.resetlink; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java index f52bed34a..402945f12 100644 --- a/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java +++ b/src/main/java/org/owasp/webgoat/lessons/pathtraversal/ProfileUploadRetrieval.java @@ -1,5 +1,7 @@ package org.owasp.webgoat.lessons.pathtraversal; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -8,8 +10,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.util.Base64; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; diff --git a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java index 2efc739f6..a943cc7b5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/spoofcookie/SpoofCookieAssignment.java @@ -22,9 +22,9 @@ package org.owasp.webgoat.lessons.spoofcookie; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import java.util.Map; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java index 32db401fa..9678a2f9d 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5.java @@ -22,11 +22,11 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; +import jakarta.annotation.PostConstruct; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import javax.annotation.PostConstruct; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java index 20225384f..d8cecf291 100644 --- a/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java +++ b/src/main/java/org/owasp/webgoat/lessons/sqlinjection/introduction/SqlInjectionLesson5b.java @@ -22,9 +22,9 @@ package org.owasp.webgoat.lessons.sqlinjection.introduction; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.sql.*; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AssignmentHints; diff --git a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java index c6e9e0493..63764adea 100644 --- a/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/webwolfintroduction/LandingAssignment.java @@ -22,9 +22,9 @@ package org.owasp.webgoat.lessons.webwolfintroduction; +import jakarta.servlet.http.HttpServletRequest; import java.net.URI; import java.net.URISyntaxException; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; diff --git a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java index 11da6ea19..e7df0a4ed 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java +++ b/src/main/java/org/owasp/webgoat/lessons/xss/DOMCrossSiteScripting.java @@ -22,8 +22,8 @@ package org.owasp.webgoat.lessons.xss; +import jakarta.servlet.http.HttpServletRequest; import java.security.SecureRandom; -import javax.servlet.http.HttpServletRequest; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.container.session.UserSessionData; diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java index 90d06fdd1..12b7516b5 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/Comment.java @@ -22,7 +22,8 @@ package org.owasp.webgoat.lessons.xxe; -import javax.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -37,7 +38,8 @@ import lombok.ToString; @Setter @AllArgsConstructor @NoArgsConstructor -@XmlRootElement +@XmlRootElement(name = "comment") +@XmlType @ToString public class Comment { private String user; diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java index b949f0abe..e8abf3bd3 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/CommentsCache.java @@ -26,6 +26,8 @@ import static java.util.Optional.empty; import static java.util.Optional.of; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBException; import java.io.IOException; import java.io.StringReader; import java.time.LocalDateTime; @@ -36,8 +38,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.xml.XMLConstants; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.owasp.webgoat.container.session.WebSession; @@ -93,7 +93,7 @@ public class CommentsCache { * progress etc). In real life the XmlMapper bean defined above will be used automatically and the * Comment class can be directly used in the controller method (instead of a String) */ - protected Comment parseXml(String xml) throws JAXBException, XMLStreamException { + protected Comment parseXml(String xml) throws XMLStreamException, JAXBException { var jc = JAXBContext.newInstance(Comment.class); var xif = XMLInputFactory.newInstance(); diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java index 2e54dc1d8..4555fcd72 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/ContentTypeAssignment.java @@ -24,7 +24,7 @@ package org.owasp.webgoat.lessons.xxe; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; @@ -60,8 +60,7 @@ public class ContentTypeAssignment extends AssignmentEndpoint { public AttackResult createNewUser( HttpServletRequest request, @RequestBody String commentStr, - @RequestHeader("Content-Type") String contentType) - throws Exception { + @RequestHeader("Content-Type") String contentType) { AttackResult attackResult = failed(this).build(); if (APPLICATION_JSON_VALUE.equals(contentType)) { diff --git a/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java index d51712cd4..53638e0d8 100644 --- a/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java +++ b/src/main/java/org/owasp/webgoat/lessons/xxe/SimpleXXE.java @@ -25,7 +25,7 @@ package org.owasp.webgoat.lessons.xxe; import static org.springframework.http.MediaType.ALL_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.exec.OS; import org.apache.commons.lang3.exception.ExceptionUtils; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; diff --git a/src/main/java/org/owasp/webgoat/webwolf/FileServer.java b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java index a23af4ce7..5eda1ca2b 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/FileServer.java +++ b/src/main/java/org/owasp/webgoat/webwolf/FileServer.java @@ -24,10 +24,10 @@ package org.owasp.webgoat.webwolf; import static org.springframework.http.MediaType.ALL_VALUE; +import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import javax.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java b/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java index f5fec0777..3c267bd32 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java +++ b/src/main/java/org/owasp/webgoat/webwolf/MvcConfiguration.java @@ -22,8 +22,8 @@ package org.owasp.webgoat.webwolf; +import jakarta.annotation.PostConstruct; import java.io.File; -import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; diff --git a/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java index 740a34856..64f6758c7 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java +++ b/src/main/java/org/owasp/webgoat/webwolf/WebSecurityConfig.java @@ -29,54 +29,49 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; -/** Security configuration for WebGoat. */ +/** Security configuration for WebWolf. */ @Configuration @AllArgsConstructor @EnableWebSecurity -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { +public class WebSecurityConfig { private final UserService userDetailsService; - @Override - protected void configure(HttpSecurity http) throws Exception { - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry security = - http.authorizeRequests() - .antMatchers(HttpMethod.POST, "/fileupload") - .authenticated() - .antMatchers(HttpMethod.GET, "/files", "/mail", "/requests") - .authenticated() - .and() - .authorizeRequests() - .anyRequest() - .permitAll(); - - security.and().csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true"); - security.and().formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll(); - security.and().logout().permitAll(); + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests( + auth -> auth.requestMatchers(HttpMethod.POST, "/fileupload").authenticated()); + http.authorizeHttpRequests( + auth -> + auth.requestMatchers(HttpMethod.GET, "/files", "/mail", "/requests").authenticated()); + http.authorizeHttpRequests().anyRequest().permitAll(); + http.csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true"); + http.formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll(); + http.logout().permitAll(); + return http.build(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userDetailsService); // .passwordEncoder(bCryptPasswordEncoder()); + auth.userDetailsService(userDetailsService); } @Bean - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { + public UserDetailsService userDetailsServiceBean() { return userDetailsService; } - @Override @Bean - protected AuthenticationManager authenticationManager() throws Exception { - return super.authenticationManager(); + public AuthenticationManager authenticationManager( + AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); } @Bean diff --git a/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java index fa5d488a3..395f69d36 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java +++ b/src/main/java/org/owasp/webgoat/webwolf/WebWolf.java @@ -23,7 +23,7 @@ package org.owasp.webgoat.webwolf; import org.owasp.webgoat.webwolf.requests.WebWolfTraceRepository; -import org.springframework.boot.actuate.trace.http.HttpTraceRepository; +import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -37,7 +37,7 @@ import org.springframework.context.annotation.PropertySource; public class WebWolf { @Bean - public HttpTraceRepository traceRepository() { + public HttpExchangeRepository traceRepository() { return new WebWolfTraceRepository(); } } diff --git a/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java index 4cca7856b..dac61e427 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/Email.java @@ -23,10 +23,14 @@ package org.owasp.webgoat.webwolf.mailbox; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import java.io.Serializable; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import javax.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; diff --git a/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java index e7a42214f..fb1bde0e5 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java +++ b/src/main/java/org/owasp/webgoat/webwolf/mailbox/MailboxController.java @@ -24,7 +24,6 @@ package org.owasp.webgoat.webwolf.mailbox; import java.util.List; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; @@ -38,7 +37,6 @@ import org.springframework.web.servlet.ModelAndView; @RestController @RequiredArgsConstructor -@Slf4j public class MailboxController { private final MailboxRepository mailboxRepository; diff --git a/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java b/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java index 6d46c014f..7bdcc1006 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/LandingPage.java @@ -22,8 +22,8 @@ package org.owasp.webgoat.webwolf.requests; +import jakarta.servlet.http.HttpServletRequest; import java.util.concurrent.Callable; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; diff --git a/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java b/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java index f510ed7e9..5effa524e 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/Requests.java @@ -32,8 +32,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.boot.actuate.trace.http.HttpTrace; -import org.springframework.boot.actuate.trace.http.HttpTrace.Request; +import org.springframework.boot.actuate.web.exchanges.HttpExchange; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; @@ -78,8 +77,8 @@ public class Requests { return model; } - private boolean allowedTrace(HttpTrace t, UserDetails user) { - Request req = t.getRequest(); + private boolean allowedTrace(HttpExchange t, UserDetails user) { + HttpExchange.Request req = t.getRequest(); boolean allowed = true; /* do not show certain traces to other users in a classroom setup */ if (req.getUri().getPath().contains("/files") @@ -95,11 +94,11 @@ public class Requests { return allowed; } - private String path(HttpTrace t) { + private String path(HttpExchange t) { return (String) t.getRequest().getUri().getPath(); } - private String toJsonString(HttpTrace t) { + private String toJsonString(HttpExchange t) { try { return objectMapper.writeValueAsString(t); } catch (JsonProcessingException e) { diff --git a/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java b/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java index bba73a890..ceff13923 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java +++ b/src/main/java/org/owasp/webgoat/webwolf/requests/WebWolfTraceRepository.java @@ -26,8 +26,8 @@ import com.google.common.collect.EvictingQueue; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.actuate.trace.http.HttpTrace; -import org.springframework.boot.actuate.trace.http.HttpTraceRepository; +import org.springframework.boot.actuate.web.exchanges.HttpExchange; +import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository; /** * Keep track of all the incoming requests, we are only keeping track of request originating from @@ -37,9 +37,9 @@ import org.springframework.boot.actuate.trace.http.HttpTraceRepository; * @since 8/13/17. */ @Slf4j -public class WebWolfTraceRepository implements HttpTraceRepository { +public class WebWolfTraceRepository implements HttpExchangeRepository { - private final EvictingQueue traces = EvictingQueue.create(10000); + private final EvictingQueue traces = EvictingQueue.create(10000); private final List exclusionList = List.of( "/tmpdir", @@ -54,11 +54,11 @@ public class WebWolfTraceRepository implements HttpTraceRepository { "/mail"); @Override - public List findAll() { + public List findAll() { return List.of(); } - public List findAllTraces() { + public List findAllTraces() { return new ArrayList<>(traces); } @@ -67,7 +67,7 @@ public class WebWolfTraceRepository implements HttpTraceRepository { } @Override - public void add(HttpTrace httpTrace) { + public void add(HttpExchange httpTrace) { var path = httpTrace.getRequest().getUri().getPath(); if (!isInExclusionList(path)) { traces.add(httpTrace); diff --git a/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java b/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java index d432ff925..35f7dd92f 100644 --- a/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java +++ b/src/main/java/org/owasp/webgoat/webwolf/user/WebGoatUser.java @@ -22,11 +22,11 @@ package org.owasp.webgoat.webwolf.user; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; import java.util.Collection; import java.util.Collections; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Transient; import lombok.Getter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; diff --git a/src/main/resources/application-webgoat.properties b/src/main/resources/application-webgoat.properties index cd217395c..186c62690 100644 --- a/src/main/resources/application-webgoat.properties +++ b/src/main/resources/application-webgoat.properties @@ -13,11 +13,12 @@ server.ssl.key-store-password=${WEBGOAT_KEYSTORE_PASSWORD:password} server.ssl.key-alias=${WEBGOAT_KEY_ALIAS:goat} server.ssl.enabled=${WEBGOAT_SSLENABLED:false} -spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect -spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.jpa.properties.hibernate.default_schema=CONTAINER spring.banner.location=classpath:banner.txt +spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat +spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver +spring.jpa.open-in-view=false +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect +spring.jpa.properties.hibernate.default_schema=CONTAINER logging.level.org.thymeleaf=INFO logging.level.org.thymeleaf.TemplateEngine.CONFIG=INFO @@ -28,6 +29,7 @@ logging.level.org.springframework=INFO logging.level.org.springframework.boot.devtools=INFO logging.level.org.owasp=DEBUG logging.level.org.owasp.webgoat=DEBUG +logging.level.org.hidbernate.SQL=DEBUG webgoat.server.directory=${user.home}/.webgoat-${webgoat.build.version}/ webgoat.user.directory=${user.home}/.webgoat-${webgoat.build.version}/ @@ -51,11 +53,11 @@ spring.jackson.serialization.write-dates-as-timestamps=false #For static file refresh ... and faster dev :D spring.devtools.restart.additional-paths=webgoat-container/src/main/resources/static/js,webgoat-container/src/main/resources/static/css -exclude.categories=${EXCLUDE_CATEGORIES:none,none} #exclude based on the enum of the Category +exclude.categories=${EXCLUDE_CATEGORIES:none,none} -exclude.lessons=${EXCLUDE_LESSONS:none,none} #exclude based on the class name of a lesson e.g.: LessonTemplate +exclude.lessons=${EXCLUDE_LESSONS:none,none} management.health.db.enabled=true management.endpoint.health.show-details=always diff --git a/src/main/resources/application-webwolf.properties b/src/main/resources/application-webwolf.properties index eedc0599b..7d7bef3d1 100644 --- a/src/main/resources/application-webwolf.properties +++ b/src/main/resources/application-webwolf.properties @@ -18,6 +18,7 @@ spring.datasource.url=jdbc:hsqldb:file:${webgoat.server.directory}/webgoat spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.properties.hibernate.default_schema=CONTAINER +spring.jpa.open-in-view=false spring.messages.basename=i18n/messages spring.jmx.enabled=false @@ -26,7 +27,7 @@ logging.level.org.springframework.boot.devtools=WARN logging.level.org.owasp=DEBUG logging.level.org.owasp.webwolf=TRACE -management.trace.http.include=REQUEST_HEADERS,RESPONSE_HEADERS,COOKIE_HEADERS,TIME_TAKEN +management.httpexchanges.recording.include=REQUEST_HEADERS,RESPONSE_HEADERS,COOKIE_HEADERS,TIME_TAKEN management.endpoint.httptrace.enabled=true spring.thymeleaf.cache=false diff --git a/src/main/resources/db/container/V3__id.sql b/src/main/resources/db/container/V3__id.sql new file mode 100644 index 000000000..2787eed56 --- /dev/null +++ b/src/main/resources/db/container/V3__id.sql @@ -0,0 +1,4 @@ +ALTER TABLE CONTAINER.ASSIGNMENT ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); +ALTER TABLE CONTAINER.LESSON_TRACKER ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); +ALTER TABLE CONTAINER.USER_TRACKER ALTER COLUMN ID SET GENERATED BY DEFAULT AS IDENTITY(START WITH 1); + diff --git a/src/main/resources/lessons/authbypass/html/AuthBypass.html b/src/main/resources/lessons/authbypass/html/AuthBypass.html index 914bd2064..1630a5981 100644 --- a/src/main/resources/lessons/authbypass/html/AuthBypass.html +++ b/src/main/resources/lessons/authbypass/html/AuthBypass.html @@ -4,14 +4,14 @@ -
+
-
+
@@ -72,9 +72,9 @@ - + - + diff --git a/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html index 4c506a09f..de38da671 100755 --- a/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html +++ b/src/main/resources/lessons/bypassrestrictions/html/BypassRestrictions.html @@ -6,12 +6,12 @@ -
+
-
+
@@ -59,7 +59,7 @@
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge.html b/src/main/resources/lessons/challenges/html/Challenge.html index 713d902f3..5a03f3da8 100644 --- a/src/main/resources/lessons/challenges/html/Challenge.html +++ b/src/main/resources/lessons/challenges/html/Challenge.html @@ -3,7 +3,7 @@
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge1.html b/src/main/resources/lessons/challenges/html/Challenge1.html index f69942f38..2d9e95114 100644 --- a/src/main/resources/lessons/challenges/html/Challenge1.html +++ b/src/main/resources/lessons/challenges/html/Challenge1.html @@ -3,7 +3,7 @@
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge5.html b/src/main/resources/lessons/challenges/html/Challenge5.html index 9a6f42348..25ace8fc6 100644 --- a/src/main/resources/lessons/challenges/html/Challenge5.html +++ b/src/main/resources/lessons/challenges/html/Challenge5.html @@ -4,7 +4,7 @@
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge6.html b/src/main/resources/lessons/challenges/html/Challenge6.html index 1a906c0a6..018857871 100644 --- a/src/main/resources/lessons/challenges/html/Challenge6.html +++ b/src/main/resources/lessons/challenges/html/Challenge6.html @@ -4,7 +4,7 @@
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge7.html b/src/main/resources/lessons/challenges/html/Challenge7.html index dec4331b1..618db5817 100644 --- a/src/main/resources/lessons/challenges/html/Challenge7.html +++ b/src/main/resources/lessons/challenges/html/Challenge7.html @@ -12,7 +12,7 @@ f94008f801fceb8833a30fe56a8b26976347edcf First version of WebGoat Cloud website
-
+
diff --git a/src/main/resources/lessons/challenges/html/Challenge8.html b/src/main/resources/lessons/challenges/html/Challenge8.html index 989977d2d..c79bcc833 100644 --- a/src/main/resources/lessons/challenges/html/Challenge8.html +++ b/src/main/resources/lessons/challenges/html/Challenge8.html @@ -3,7 +3,7 @@
-
+
diff --git a/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html b/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html index c83603964..1ce04f190 100644 --- a/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html +++ b/src/main/resources/lessons/chromedevtools/html/ChromeDevTools.html @@ -4,22 +4,22 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -23,7 +23,7 @@ -
+
diff --git a/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html b/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html index 18d965c66..a863a0edc 100644 --- a/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html +++ b/src/main/resources/lessons/clientsidefiltering/html/ClientSideFiltering.html @@ -2,10 +2,10 @@
-
+
-
+

@@ -74,7 +74,7 @@
-
+
diff --git a/src/main/resources/lessons/cryptography/html/Cryptography.html b/src/main/resources/lessons/cryptography/html/Cryptography.html index 6e6f32767..19438ef6c 100644 --- a/src/main/resources/lessons/cryptography/html/Cryptography.html +++ b/src/main/resources/lessons/cryptography/html/Cryptography.html @@ -18,11 +18,11 @@ $(document).ready(initialise);
-
+
-
+
@@ -41,7 +41,7 @@ $(document).ready(initialise);
-
+
@@ -58,7 +58,7 @@ $(document).ready(initialise);
-
+
@@ -76,12 +76,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -101,12 +101,12 @@ $(document).ready(initialise);
-
+
-
+
@@ -123,7 +123,7 @@ $(document).ready(initialise);
-
+
diff --git a/src/main/resources/lessons/csrf/html/CSRF.html b/src/main/resources/lessons/csrf/html/CSRF.html index 01fdb696c..61a6029b3 100644 --- a/src/main/resources/lessons/csrf/html/CSRF.html +++ b/src/main/resources/lessons/csrf/html/CSRF.html @@ -3,15 +3,15 @@
-
+
-
+
-
+
-
+
@@ -54,7 +54,7 @@
-
+
@@ -121,15 +121,15 @@
-
+
-
+
-
+
-
+
@@ -251,7 +251,7 @@
-
+
diff --git a/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html index 1b64172f4..43b58e9dd 100755 --- a/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html +++ b/src/main/resources/lessons/deserialization/html/InsecureDeserialization.html @@ -3,24 +3,24 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html b/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html index ac8ab94d5..4d5022d0a 100644 --- a/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html +++ b/src/main/resources/lessons/hijacksession/lessonSolutions/html/HijackSession.html @@ -7,7 +7,7 @@
-
+
diff --git a/src/main/resources/lessons/htmltampering/html/HtmlTampering.html b/src/main/resources/lessons/htmltampering/html/HtmlTampering.html index c40fdd68c..859de642c 100755 --- a/src/main/resources/lessons/htmltampering/html/HtmlTampering.html +++ b/src/main/resources/lessons/htmltampering/html/HtmlTampering.html @@ -3,12 +3,12 @@
-
+
-
+
-
+
diff --git a/src/main/resources/lessons/httpbasics/html/HttpBasics.html b/src/main/resources/lessons/httpbasics/html/HttpBasics.html index e3dcc79c0..2171590e4 100644 --- a/src/main/resources/lessons/httpbasics/html/HttpBasics.html +++ b/src/main/resources/lessons/httpbasics/html/HttpBasics.html @@ -6,13 +6,13 @@ -
+
-
+
@@ -42,7 +42,7 @@ -
+
diff --git a/src/main/resources/lessons/httpproxies/html/HttpProxies.html b/src/main/resources/lessons/httpproxies/html/HttpProxies.html index 3b96be434..25ba2cc70 100644 --- a/src/main/resources/lessons/httpproxies/html/HttpProxies.html +++ b/src/main/resources/lessons/httpproxies/html/HttpProxies.html @@ -3,23 +3,23 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/main/resources/lessons/idor/html/IDOR.html b/src/main/resources/lessons/idor/html/IDOR.html index b4b7f530f..9547fb00f 100644 --- a/src/main/resources/lessons/idor/html/IDOR.html +++ b/src/main/resources/lessons/idor/html/IDOR.html @@ -4,14 +4,14 @@ -
+
-
+
@@ -46,7 +46,7 @@ -
+
@@ -76,7 +76,7 @@ -
+
-
+
@@ -108,7 +108,7 @@ -
+
@@ -123,7 +123,7 @@ -
+
@@ -147,7 +147,7 @@
-
+
@@ -176,7 +176,7 @@ -
+
diff --git a/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html b/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html index a150a2f1c..30e04e4c7 100755 --- a/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html +++ b/src/main/resources/lessons/insecurelogin/html/InsecureLogin.html @@ -6,12 +6,12 @@ -
+
-
+
diff --git a/src/main/resources/lessons/jwt/html/JWT.html b/src/main/resources/lessons/jwt/html/JWT.html index fdf7a5fa6..c210971a2 100644 --- a/src/main/resources/lessons/jwt/html/JWT.html +++ b/src/main/resources/lessons/jwt/html/JWT.html @@ -3,14 +3,14 @@
-
+
-
+
-
+
@@ -35,10 +35,10 @@
-
+
-
+
@@ -102,7 +102,7 @@
-
+
@@ -112,7 +112,7 @@ -
+
@@ -134,18 +134,18 @@
-
+
-
+
-
+

 
@@ -173,11 +173,11 @@
 
-
+
-
+
@@ -299,7 +299,7 @@
-
+
@@ -359,7 +359,7 @@
-
+
diff --git a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc index 2be501c4f..8342ffd9f 100644 --- a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-attack.adoc @@ -82,7 +82,7 @@ green when the user solves the assignment. To make this work we need to add to t [source] ----
-
+
-
+
-
+
-
+
---- diff --git a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc index eb6502f36..b3c1fa7a5 100644 --- a/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc +++ b/src/main/resources/lessons/lessontemplate/documentation/lesson-template-video-more.adoc @@ -5,7 +5,7 @@ You can include multiple adoc files in one page, by including them in the same ` [source] ----
-
-
+
+
----- \ No newline at end of file +---- diff --git a/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html b/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html index 9b1c7557a..c6f49d651 100644 --- a/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html +++ b/src/main/resources/lessons/lessontemplate/html/LessonTemplate.html @@ -4,38 +4,38 @@ -
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -71,7 +71,7 @@ see other lessons for other more complex examples -->
-
+
diff --git a/src/main/resources/lessons/logging/html/LogSpoofing.html b/src/main/resources/lessons/logging/html/LogSpoofing.html index 68e49a064..13071ada3 100755 --- a/src/main/resources/lessons/logging/html/LogSpoofing.html +++ b/src/main/resources/lessons/logging/html/LogSpoofing.html @@ -6,12 +6,12 @@ -
+
-
+
-
+
-
+
-
+
diff --git a/src/main/resources/lessons/missingac/html/MissingFunctionAC.html b/src/main/resources/lessons/missingac/html/MissingFunctionAC.html index bb0d1d248..8fdd8bbf8 100644 --- a/src/main/resources/lessons/missingac/html/MissingFunctionAC.html +++ b/src/main/resources/lessons/missingac/html/MissingFunctionAC.html @@ -1,12 +1,12 @@
-
+
-
+

Y$|Z;qI@?)7`D0do8&gNU_6#&cAC_Xrwt&(nsAk8&IyB z4J;eW@}_weij9A)qXusgy?ww2m9z z1F_>5@x`A`WO|>K8)DG)BK$*~MjY)U4_oKHK~*hf-L9}zZ}rc1U6j=@%;7qWWL#DA zs=^$ve7RNDefl7v8Sm5;kJ4hbqe9q~@{YIfxJJdh9|28%)4(aDo}_0FY_f4Soe=y%ZmDqr^tZxC_-kFfiTOasn~GEgKXE_Qn`Q$=63V z8%>V3gPO30Mm5*nha!w&jIZ1Q!y0$3Sb5}2{m?rdY6Fz$CCbug6S$E+4=7b+6KR2` zpBK2BOT#BAvxZ^yl%I+euj&@%4vK zS-pNS^DeY&-!j8n&z5DJWxEttOXBQ!Ke|3N1y-sGo62ynoxci?*27J$r2&9=C*SuK z-^Jh9@Wu5W;^K=57N-7m7L7dgTKDILkYpQE9NLp?Sy4pxk%@G9EgTwC;Eqm|k=Hc{ z#%*o+aC5^fe0^W)>L#XTWpxr{gva}M*q}0P4=O)M|B!Oe8DOxH`S=$Xp8Yv1)&~_Z zU6__h8*b%;2`qf+oJ#cDGdcifLPs6+U0;c*G9skv_|v!JPeQMicUC4pRWZ{Jg=-95 zd%ku8$K&kj*rm}lGH%T?HWw>|{dP$120PrHsb`>BLUb z3{MMS0p6jsYeh8MI_}JL?FhF_RMZrHZDu5YSEzDUkyw268d5=FP)PVLvUAJbG}_l# z>kAhzQG!kcFVkA43P-t8sf|LT97MQ0?_EL4RGx0#>;GTSy~uc4xBC+MbS8UG{!i7U z6On$Cce5KUfXit=xg5s;P~2pAZGdBV<~AON({xm4J5cQOocB=7ZK?%_y~||Ux*JTA zcdHOhJKSWpErwSe05kl!(%Rk7FSvT89saC=FrPhY^x+3|R zlLZNSg*h`X>R%K6@UniqGPW2i@k9MQ zikU*1b-@=A%WtX+az9#kH*mc2=ss4TSRNMBjx$2;o$3AnqcxaNt;qp@aIcF*9XS6c zG=g{Uy+<4X!^W1;|aPRty_D>dYTIJLoDZ18$E}^AzxvxM9KopG_JkNp;V#J*lneW=&(jiSKcxxjiMcPqHad#B)(X`O`>q9x@{@~Z zq@6E1lJXE;(vF}^h@;YeW2`+^jhzZ%El{*l53JG(d=r6S9IN)#Q!T_FW0u^9f{HE* zZf&_FFo-KSNGrg}t^$StduBA<2q!4cvlZCsY7(04!!Z5RZSJ>!%V|>5jOuj~XMy|S3_4w?>~+31Lv{qW3I`F`kAtg*X*b@`X{L|n{>0bI(g;J2}Lr|^$=F6?wJG6 zCn*R7GnEGbq7NPP!zXzRe?oobV*H$C@=CX7B4B?Cy(o@W+9JMOY!Do~_+RPX6XbRW zYkj{&mGL{1_}%_FDm8bp_k;7Z>z_oK(0Z3)9z(LBN#bBxW$b( zUv%OFjebtbnHYu5gp1NddFVt1x(?rU*?qxCx(0KQm!sJ11PD=Als0fb#SGkL;|GYw zmM`kO)zsC1TH4#AZfsanP#03s_Oj0o-3VmKiiS1Xsk&XlUcj@&!w2cVpvK9bw7T-g z#pLwvEEu7rvJHqQ-gFLqGN?pY3bOxc*#F~6y=DZkSSjugQxC7kV^Z+PVI<;0^_Nya zVQRp7Ug1Mfv504*XwWA`bvs9sps)&1TMhax(2+%azOpVu+Cap_nN^xqH?jQZNE#j1 zK?uGcTsR}tI{QDjg&g%jZ=J#8xe^wutzJwc^-|7VcIm*K|;$DKje`*ZFp(Vy&0^)K~ zHMx%#__8Fs3SYgkKbY}A6+9c_wWC{n@LLeXvID5vxprcz+8oT|EpUMO*(>S|g(J#h zdc!d8^oe;rKeWz3A_ur!xrXk9*+b8*=oPp9 z{qd@cXb)#NCBWfjW^<9iDNYN3ssO(!@S~t)>S8{O7qPO=ddVe|)m*4YL?haZDhP2a z{IPb5S>d4xFI5zF?v)2bNQ&NG-&41o$Ue-8p42|w3|^fU7~9D zFfW2IqxjjLvMg2qGkYJ|1_9CLW#crD#`o{3dSO4&OSUQb<1DR!BpbR+6qz(V*L~~* z1FFa#Fe)M8xiI<|fyJ44P2={-^+w!UBp`0`v5|saR<(bF@~uWr|MJ0$lpQ+GEnM7o zWw9zwN2JM|>AbZ=@3W^8EQVat$vWEdO-@E4;&mdxX}NGxBQ-m-X@mq6Qv2IJHbvq#q#77F zmT1XzM#j7l-kS23jMWmk|q6K12HoCi7Ax~8R0sl#vIuZo?xH{ z$fjwBL}Q^?yldX8z_9+sL>3-TUdq(eYCH@nD){%^3hr8>jvW9W&z>G}{2o_Ud%0R< z68@_@%b4D82$bd9N@rl<_=>YNM?0TGcyUjc&&Q$8bAiS%=2)IkG1kBh{<$zb zG=KwR@Qs2rd)5eU3SVfR5<(X9_Klp2~@*9rA# zzb$)b;{J09D!*DybhhU{rd{s`zeQ`9ikGjghenl6_X=L_(7>&0kPRiqZWbZuB2OQx zNGo@=Hpd_`Zw5s!3dD@ZTKV~+TO;E$o2F+79cnG~E}J-gbldGw21G|oJGiieK2Xj& zz#2N8o((H3wBr}|!PnK@?iUmH!30ci*YV3@7hAn#eR%0uVk_qO#<6wg*ysF(GZoP< z!AOBRupED0@M@`wg?jM&i3snBn7Y*FHUmPB^7vz_olJ?{j@oG{*Syz9!sD7iFGh0H zvMpLhlQtUBe9hGw`O$C8ieP8LVNKw#oceL!^-&je_2!=T{_<+I=jd_g^Zjp^FF4(I8w4kXTB|6ZsOu4*v{dd?(1D6x52*u(0>To+h47OV8*zpCMu0>D=8Vg27`got<$T0o&yif^l;7 zQYc+s_zw7ZbJUMwZ_{+UXD$(iq^wSLJHfG0sq01|Hl-Ko*^!>@#V4WiMF=$7|ZO_ zO~*fojkKe$huqof8sZx+Ra@vCJ`nHHSmi+I990M+phPE;MQVP~J<+({>e8#TifrWM zmts~b=sHNHV>8H17tMMTC8@=nYpqI)2F>~-Hd4?!Y>6>3q%oBXU4`g{v&Eb`+?G8o zyR8&uU0!OpWeew}*rb+WLz6JGv*i0g{Ejt!LS<~{1r&k=QTPY?D~!2q3H*~@i9;Y@ z+439X$&&i)`a;EiYcAoCaolujx5oE$B=Zxak{FH%NQFS5t2Du>v+r-b^9Bb-%wX!r zZ|`U-1su-}v8h6>(;;cdi6-~C(CG=I`^KX{X*LDYq&+NR$YSDNOXf4N?CrJZ{q*?n zpDte>_3s;xx`J1)ukrjY*J%9?$Lx-Osgt`syn8Nr$XwCJqvXe0MP+A+^b)P24_^_A z^c*a3eVlt^vOsSWl6f;FAemVy_M5M8^-V!vI?VF9f(g)jzRC4H*)El?HaFDL<`g%F zLN*+*vF+yNw;4_K6QPquPu;&r6b56WpxmlLV7-mivIM$QHBL9CWv2HoAK)Soto3#L zfm>S~+71^29d*jC9QmbPW@YyK83^grzPC zV-oS23-K|=gg9x0za|(F!g&;K`7|FC1vcg2F3Nt2!A<6Ijza+o%OO@%F?#P=D&qO` zGMe+U1aXeD@XK}HOl7f4O!7>N4lnA&P@~5q5>OzK)UR3F%5#nM0M^Rg7Nz1ADm*1B zIcsj-p$L|-aj6fI1u9d3a8nD)BXvsWDVr?{oSzSJyXjp1el6BFV>rHm3%SC@}QyE zq{Oe>5kvDDC8H6hX?z9}+Z1rgPj@6SurX;nj}< znM&Up$(qDpGT5r{z@eyk<|G}-i?hq)9ZFIIn#0h;Ae@yf>lZnc+@(mANo`zdFlpC_ zlMWY@K5qi<038ofj&*V8?i0!R#IF*Lp@=u8Tb>4o1y*m?$-nAIqm+Z3+*Yf5`aSh7 zU7wV?{87#AjHUnVM<9DW_rdSB$I0*hQT_U$*YLQ@r7KJe{dwJr+PXz1BqMskxl{+{y|y&b zQTQ>+unq{k5f+pQ({aWWlL?HB%g@&6x*aCN&Aaj1(&Mdv}R}A<@sPx>&bX=+-ba4 zanv(VLQ_G1Fz&Nb+1%}5wgRpJ@qL9BJlKg$r(Yd5GHiW&)diM!?+u7BfyS&xD9mtY zT#*^o$@0_rTL;R|%X6v#zwYo)vBq~(Dh!54K(D{QOF?3|h$iFvo(f3lmx zrRH|-?Sl~JZ$tfF2RgbMDOq^n7_7a6hwrrS0}2>ydGbr!e)4{18`Gd3la!8eanH;5@7~9c?vTMKK!DfR1s#Tek z2iazSj&W0=ryMk`1O&N4@wvLbMVuoFBE~?)Fc8aFnbO72mE|`Cg@P2!5IxLb#L5(* z*cFcEjCiGJfhvzZAsqO-+5Ezywjoe{GSn-K8eKw}WB~04%x4TY8v_(ukLKGNH%}=+ zj)rL?Dr{vl0du{rY|%Q-r04XsH`guR6 z@490A?SH}PY_{6J_A1UmJVe1B&BPgRW^_9*?F%j!ZiXTJ#e}1Prq!%qFy6c%BMNbp z5nnr$BKC4bOuTH$9TlZE5H23KTR+GzdP6~GN7s8kLo9apOlUP6w#_;yASfI-xvLhV zp@D27v5EB(b#>D+GKhqwg}Ihtji^GpV`COR=_qHodKE@GRI@PwFp+3fYG-Lw6SC{S z+x+3pVuHO9pb75f1>E#r8J^_d!ourFf?xjmOFBS7t&Gh1J>?Y)DKxn({c>vl~6p0vr%xA?E4u%Y&>Ev0NDJ zKjk^(KjnG7-!6IqEcjleK;KxNr7OT4NOy5BXspbVfq^-pu1JWC(y>LDFpt*~t0~2T zYx{Ozzce|%3E9Cc?gGdD#AxNjSUErYo~t&%y4$|TYcjy!EKXA;SVbYy_$>zwtg8xT z2?MSsGA$!LLKNyMDEgJE37owG^E5^b1bho2y9mLbO(P4Pz<&4Jr*Gaa;bQWPMZ@Dr z_`o2Hu+5O7J%v&XfOLuf=HW#9A=uw83Xtyvnp$|B5qg(CzU|bt%qh*)kwdB7QD=Br zkn4#vC>Xqvr4=5On*#tTFyxWvrDvkM>;8BcA(P->8(~Ov6^7uQ?t4GYs$UbKA&%-2 zZ}cnXLke#?=)VWKbbnM2K)7_?p?<%dgLoent9#$RA&@*I+3OF8=stGY$=8pO`*w~l zG}1Ux0hceO4Ahau&-9^k-hLS*gEoalzPI(N#_aEUaVA@uv1X!HgIj8J-&e-L$mqo|$ECAOgWJDJolKWSo=UL6Bh=4o6VN zj;8gDjNs1f8G*{0GK|tibmeuV>BpQSk)AWUndjBu&(l$TVMIpV82x3?%gGB=Ds)i<;qMGp&h{Yp z-%(a6-!%q8e1~J5SD;=_c*$89>8%Doc1%sfX~y#X-tjf3PP4YBVykt(45b(&`epEj zIv!dcU+iE2!1Nfc+%Z@n(zx5Xx6+HY{~3TSnWkvGdC$4olEjWV1L}*R*v#4iI-v^L z1*!k992t4eABb3epnv-RaB$#1IQVUGYrs%fg@IV=^qL!UaN*EksrD#Ofsx*X3?3$4 z*re}46YheOEW6pfW#;9DI@LM0@%icUgjOV`emCfc{7P8w0H@ao8I1@nk``2Hph_-7 zEj|`}lVD4fsH0V;Oxmkn;4Qf=BsyiBWW7w|N_kO4CQ167w@KRLP@hDRYv@``Hg#Nh zN?OAs25T$^B&57t0Ke;wTSFs#?e6h{fmU)_^z5gTm@g&$~wfMsDWb#wXF}!Qv`|huBr~4WDyyg;x5OEpl zJ$sSVarJ(_VXR`KA06feJMJ$cif@ICwTrg7;j5L(QdWvGU9tX>fvL`P8(p=_eVJFz ze=J&FjJK0IXJu0KAnD-}av*=Vkk+?=woq{xZ1VP} z6j>Q!JN)lf+aee??y6xGYwzZaYQH5U#6>(&#+s+A^|L59bh_s2oWK)YmL_|#)HCgHL&!BX^d(g!%c2X>{9 zs!t9j2)+k$KK^2y29}wWQllbT$trf>IxhXfi5#N;eKV=b1ZW_HA42x&=u71I_cK1X zZGV}@|KQ~L|KQ{YiRE_*6vkAB+L~|%>U237iNH7ZDa}0AE#w!fR|h&8I(dAMrma+BkO~>gN?hVw)KT9~hOO$27&VqThLE|k=#Hx|+W^1kvH7`m_3ZS?A}1w6EqLz9&IoRWGasPJ}d z4jJ98JDN1_K8rbryOz@rg89N8G6IhzjB?4+dGMx8IGMY+tW2cAs>062$Rn?I zlX=-*>tiOLuB+)g16QvP@V`Huj9yP(nGM>}cZJk;L(o>ekHq~pD#tf|kCLD4MVET- zE-IQzZm9%NZW7a7&o6YJ2tRuk@JvlfUJqKgPo8g*699BWCtG0_p;2Ej)wLRvA&7Ip zk|F2_zW^Bod*6gIbzUWJA3x4JuW`A^25;vssdlF-Fgu3M3PzLmQ^GW515YB^_(l9N zb#PSKVcI~iM;IBY}>CFQV0{C(5L z1P8pQimrEH@1>Q$ujr(H5tA2l5K!kNfJn!8Kw(5}srrRu)vJbLWf7pF3>p8qZ9&HSbYb;tnX~XMwK*a{tH^bUt$FOq&d<}w&6P|JGQjIbOr>_hT*S+fl+JMcJ z(9|QsN!Yi!%SauECYwza!hI)W4Zz8WYP{?Q;*}_z;v$st0948xRUIC82GS{ijD{w& z5(m*nvRlczd`)YPeAQTlkxYRKlbv$urW}+>+{(8o*hA*fPWwaA**l_JXg-Dwfa+f9 ze?dl2I?Rdt#@r{;$5q)!h~Gok>d#8Pe@6I=iGLc=k1n%r3-#-D4uf7R0R}k$upI`7 zcYsyr&D(rJYE=&LNELtlhYHjsJeFkCl|59dP_-Gs>7A6Oom3|=P_<>tTAnReyE1?i zig3OOrwd*~+b^!978}5NT>l0o{uh3`j42?-tw zF%ehnRrK}V()jvxocUmjj4$cY_cLxYpBs|-8qfNUMEUw1WpSkktT2o^2}wB@yA*7|<8KdhfSY!jKeXePMcY@ouNpLXgTx zAn=#k>f9TMa$J%Y^sOA!FXVDiqYUUML|~Xuc(na-oN@DmT{^hj{aH zvK57;p(@V!4;iS>KhVZvBJZL%Q3$0>vcH{vwAI9D+<9xNyY$aR$y=g z%cRPFxh@zJYx{QCm)`9tOQ}Rwr0C_77{1tsh}S3y7n2PVaEcfkPWtmS^suz21l1_d zL-&>RRN}IWm*aVAN+B8^F4Xqo&gVcPyw!FV@@5C>o#gqQ^eT3hi?UMg9dhueVdz5z zxukLnl6zbSI`dFbas%|>x57g@0&ks53@3-H^RwNi1S%<#q(Mc8?8-6c-1z6})F4}f zejnTn$xz|Wi#?+6Ps5|v_tS>QhXlFgc{d%a{*>lluE5J}o+y0F3j?#$th{u3_48u| zS&KpA)!{|}_+vc`QzZD?@)UGM_!o+ zxOC^$-!t#SS+3Tbdz2cP#hx`wq7_Y=**7ne-;rDxQ$dZ%ZagS5sVgTOQtF%dL$_|g zRovt9T@5dit;nL3y#R4mK5j~Kz+h)8gHfqD8ujDq+8;*-Ke|igepD+B6)M>1a&b20 zzuYgc!`iLGutbvNc~}Cu_(aBVrK+L9Lu6BU@0+xfW*kJjCbAji+b88AeTB-nj}zgW zLU@6cR2VYSzGM2U`(h|77BtB{`H@|6N18%=%tYA~QxRb`z?_Ffdz9{@Bd*C3BJJag z--IR&DbKRAKz|m)@Sg1p#f3PCmrw@*+r6Nd^B@^gACuQCasV{2OgNKh21bXgQe&bx z`Fzw}3#TSZR-=&Nh5!b(Pw@Xz&@xKn=G|;}Upw3nK3$t14^*!HBYxbooGV1jc)}ky zQ^P4U)!-}0FqUI#iE%ax_1A}*DTiu;jpTqe25u-TBQI~i*`Du@Qq1@iUF>0d?XjMZ zkf9_~vd)7KNHl(agZ)K#^{~sivwRP-SK!GLpkLxKni!$Rh)3)|tzuRRrpuTOw6zG< zE<)iFm(;vX?kY_UD!*z+S(!%-a}0-LTg+IWeP?- z%%zlRPDUDkT|1}3WD(uN!m$@%;w0)SGZ=Yet0=my6g==8DbF1KR)lw0g_E5XeYvPp z2%+B9pGlm{?@dQ;A60qKd|>L}%oCDy*Sbpdz8X`%z6>8)Mew^V_7fld zT6!fdk|O^(sY$nDjGkUbOgD%P%K-0?CJMnV#|rvN)T8rM*$Oz;Q7ZB%LyK@`v&hK_ zMz7CJKAvcOxkiemS06U<{5JMmPUbS_Po|;=TOal4+1N-A{sJfWizl1~Y%~kw@t8Kv zgI5Z|?0qoR&xo~o$K;CB22a;Xs^hHPoI?iAv^fXz<~4eqP5K7fsbp#~Wm?={WR&|k z{2#X7GAPcl=^6~~5Fofa!DVo_0fH0U-GjS31O^K_xJ!WG?he7-o#4S;ck=A+`+U3g z_OGd$x~pdH>Z`lY>C=6U-hkvTT2e6Al#x6*1yT|zsQeI!u%LuEFi|*+QJhs!zI>*u z#bgY2jp;syrgZfW?zt%@L^_Z9Fz!@^CA*0{1BEst#$0R6JVSw|pK%A5bn_fz^@$|E z%#gqM5t;>W28fxL5LJeW!tfD|iHc-C(r({8Bb3;>3iKSJZ^%=}eSg zxy18GQ+f*cDv?%_X7Dfvzy)y^D;%^5FBZoeg!PCCj9EQ1O}CVN8fmXjoNKu%Va_W2 z|Ae*wV-SjyUi8}V4ejl<>%GhAzQ|DA;y)rH@qggykLB+}&4$C|mKHE_M-==j7fEu; z0#2#rfy(Gu{*QF=&+I`azQlj_sE;2BP;HR)4o@6gO$WkaCH6jxM2?g?Q*~ANA39Fd z>Z&n*0BEu_2c}tKfx86>t+PIi>z(S@n>Dz0s)|~6E>!atc!f3EDud`%zB`$P2Q^y9 z-!W4WDd!W<=B2Yu-Hsh<>K!I!_KxU%!9N0{-Mm;pN$KAL3at*+*KT!Ve~=!G%315QN2(n)8=r*BiH;=GnG{t+rrs25bla z8mNT+VhMfI?nRgFUmMkQo)zW&Isv!l^9XI=Z;UZ1G~Wm5g3m?kMm~;f)v@$eb0Du@ zAu_EH=xFvf6-ztzi55wDMhDfwZ!0kWpx|^yLSP}qy*z3%AmLB%dXL2yd7ZF*ctU8p z80;vAL`7j1LwlnDk%+9nsEr74fSJJ;zpDzblhd_WAq;I9saC7dgpXbT6qI^D0)ZHu(+1b2 zGBi_8d^S9)(R?Q|OBNSwkyuFfd=+R=ueUa{r4Ycu{-D@r1Ov&Y4oro>x- zoKojr$;HMgu2V=yiuBsv3&!V91vBqMz^ zvRsW6hJQe|8jnuc?>tu^Zlxbon%Y^G)Ae2ZRL;&i9&L(8j(2L@=`=EW;TEwi^`o|- zI7|ODqeg47qUgSv5!_{^IrWjQr3rr_r442@=1L|W+6O*{f?iBQH81#ar&fPG#(ZW~ zfCm;-Sm2o6a%9wgFhQlDbhDO~y(0)-V1JyFqky>J0JTiYMKpXW^N${=woN-`YOo@5 zyL8sCRXugsR}mKHMv!4Hnw$8>Z|U5}sg|riAQ6&*|7*u`1rcz7_qNSG>eu9tQ4r4b*9>TJ5KVC4#fYLw=%%q-zB3yQ>6g1R2+&Bal!fL-{ZZH4@pljv@n` zuk&S%*oDKqEu;dCW7~|D#sakfk8%~hH51RgiXmB}#3em!U}H{!jIW*AW-r&nVJVXJ z@B&}~%Ijs2cxaq<;fYlOBn99V`jja2c8tZmw?3-Z*AhGBNt^khe;=oreyHlXi2s?= zfZVJHB9Md;wMSiIZnavDv{<1Py*8Mq;z;M?C`hQzC0;1_t$5I^-3)FaFzRZKRotL) zPHR~@YS+I+Ek6~bJCXcsgGJ8YQ6w%r{L9i_)Jl{#Upne%Qe%OEo&?u{IC$G_B1V1= zy6GD{aGr1J2K$0H1#_K)630M_e@B$kN}}HuI6Ug;h0m7|SCyY>AVG67m`gCo$b)gw z5qAzDq`IgEcw_o3(t$97-YqBiXaLVLuD(20~ zE^}?`O@t{xQ>H>PqaMywOSaSWbJL86wiM@iaf`%cwlkuPe$_EHPps{yHN{}_LS@kHwJghG`UODXmWu@wDT;iAX|ddUX*fe5dO zN5r<;R24;n=^__a)ZoMUb^79%VY-`AoQfs;Z@IcIMoo3GId9AVtF{t?w;>V@w6{L8 zm+8%r|Ceirz?W#AtEF$pPJ-3Dp~uyxn9~GV`B>vG9O*4I3EPkw(7+sS)s5MmP$yw@ zje#jIGZRR`9rndp`@sO!g56jbImw2dP0{XfTFXgM; zESK-?`(Hro%X-8$Mz{(vVTpY9VpI)Z?%AdtYRTp9qaNq;vBYBAw_Z$OV&FqN80;!0 zjQfP66%Mc{Gx+yR(C3^ptZ-jS%>;)-N$RuW{*=O_%wWP#uq2h;@w2bB`9+=kdJ>7e zysh>wzq|=d?rdbqHM>0Hdlci8V43}MfKo~k{=t4mQ=Qo>9_)pr4C|s3ir+JH70>6Ccb!cd=d>LQ9aoV zW%94Pu4J?GpUmgK2APV%K(KnVeK~l)F}&PgC;JbQ#;E&8PQJ_oy#L^wa-;Dc zrqwKR#{H#~wz6`Z;@++{#TtE$LXJ7b`f^G0saLM|yPgueAZcsb z*G@jDc+MU#s01lJpehX^f-?wy(cTZj=)g665&+-QOQs~CvCPykLIRuio`H(-^|YQVUw+8#O7SMz zmNK;^)y1go5chFxHH>STxcbuqiqY8e*r`0r&=@5PgnrTI4>xuZ8nD+Rioy(`w|};jbKOQbY9lgy@A6Y4?kqU@kh`^RXF6+dh8C30sA6W=)W_`x5^~mxJgq z3FciLEJ}6ed!GT8L%TEPrtdke|HaYC!$St^?unrvxSltPs(us_T>wA|R!eG@lemzbX z?|K~jyGD_wv+4pCg8?4CCO)YXs|B|mIv?89Md0f<#;XdF-KO;%^JVW3KP`Vdl}zcB z<{LN<8|8o<2RJ8+#P%EUIMG-FKMU9)((iCaaigw~W)e#}@gnA`)JMbnXU^)4IMxm4 z$+e_#AO*JTZgvl3v9Z+q0eCVE`|D5{Y;RV%YgR8%iJjEygK@LxMOhMN_S_ z^6m^gQ1Fhj5lAp%pM?psDE>$D0~uO|%+7rH&+N<~+3U2@#m1`0V0qWfN9F&BcjfPi%|ugXg&(|OFW^Ir{4SD8piS?m+2)fRiZFB z!4Uq?34dG}V14WOG(SnboJxeIaHSsad?5a;wbrY{b#%c3zjHGG&WtMtN5PPk)5 z9@dj}G!^AcCx~{;z!fL{#^%b#srRe4j&~5<1p5`2?u#r{?HUuyPYIzuCldRc-?lf; zjfXrBHM;jEC9Og37C145Qlk3>#bm2+JW*oI92skwiZK;RRO!!7*ImHhvHngW6W2SX zB)$H9tj=lWr-8T$glm8)kYLv2*}iJO%z1aLymACU{n-Ccz6F#u*&d-wU%UKLC~-_# zAe2fR{81oMQD|azR(8#y1V!VQMK)wnlG~pNsD~=!zqVajqj?O3dw4edG`bwG;~t9X(^9tF{fA;c^sDUP+8-Sz@OYQyju_JplPn1?u~Y-PpO z7AoYe=2Emf;-4FWzAS|3Y=pqS-f0X(3%{OaX?%D+rtKJI>lpnI>*J)iCOhp7i%=QA zw7~3jnB@}|(jsxdUCUDAzZShcV-FwkmkmByA@eSg_qZR{?hH@paBuCVlH_K*5@0w6 znHD7Dnrf@fpLq9DcFg_iWSX({ zwEM^&R!Ha9A0}540e+f@$O+!lCr5LU@KGO?)47H7NzQ^u%jL2b>kZE%hu)B^C;nF< zPsgR}-N1?EqQ8&<>_z)2Kd(ne0h!IPpl5y-*Y=s||NjuzhyQz?M&xn!9b@xl%j4p> zY7g*l*6TL@2tsi$vI3`>86iM3@6tWSlpVmkY=C3RSt82C73XY%8+Hh|O0cfKVmwTm zqaU<5!WSacrbz}$nmIzK?=lKy3y-U6_FSywz3NLc81o79eS9?dSzo=7iS6MSv4L3k z4Uk^s`Y3880f2S7RFux4&wYSKuehib{V|vDh?(X`1~NOxn)&c(b(7&bKG0IRAzIPf z0PnRC?UaYdsg-0vR)o?yU6(-Mw+c+HFb)GI%##-#yxMvN%<8E9<7Z0FI~!qk z_iKC15|!BWu-TU1F))eTP;pUV6PFitwwqh&%pN zOvmh2coW^F!~;M5(V?1R&$h*B{)p9WNtB#Qm+VNyZS=S^2KI@CPQ>jtlMRF^hIT%u zJCeDqNimP3k*(kO9}!JGuQh#ld zthYZ@;r(icD~Wds>G=2Jhz+#yQE0#S3b-N=(!kvN&-sCn z!+FR3#;BK)oFYH0xYH>d$tZ{tIygTzWS2e|xd3_w8kw>KR!W9kl)NzeCUMrq?|uUz z>(SzM;an-a;?%a%=kLL?-{!N|qIQdO)rr0I+^T}5hOy~Lr0Lg)_k&3t(f`A?0+0@1 zrpG`Dz_IygdNR3_mgg4NLBo3RY&Mi4 z2j}OQ?MSQwB%wGxi;*{HD85^=LMI_XF>lgq=5_GsSc34$+0#~NLNkaW!>^<`y5@Lh%VnR#h!nZoUVXGkdJg0>uk?&>^D$r ze$;fZmiuM4glB92L^!kk025O`B-W~l{RN5Ww=Co`8!8TpVghEVeu2QWDzdVzkA4DQ z3XjGxrODz%6`#VDqiop|>N=$vblJq;bfwRUBC?jWoK2Wa^IMW-*g5#gcSD^A>FB!9 z$xTjnuLwYa78)R%!;JW;`}sITZCXkHScU{dROd%fd>aD$dv`57r}hsihUe$_@1x2S zC)LJlv6sf!0hW>gs?e`_V%gnQNovMvQW6LV2^V)W+J*Re_r9Sc)vgp0n@S1UD8{Xu z3R8ls}0U z@m=m48+7`uu``hYcH%~9kQ_vmX;C~mCUX$j0^YBXo?83u2L8GMwHM5^q7_~rNAu?- zXmaX{pH6eXRQDQGTQRAD98vHD3oJ>NBTMXd$d$i+WK%RZz?0FGrhw;S*o%<@hck>?#3xEI*B4!pyrOJ7EO!yJrq_pAQS~XszHG z@>1uzsjDK?=liL0^#S3S<1MA=IW69>i~f%sde`6G9(GFTE+-Aw!mE6Z8DA*~cc;fz zn`mpiI6${l2A~6y%yx}C_1v~gi9_obw!L*6HW?yo`>PQBD82qr)wSU5P=caRDDcO> zSuZ5F@ff}xuDB`0N`u&UHMN;jmxu?I1*G-`pNnTJBz_Ow#N4JaEwPEyP!$k3js2Ea zj!K*_`}-?CZ~$5*ArU>Mw_rj8R^LFo)(yspwLEojb8cJwN^sdv6zdDjQ9)J0{mo3B7ChB4#%mLM#cWD z4w-5hv&j#tq)5q3!Od}WJJ(ja&30i!OVC}FS&8GumOS}q29o+hGEeZHBUmtl+Gcef zM8;Gp{w zSIiW8MVR`ssQctP!i@!iRlfe5_76|V4=bsx1C}D} zv2lj#z4HQBORW^3nqZQFP$&&`81IZX(n|G!kj{M!3D&X_A-C?bzv5F2Vr!M6E0dJI z5az^{evr$I;Yo20-;%H&j%vQV3?uqMJ@-@59eqQQLZ-5XEGBlg`BA)^HVR$3wm>5G z2>c)X$uu@_lQHP&+AL5E4QK*iF{)KIA@7$YNMRK0YiM*uQ?DUhEKW=PTn=>MD<9e` zB}t^>Mzw6nG_z0{cfz$MCY9_S{{iDZ{rth%iBAcUH&esq2!%GN=Qdf})kQC8B>(#f zXKLD~h#ZG{PdJxsqcq+WG!rl#f}6 z5!0!O3iGgMnhN3Ve@Vb7(=Vx^frhR7n{VkKmH!hA30SD}A@@|F%NlY4XY}#MUGwm5 zC1{r-U2`!p@uKYwVA}R6dFvT5Yayvlqkl~yvQ0N0#qH}VgCYQ-@>a%kH38{{{Li^$ z_wRgr%|3d4sB&i+$e)05>pX&5yg3$m5MN24Vkr=e%Rus1FO|>E zp@e-@_Fd3Bxy1rlkmR)!Xw*B&UHyLxDtaUxQx`gOUk( zVM0b9S7ALoun5iHl#wn8tOjJKmgdXvN2KC{;h@g)18kE2ID=vd!mM#~X+g!UBxZBF z;xIGk?+nR|j1@rGhz>l{R{UsH^kxQyn^0jB{B;!`gDy?i=*N5?Wr@q-iZ$7KeGbb{ z2U?MaY39pH$?uKiL)$lj8}2tDvn)nq6+oZm zX_3&ZS35&18BVPj+7}g}-?A{^6#1CxdiaxQczIlT*8pVyw?FjfP^6FJk)A0`2?r33n1L5s4(gAg5Q5Bgo8PzPd#lrB%RbninCyz;j1V-GsTe8q{P9(I^+;$8C zD)}vGKo|BrAV0MfCHXetIL(UE=Ys7YIL5R=3946!`*x~9xg|t3TOWW|{Moqh zw#?b{L>ZVUHqj}GQcXcGb)9xVK8ek*iZNvc5&yeSwvXYBZ#_4?M7`;EG38MFrz}Lp zL!WLBRWQ}@9K)lqG7Kqg!*WtWGdZ&fB~C_MW-P4;-c*H(b3*goXOPrKn=2uZWu+=? z+u~O1Evnl3fzLEeme^<1Ix1)bmbJz&_v#e^0_r@9N5=r*f(cwjNT&v7I6@n`+db4y z0Z}J?i<7&zxFS?veSn6O_*lo=(p0ysBjh-Jdx6X8N7{%5oBp1vR*bkzSfO0+h<;8H z2`*ttaz(h93S3Fv_q&;~Kb)DZ@Hz#_chj+fWoYc_reHITJYo$KK=GiAS`5hYqcP(w zsQ3}|4N;rl`qexijJXCBI_y(xwkB=H&8`OtdEsu=j;V1 zjELE??wnEZ3&;sCP8OuT%5(Qk!I#&Px^u1CV#inqO00^ys5ug#;ojP%EQ;}HAr;tx z?a-&dDD2EpvO-r*%q;8pJtZPKgLGA-gG26nPV;xDw9-O|J~UG!9?wTF#tV@#kc`7wz4Xq-_0N-0Upj2`h+93 zI%h%i{-;d1RiNOKs=JeT%O`DGur^t-*EndMq?|DvFsd@nw4Ix?U~J~9C%s#d#0MP8g#?!WmR0B;C#|xW%6yV>&u1*p$Jz(&LbL zT4KfmBkeShRDsDp<6l}qo-Wn_u{MzZg` zBX&RkoR1>CrxXG3^Ts007wF7v_XZIUx`xf!w9!caRFsEKYDsHgRCQ)!k1HxL2d8ku zj>-G9gwdUurbCbn-?7oi%W#EI=GZL}|Mql4&p>^w8#Cex|8|pGx5KxH(WQ3c0eP32 z$o)@=`WI-FW7tR>>j#R|R-N%>Xru~Mlqu+YVB%22!Nk`A{nt!U3s{E42>PLVeAz?x zu|i5p8%2K3BlJePV$5%v3w_j;!RiL1MS!%z@Ko#1L~T}89j@61iX20FAhQ&TUMyHu zfx5t=h?hm!#0q!|FSLrE{JixsdM+#Um3wA@#4RILVy4>VBGW6PO4nb))XV-t5*O@c z7%R4Xwu7y;C>gXbY54Axq6hJ46+q3=&HSc6@1pBXKhE-FUZzGDWB6%?VUx}iM&bL{ zRjJ$MTP1t`uyTl{z*dp9T#$cp`W(LNd@RzBO&iOBwx5|S!g-Y@1?D8Hk&GHOs|Cq{ zs=d;(Z)Nm`R>AMi{%{>cHDz0a`#&;X%1eBT*-ugDBCJK~3WHSK-zoTz*>RlN;c8X* zu8?rTIdwkG5@tuTrm7k7mh85U?V59b(MCg-&)_Qna?|ee|L&Vh#T=72^aAwd7pe=2 zg9+qrhCosroTeimu9=P~8rV(1Qi7Zu{M&yega>6DRxH(iQxz<~$kV0ETC#mdFGeD{ z_Mjpm>7OnTFZf^G%%Q^#-|(@w);F9y9S(K^GNL7UO)2bNhViJ3%@IV~ug7IJFXMTP zNw*7ISBb3RAtWBF(+slR;#Tjrtqh-S#^qkO;YGzkTq62-T|b@XE~(FqxJrasKkpL! z*d@(dH{)RN%jKlJCA?0m1q=PJ4od^3hsV^4ek8wuwH*rO9XgBg$6B}Ua)+H60w@;& zy0qP2Vx&@|dkpPQ?#&IA7X;uWDU8KiW=U{LH7+(BiZdpk5-naeGaWRj<(c=N~dy$1$O zVD!*e1xHNe6uNMB?7>o92uWmWS(vX;QHZcBSmO?qcRPwlCnSY`q4|QuCsaN>=UVKa zWXcaJLeQ1t;svnY+H;}E?|Fir zXMa5`P46s$qJ~igS)ghe)*De5NHg$;%7;o}maEA8RQYnkro-mKSe)h>PFz+B`a*yu z@4;8z=V&mQ2#uT84%CTJ@n)q?7K8>LF*p#{I8`1|Qh~Gq&Y%gPOR9B@UHq$bcda~p z;sm~|E#K#HN(f3DGv^5oiF`Wrhw+mM2it{z0ym?M&_A*+MSpnK4}A&)Li7XwW8Dad z2iVhNr#E2jwYyyeFSWY=$Kzxqum519Q-5oS)cJ`C&>%Od6bt;)a7PO_fZE5vvnjkr%;E{@9oqREsl(p$}W&3l|XAn))o6 z$$X)EylqiS7|xI_!**&1TpN|yVrjm)y}&2JZ1k9UB0;Pz^dH2<57)YJ<%CW_m(21L zoZ-S&hE%~j;8xGQZ@rwM)dd)&Ux|Dd(9I~3&H9ja+Tup7mjGyNjJC$AYO{ouG6)1c z^wJbRfxjmyJ;6+&>8Y}tA0Km57YId{77$8OD*I&S{5uVXSEOy5oJ`L@=YVv3ss3P& zx~pRoA6saBLSq{t@`M&m?Znnlhh%K@2lE>JK4z}5Vx9_2 zeB@LE0uQ`x#W?8t0^_qa@=UfXrCHYIO+EDAs15HZ_u{*yUjNp!mUL#lOrg^t`-;wAp$0juAUu(zi7 z$w<>A36m~PX!)H`h0A`0TH-<^w7+RV$*996pCTW;Kcd|fq%Gw4|F|BQHzO>hWatlZ zHZx%;w61zgN|6sq2qriI5(6Sgu=}{>W0%+s*zz)`9ebO{DC0OG1l&RLlyI729W*wd z5O9V+35)457n&yB7Br}k@(mCNK4+9RRZXDCc4XT-p*;y+Tt==c$AL1W3w~qi&wrIE zDy08ZpN2I<0R4X$8GfdN7 zu?I-8S{eGjAe_$cRT`z;vGyRAH`C zdepwDPg-<({MZC*ecXq*YXXb|tb4GR4?I*O?V3{oqd{{d;!B&~WpW``D3PTO3{QJx zn3;JdC5$qqYlqb2!=&0r8J&ca1FtQPOC*HL{=mA%POyy)AEgmH8H#WuK+4=04D|T- zLqR5E^6|DZyPifTI$AmIq}e@`6(tVA{zUXzdK+M%CAJzKiFp5Jgs11n%VF|bYq%NN z&oiHoGg2tQ_>#0#62D4;USpwUrvEDoO*H+2w{d7G;-~c=08I1`0DqkbY;M7YGo(X) zQ!LCJzp!C6N(g{e{A;5K5J0uv2B`N|=)%<*quHf=S^75b*m7*d_A6Y`hn=|C#?v~x zD{WI}k}3oI$aI@&WibiTYy7F{Vme77jm?vqhfF)baAjJe~MIT_xnleHp4*0DtdniBzj zcx#o#%`PEu-g*#vADw<#i6D0O<7seH@k1DwI+(lvaXYOi5$Op4evs#ISlPldL>#&@C8(}HB+ z@oRpGW)JRB^1o#k`g|_D!DrNF&orGLwaHeliqCthhb~+C@N&j&wj-gJ5=`?TH}uCf zM?$6+Hq5Db+OBsm4KLLp+H}+`5PNL;162qO;xqiwTTv!c>?6WQXZI_~z>_GT{3q0U z51haHK{X)46J&1=7$67#o@u?NPDqR_2igJtUxBin>9LkiLM6%c7W2b1E)*p|TAE!5 zuPQ4Xc}IzRBF)4e>8kh{04eeQYr>+uWZ(C{mRLGnOd*K@P!Ka6njIteY3zmxw$v+u z{b~_Ob#R|OP<+D~wDuT{7At}m)!;Jh(d?eM*laX=J=+zkwablp zGIYmu`}`tKqqh ze#H2Rd0YN5_aIj{_9J+b9hNTUdIiCHbKQ}x1qp+Sg=>w2ohr(jsMi4QuT)gCVKi)A zATE1O`Xc*P>U_jH@&<0ToH5UhD64I_*feyUS0Hxh7=GDl%@-rC8}SmWFyI1ogmvHq z+lR{b$|Ak*la0hD}$&&qkF9e!JY zD-TLz*D*V1pzd|$q4=CSE)%L&PX{v?@fUw2qb%l(j#?so#Flz#Ic*I7Sqb4T2P1@@ zCRs}pVC**n;uuf9x}R=Z8b~CmEetrC`q(6|cDWcDLRH_iU4b4}pH4?IM)L>2<7b)x zMU5>XGYC0nF5GgvvjAatoIcUD7}*nAICf5KeC z!Rss1hUqGUH}-+apP&uZSAbmQy`+Q30Jz#3J6Z$I1rn&Mp5&;kiH+Qj2Q+?Q)i(et_uxf+quI^l`LFX`}?7*BP zG0s-nX2gThYe5(oyy3&oFtw^ZQW+=D>=33m6WWta$Xh8935)6qM7jYe@+-$pq^KWK zo>=QcT3&D(`mC9QV)#iU?TRa3QLu048CSg8>YLDon9HX%JTO;|S;Yf<;O3jIglmJ~ z>$&tngXtJ~jXMrr%NzAr>wj&ntC*)|GD84{4$?F_L>i8!udAhTuDJ=2TM~oL_=j-%Mf|#G8L9^a|y{AO}UECXohtD zLOx9Xri{)lF80PzU23Pz*}>CYdK?(-J2`w7FO3%W4H2p&xHHjTcrgDYe3epcX%x{2 zJD3zH${L1tNDu#W8k-IP(vG_|#kEVT&P`J{S}#R90m{=Kq>gOJ>wQ;ClGSDVdU9}NC4NRiY*nca#oO7Mq;9}($hdPfv*^xmTq8V+y8yshsxuGq5R zstA*9A_T58q=9VtmQSk}d+jU3BL;b6=GhoD#TnZWf%9LQ$G9S&_~WD5D-DrMglQF~ zA4r!Wfzcg}DV@u)Iy4X4U5N9+P? zP+|*-4!$cEePPyI6C8^->we!7K)XBq5|x7;?{tu&G<=0^ZdHaRnj4a$O+&3<9}5v| ztObZ^34UM-`aYcPuOCcNAfg5t5m0fdO>I-T{^t5p#4SB*(tX3?rw0MGNu&z^}l%UV;U8wN0Zt zy3}ZnPegY;Yzc!;Xh*RbFM^Y$oDg;hT28`yOhPcuAq`n?sK+B&7yAhJQ%?GC_3`tF zW@SAO!r&KUyVZEQfa22i@l{hO>_>Izg4U3;CbB>6aJqCbb3-wg#{X!TD{ER6iULk| z3WZITl+FS(HStM~Cl;+eu4oQ!V_wVsE23rCS#NIT3{omM6x!eDAXRPolX#`l1k^9_ zF*58wri~<)gB48O4Pp=K@fFJ1fVTx^j}UoBAt4n6%3K4Jw5eTG!|XDT#4#>~syQ$T za(zz4`1vILznT{7e1G!f5d%72NUESH49lm8k7NC#Wte*EaU>6rI%Tux4INq9S4 zq5&Cq898m_^q31#>-eKPcaQ0>?ddV#?U%G+P`WzX$)aPec9s4=J~>wBL1_Gfk8rzj z&Ut33Sa*~von(-~v*H%9cG${;5n9dr+ihl`#b1kY8%1sbEl86Mib9F5eFEynU41KR zRQj^p!9Td<%@KpN_1#^s!IXqwXHm4|EL>LPgKPjcFbebK0)c018{w??JirIz)-V3I z&*eo(SHF_~i=UqRGVRhge!yIn6bETH>-)94;9VO+&2^s7#q$tp?{3_A|GB@9i177M zTyL(XD9g#fgBDcN`%T2s>v1%1^}Ad9>weDVX=2<~@kv*dIBn>!Mjfl<*M}!fk?1Up z7dleE2m7_>@*GM+*R5Sm!(fE7S3duy>qXDg1fR?KfG6rgn7!k1Fjpu69OTUZPU} zaJO6G4L00(49(gb&DjE%_(#+Ezke8iCZWz}@%_t~19{mVtn=#; zZT{|zKsl#Uob`vZal<}|``65uW^7i$zQHvm4|h8Mmvf%oV-limH+tg*NIX%^P2zId z;lG9wHWXHsNizEEsi=?T0#R z8EayH$D4wyWa@tg8)IxK7$wy94k8g~Ea>bcOiaY~JgMwR%i;?O3*$R>N!b?}9X$^? z7f>!UqsMjeyc!ugdQ;k-E8B(mo4$OGaCi$ik=JeK<5}x9H!le=4aD&Bl}K@Yp%~Izy-)_HPYCX03aq%l?}G6!E!(U6v*0t;YB|gub(t=Xdjh`|phBwR$rH zrK5S>Pd{2%D*f10n$nvzarQ&%dU%Y3Y>f;VfYUiiK5)8KUo~PcznJ+|GJHN{)kSBO zWBIU*ca&*)*zV)r6{&0gW{J=B%`uJtBg$|~D172Tp5E`DEoOuJvIw+8Ofp08 zd{87P9FI}L$2>BVst@`?h^-L*cx?D!JM;Vgj7vTrk*0Bl(coSrnl5doU{p)y0y#FR zI5(3RJFNs?*&ZYubA*~1eRyn!F7Ohf{bB*a(N?R&Edr(Q&l|Pd^$)sne{%WquqH2D zjA{S$O_FL|#xgwq3z~EEpm&0z+`l%?f9(P)_+ME8yTPMJFUhIv04R?T^Tre$w1KTa zQ(Ne^EeqM)KrFXsW-|ynpF9ZnrdWi2DDfX~iM$^1DSA#!WcOa~B3zDf#_ewPcsRcF zvl9x$te!W+Ff-NZC}Ir&50aET z^M)-N1dw0|I*J&0zG-I)vWE?XX}7lpkg;^VV0j8Dp86j8kBuwro-Ti_-|(-{<%uG4 zc!5_W77>3o=poi3{*$ERH4VE;}mV74U)nI)t&61>kLNz! zojzjUo%w}ruFlM83trFlMLu!qmGI>NOlCvLOleTCjFITluRp^9qxj{MV(9VaWF((3 zZ`{fE{sIAPfohV2R4HCpU1`XsQjE0H`b)fL+cO06ss%~Yrq2CA#(!v^$3SKgwtffNlKLPrGI94)p#D8tgEx*1naX?+ENxOvV15bN70%XElmS z00N}1vwK?1u(4m{0c3k7M$7cHa0BI9NOG5Bc=>GKmQHOLM4$WLdylbZ_Mfgf5re&n zcs6~RZEPT=iKeK7a$Z>(ZmM1_QdQ@Z^w>mp&)N&>+eO4s{0Qy8oy^8hr>GVOc$fWO zm8d>O8QGZ#Z7c_Ts6&zC_1b4L@HgiX_87;j`8zYZb@jN767D#}de%So0$psmfARZF zsQKzRZfDDQpm&9B7A)r8l6ly^wJ3k*bFrNDm_w# zqLu%Y^RhRDPxxaCuj_#zHYdFoy!@4y&yTq2eTVyHm#%7XZ{IDlq~PHtRXExM{jBeD zR{HYzgtdS-eTd8OiFm#v?5m}c(R*IgWx=XHV%%a(3=z-nsNoYHBnGd$p7D)MPR_h- z^j0?c<7IHiQDsEWr0Y*v(ZrAV3~M^C40RbBJHboGkq%a!zi|maw@jM62YEQzcoQm# z+!I4@vo(I(jOE$%C%Sf>06%)I`Mmo%Hm`dgHpRtH?vLPJtPnXE?WQmf?_KkXT4tAU zvM@~;FaA1U7u4(i+w>m#QvV@Sx1Y$NJ-qza3RH_;FrI-YE(%JWp!drFUQQpK$Su3? zxzFbQ{h-Vv&;1)x>NjR`?Ag3Di|_Y(GScc7ORGmd$Kw1fQnbSTBj#OgI4z%i3>R!j z8NqBSjg%S6c}ervdjTiFOhG6kK8O7)0;YVvRAtkOX5wkRFJ+h|4$NmKy9uKMp}}&N z6|BbzabLV7T|SrXj6l;#Go=wX;ZY0fp&?4iRq+ z9eP{c;6Ftn7plTUhPK7aqay!*yYw)y|2PZ8c8C9v7K#yh*zo2RV z5?cLXG*YhI31y46Xe2B*GDCVnO?F&9-ge{ewfGsX;l&P0pLf);*>VGrA6kcPE~7Rz z;F9_XRb98G^AU`@W^0Rdl_QCHFlwVTMOMK~Z;@xB^Bog0IQGRD_T4O-Lel&9T{?`N zw#{eU^mJR}rVOJR{8A;fMu*KPNRg5(XUyh&|Mq+yBK~>E?|wZZv1HQ7{gU|Y=9sjw z7Ea!NCF&I+Zi+0Bk645&CM!-5xnU!cOTFP?_$L=45Zm+6BhJ{uA8FPlda3SS!EU7% zZL|3-!}B1^<3T7>zysazd1Vn3UE1+n=-$NMJ$8{q*cZQ`z_fWgoQR#v4YWyh{r6kb z>n8~b)wSCkkL^9c>m`^khKeqBYle|UO6nWj!a{}AyE2*Zdy#GRdH3p^`zMEvHjLDO z8|Tx9L-aMj&2`V6WxC5@)3~j4R>P2|eV|OlCm9-)hgZ{p?wwvrx^9oZ-nhDr6%rmw zoJl{SmPiQAYAO1${cr9XcG6jWu}Tz4`d)e=dt?4gJ~Zh%&=?Z;mw^dh9_|FlHEpY3 zqZlu_=f`I@CMPC$Q}5SciZr4(1)CE$@S6Xw-=jC$ccFsCq0VQ(cqtRc2`_ImkVPqa zEZea;>xu^kX4$qCc^%?&P8`m0;izvcn zv4siXIbP_^)kjJE^<%@3r0gE3;4!@ZY9&W+m4|3>-*4vZv&&%eOA+rnc1$ks#4dqVjJn!HM{;-x7PF}Ys`nOwzO))v5 zTesD1M;#T-$;f@VYLD|1Ev>Yn<+@qVi=O|Bt+$GbYwOa6LvRo79^Bm_xVyU-?(Xgu zAh^4`2MZ2?0t$Bx?!o=9etUeU`}FwtMU8z~7jv&Q=VKE#NuJ7i>+Qkp_ASQtmdH^_X^ zjWMc7JbH#+E65$Mu*S7VBJ@h8-*Q4>b$R?tgQO21Hi5Y0o_F*82qO_(ih&RybtKtb z1l?k6KqMAIDgs|ETC^98lF;?7FF)UCDLt1RjhS%+a#97xlPKIN^$jkOTyc9n^jcf( zDEI)CeGrV?8%^BlJj@^Mr;u{G9g^`oQ_GjsFd$;;`YU@T>=uUcyWA3RUF{nP*}56@ zLcAKC84jIO+I+F`z}4okl^|aj<%vqNBYrcm``M$X1#$l`%SA^h8c?oPE;m;3!>qex@9Par zQ#6_&f;BCA60tai!MbR_M@h2(E%H4Xrp=+b>(}{U^&%tj|2m5QdHjM%!HPYI;0I)| zlS7{=iMgTy`)MSU@34pNmb^%m3f=tmt?J=u`Ajmi?sy=4rw>li7 zp(_@Otjp~!%t1DgAM_s=Uc-PVixGCSgo9i@zVPz=0Q@eG^8uY_Grx%5!>x6HoE&Uk zKNMOpA!Hf=t*XMgq{p<6+i6?;Ao8{NO?tF4?zRtSLm;T29MU8ZHR49sPWNymO-7hH zEd`BN6%88f@ECICDjac=j7-XbI3D|=+Am^G-a;&BDH=K^6{#FE!J4IM=HLITl!J@i z;2;F$xB_ZrnKZ_l7l9V9R&b0Wll>Y(T87I|s<20^#@ORuf)K;J(OA|0aTxyIp zBR064w#KvSzYf8195CZnB;>&;VXx;`JOn)blbkug1%Y7Fl+|tBsh_O^;K&ap8tF?E zIpoiq*y3X7Uh80Vc4>EQtCtuN(bCI>H_iC+Alp&g{Z6++!=++DmV;}4FWG+W>M-4C zWZ+exEYs*nep0*S%$aWwLrE}P)EXD3wgZ>KY|ra7_btPSFY-83XL54NEN@(Va%$(s z=#iX?+kS*1@hvZ95=lPMYprz6c}`(#r0@gW4POBXzRQucC z`(Q45s^h;LI6j`s&?#un;SH|Dkb1hQOZPN!Glc@410N?j^ZQARo+#3nGXOF$<0!;} zQCk8|%p=sJAkZ92`5JsPD6W7zi2D7ChsW2~A;Ry9gQ>?HDmT}5$7KS%7vM`gK3a6v zu}-A&OibY0J90)gZ*cP*1H_hqVy;j)f8bL-3s^Fp-SQD&{!%)Ib;INJhb67U;m%i_{pl%T&5=5vfh``bTjqVO(G2zn z0`&daW~l3*hm}mc$!VZ#cA@Z4`B6fi_cPvEha3AdU)4^tToRl$8Y)dvOvM*O6tZmP zqMW4xGe=Q@^;j2?VTvroOp5{Tvr}>M^+ZrI#HnNr*bIg?AE1Tm-(>*T;fr>An{-Eh zK=O@`VIMR(XpKDb7bb517FUsu7c^NeBqw> zy}sT^tAy`_`j^D#*g~(`t2*$ z22>fDc&*iiTJ?X#xhQ_T$)~JLkeBLt8);f{GfN3|5fM2pn_Jmb^|w2|Q(+&JJ3B;Q z><+2;^r0y~ZuU7|(}G>ioQ+I^o{1 zbABc%12M=&$Bd!iP}oMd3qru*fz!bm2eszbdONC?p+6^=-Fnn0@rNLG8g`@f{Yl=& zvd1`O$8lNqM}E`+L0uO_r8olWyJykU3|d^;sFPr;0#W9`t^km=FKjKqkDw$}tm6j9 zbG9dU2q^`E*Y$H`LU(Lvtk*j90I7g2Vb!!Li-cbFH{UNQf2)^?Uq zGwSlI?LpK6btYpgt1pUOMA6bvN&@%%L4K|d*O-Q@cQjm%zl*AEu?9>yUjv|v(1=uN z>62v$TCyDc&PdvKmO<^T!mi1$KG>rPB*UCWuLAS*@}7pl^V^o- z%n7HL(_!eX(5U?uzvIsA1d~W_#$aJ_Eo>2V{Nc zCls1}gtLHs4Th$QqvK{Cmq(WO+gTD`H2>#6wB3&bh>yF6Lhz*}SX9Vzq_iT-^kVbO zVhm%+@3b8PFtPdo&?EuXq&9bzRA<2-HhhpV_kvtUoFrF&38%V#3NQZmtLw_6CQVm5 zaw&2e5ju{r#%|*5(X(}~1Z~!|IWmH>wr|XAg%E$p(SNAQ{$RJk=MMVfe-o|D47Mo& z%v%hFo5n?INajoi`6w1$*$dQvkfjrSBL~HQYk+Rl#$l@rSgj^hQxoMA`MNhpnC<9C66vy6Gx%Tz0&&odw)sF-!uJfP0zc z`6G)!y{kVdN-m7MnJajT#IPX&HQD-!Z zR3(rhE~q;y|8>+J7awmcn1Zp$F#LkE!Sd~n6V06A5r|8)ydHl2*xp5js_B> zD395)avg{;*^1ItN-P zICY81x1S?xj+OSWHE;R^E^U>{& zKbs&$Y{)p-Zs8s@uLxGrgKw_G$M)emt}<4x4>#M6v}ocgHf zH9rd$J#kDi0=I7~iyt=`w?yqZYpd1zZCDFiJJHhZ`!MS-R}|g=tlaFezJdPSCCOy3 zhX$S|TfOZp{`W|mUS`Y);e;b2k`)&UlPX&)op`VB*Q2EAaw;l~*z`<>!P){bQp79K zD=W*Tdm)XAgCXVH(z5~iPLlFgm*k&le6R*KJR_G z6ftt-me*x$y2i~5*j34LOj{I=7QXPoU1J7*tF4l#v=R|=6s4-&Gk0YuBFmqrz-!(6 zFBWrl9bU-ca#MJ4r|Fj+sIavbLd&p~C=%sz6zR$D`3!#Df7)Q?ICZ)6Mu>r7YNp)< z`|@lnc7kau!jv|YLTanD7|f*vTnH9*My)x%(B{7Mp#jsGF@S;Vcj)PJ#wtMOo({yd zHoM`aqePYz-puSw@TQBt5Cse!uNpC$vNLUA^k3SbaDj7SJn%wcFOD0dueuqVo&xJ) zcmC26csumN(juXzn&255bvO=!U{r9KW%j*l5c|I& zs#Y4tB~-HT6^VC#9sS9g`yck=A0?U4w|~kr+G=Y5R+)||Lb3;2Q%Sj<5Y>e+_``H#FhWrAew33xtMy*roBTlM}1#_uJiXK!q4y}=)4!NPr3Ts zSrVV7Cv*BO(hWjZSHWF3cTs&Gtd@~KWdb3uv@d_`+slKAc}dv#I6Rnn#>f!z%^H#4 zE6hdx#za7F)C9MMo9Fl1&eJaau9GosRWZu39JRNX)gG&NRE|6PLxWP zu1tzWEIPjwYo{J#sv7Mk9-=QFv-2ZFidE@P>#_h~Fro>5KQ4KClspSBGuIRzZZA}N z6!y#C)->3een!zuR z1>gRMZRX7fiGar&-31%m7F&~9Ef;@4+LxIElkper78j!1`MNYP;?)pNiTKr3_hhGy z+r=8W))YFu&v=L}WwHUEzR+xSk|QEhvlIO9l7BWnX`8ofs7vkXeYtn1z&?=@C<+_? zDy5)+m3Wq$CluannYeeA^arPJJwHLp!jl{Ck59=XX zIkTOxb~#iirk0VbH$-25Aws3~DEY`=suR!X!}U|nOYcL^o4-Zw%@OZ37!U@5R^q9|5P#4izXpG~==u&yD&Wk1pf(H51QeaEYdaTV*&k zHMN!tisheoqIRn-X#87VxhY2shCWY}G?)lh^Y~DH0o}K=H|oN&^!Ni^lYC~b=r#su zA!x`&@PY%ZMCjHStNc9;%lK$Y9alpb4{G_WC+yJ%U0Wd|Gf}G%2!F7chSOrFhJz`L zK+Iz%NC`+gojCcC6XzrJ^}PfBwVC^4k>I6`b$K2`$tGtXE~r%H;Dw*g=v_jO{EPNS z!;q*Oa_(js!G4I-f1e%e@?A zS}!|vsC8saRq;$=SNZsyiE*(iQleAdCZTV|80pQkdla#~Bv;+3nx)fYo*b{w3&dPlaidtgYcsy_LF965D5^FHHLpoa0X8~uN>I+qz~3eP zvYnP1Qd8U-=C1PkAC(*!XA9)}Pr!xKZ>Q%kK=z}tw#k&yh<%o3^B|yicMG1@wRq50 zRebG8suhTNzobc|3)kmPR8f+BHiI^MA>Pu3834%a{{fsV*Qw=!%o<_0>fYy&gwIJM z_c0twf#P5(pEB1>>$8RgjQ!eqkhD{(?kZpMERPWQ0?L z6uy2mqoJjZ&;&0e!5*B-4TMyqng-?$!FGMb+%S!a76PSc>cj*rzUf60bt+P`oip*( zOSV{qdOc@G9_6Bj>R6tw_F$Zp!O*y?N)uFrddi3#``R(~=FEVqx8_&y(OaKYFcVEHeHSDQY0SP164tR{YNse2M&5AQ1(?4c@php0& z?);&D&_AgTggGatyXi>IY;DbswW`rrzgaP1Dtj@lAi$Z;kOLJlhcxQMglNcJ3xD`S zmX$}c@bic56ox*&*>f8&R6#3OPna~*k|zDxOOE9>`{+ew<>U>kj8^HsS~oJbap(tk zGKmyY2{WB2rk9q!Nk9wGCTQv{%MxHoQ`T8KvONv~j=6Z^^z|p+bXbDDbag!JPYGTA zcoOpSe1FjV=T7OUPLqC?s?-E)cuI18p>96m5N03UKOc~xQF812rPyZe> zN|qvhHIyLbEjoDJn1YMPCvr*zeNbiv9PGHc*DGA2y*gu8GR|0yOF-Hl+hKNoJrdar z0Qevo`8R@T?I$G_)g08=O1x+uwzqEUF9g2&8G1|Bn z>HIjh$=bcWM1xXGuX(+l?TrgV5cuXl3!LjyRl>Pvy-VReEF9h72~xjcJ} zhU^GfV^&sgB4kR0Cpils@PC74<=adY^z}43mB@A;ystjatDJ)61$FP|U?!s>`?zSj z9WvzDImB0k)*OZ+46AJC3I$i9^hG1Y8xY7mS(E!^Td2Q|jo2o|E)}pdidk02P@k{O zn2kI;Q1G#HD7HY*rp+)jl^^1}sTeSo{<*V8I2c?Pd0U;M+oY0kx2MJO;n(8rn{4uL(J^o|5Jy&qHsCuyrm{`nTp{ z3+ume;~zV2V7GWtr;Iqf5!|0&$tku-HX7tCLR7?#gT{#nW?15QT7&TX- z7zFH{!w+{nq0KiKq30{k?uUm%XCP;~@&50?NLd}EW|^4T*>LSF8)Bi4 zGqLrTLw+j*gmv$i%d3m6tzVWeLg2epAW?ZNba9c#F!0Ty`{hEh`{`io>bRU_doOWo zREnf0F_NSchbbLeVwfHTO6t$`+>FWPHyU1o6&{TaE?!T3Oy~-T)R&^iq@zWM(G;bt z9{%aR;dPKX>bza;k9#H@QJCV-bG2{j_4it;!TZfd{RieZL!;1-&L^1em;37F8_-tc z<=n$HbnRXBTUX=DTkhjZpkYY2g=PO`<94s`R)io)UufjAS`0CWDWdp=MBb3}W z-2NaG&-DNzp_f|)zh|)dbN5yK~2F zI5ZvT4edup0Nz?MwvfuQirEqHGJUoAQePB!W&0d}di{4!FmM#u_e4pk2$?%)N|$}L zj>v!2f5CkX8g<;u>=TH)$m0ljO+mcAF29`N_aR~Kd!<=)^937EW-?jHh}7nUY|;M8 zeovsA0Wa_1q>{?%tzMKCRNcW>g)$!RvC^aumf~eMdgU zyYvf>nPaDcmzXRvy~B=A*5R2<&I3{8AZi%ByNM{i>+}Fy&^67783fQsDJm*C>bB`D z&F1|0djPD|b$f+A;;*e&UG!+T^Q7OvKLggZg{-pHHGJw?vpEXrU`hMeJudTEj}C+Q z?~BZ#3;Kw)Ww;35Lh_96^CP0u{#J6ZJI-9iHCA}3jM77IRukf15crghj1WqjU>Yl6 zK~abheOQ(1pEBj)#=}UM($Vl+p6$1SD_o0aqv0)5cbhED=(PCWm^=yT_I! z1B)aMAvKK%&s2CE`0(o-T4@I>4Ru5~d3`9^TyEs}En<|_AthB`@FkGXWfWdK3OFw$ zd~Lr=F%f;^I|%_+1nH|H^;D&;F3%*v@4m(pEjLwSocgF*$@!Sr*aW2=m`~5La%WZ8 z|Jn$X9}s!*ASGTLw4xDBaexka=Ua6Z7n&6syvOX3#IbOH<^4F#!F=f%DoRe#A zRd%MNvgn~I2)r_4p(JE!BP2l|DVy8fw$&!=f;BjS=u6uTm*qp{#Wa(iw3j@3VWGRl^{0e-J1(O_EJ)s>Iic=u^20ko{s4I9{zK(Mm1j~O-T)lt z(p8y3pJhLS-a&-U!L3n1^Rd<7H~yfk=>l_diY-!K+?k8cL*zbV8@t^Az9T24tU-A5 z_ehvEe2~LWh{|f6NR;806oLDh6;;~7yG0V;IWfg5X~evb(bcsWBTGE6O70{oikqrj zcz;(I)>eg0R0dBJ1~)Y#VeQK~gBBOO{#JH1-T*DHFMOGI2T_fNQ-#+MT7RN*Wut3W ze^!-&sQR6L-8mtLEvnjsKD+8iQ%Y$CW@9=R#B`g{IW6Q=G`ev$BoI{$ns8@N1S%|s zS!`^-xLGG5WTV|5fr=_4Gz~vnFCeI6=OgPV5IdSVS{Hcm4ZTV(XA=}?{SY;5|J<~? zKSQSp9suC-s|m>*DOXhya{5-{HxsajLXg#|!-th+Z67^<9DD?g7{)xu3^yEAqq$X~ zIoTw^WrFZsf0-?bGBQWzOhx8`7b}0coAt|JxVwUGZQ!wH@Imd3Lbvfp`%Q-9G^ddfR($(TNq(D7hE^-VMIlwEkYL`DHP!niy5Q>H z>UpOBR?n9sl@oy0OLQJl*N{?zX_F2^DIv|>`Sw=?Ot#y{A@IQ23~XlypB=zRoHaO| z2r7sHtzc9Ksy+9Kwfq8A-P3DT4di*S)Ht2pYQB4WZ2HntHRhDdFnO||_qKn$7i0LF(YbU{1OYY3`d z4!Wx}_*pf#GHE}|{M>6R3nvI)9@&pb(twj$>{bAcrGV2;oNhiu zQ&&ssckm_`YLhels|WP1kl-ptoV+_J1}BB6e>XPo=Pd!mxt`d?;oqAzIN4fK4P_hQ zc16)Ic!#R^C1z|;8~Zs{7ANc41t=!W&6(dp_+Oo}exo@p&f&L2@et6eAIWqz@STb! zOrMLxZj6dahFK}r%OTh6g$EfyR@b5c_yVh&VJ?C&K`r5dJ*aqh3kh_Vx5}`AT^atKDGwnxy8v0xc0vvMt?bx-~G$l>h z6Aymm=vg+{rhQ`h!z}exIoP}g#lF_X%{)`7nXprnb`_oTXj0mFcPQ!O#fYI8E7 z-Yy~W5mWWM+1RRxl>xot2lPw@LM0>c)Fy0>Ay~#JI7luUtWGHKwqxXk^HsNs=P0Id zkYoag@~1MWLI{R<@cLoXer^K?D&E2IW2tWq?RT)vMPaHO`?G2w zZTShKp3JYbe}8#kzciH;*ZO6~_y^4`m5j*xo3E=zb+)@WN(r8th$VV4ni5Hd zGO@2!BBT{T6B?+A?GQU~J+VAJh&DJ%9AvnvNuZ4RVmU>zh?e4X3Iv;kYuY_7;({|`d;%9Tby52Y~gIU{!gPMS1BqfyQ6-jI7LfNeJppJa|DRS>_VwsUX>RNJBPomGSrz zN?=lguj-w)baFK7ZDJs?FWx$rq~>%wiYt6;^&j}{oHUoJ>Y&zAxU7+3<#B8kh6uw> zLWp)KlYBxCGhTV06SL~jX%(eCneEo4L6a(xq4FXwI-50Vvjq^31`FQrLJiEB)xXvx z8Y~2KV0Y&iWE+!mp@4Kkk;IUc8K&y6k(2@J>d0YzNcQmD5$^126P6$S1`I{RF>~EEupP?Ue4y;upsf96gwN+_3!STzkT{g*DKL^K z4yr}Sl}9P?evlKWkrRNEKbMNQZ6&E{B!1~9e%DBdGJsHGClKq$nwm<}eLQ1p0rBy% zdzjBjdS&3RYJ?k0hsZ0&>!+jah!ef;*gQg_^AM^(;}uTMkKYLlBuUyZi-n!IoKt;G z)-9QvR2}^0jsRuV6HMw`w{19!=X2o+;^9D5W3^14loVrEx~Dn>gIct!rY@_YDA_*1 zBWtxnM%~1zlDH|kV7H0{uqkU-y^YFTdbs39Xp$AL9m9IM0V)vRA-LZ^jX$em-sLkN zdc{ss!UWBqE56@Pp1<>0v*Yi2sO)qt#k3S-+uSJPw}PO6z^Hvf>U2u!5gW8(cKD~? zUv5*$@oGhVtDy|+kwyP)-y!;yw`HIV7oB` z!~g(E{WWX{3*!@o*7~1nPwXy0cAT{(9^MW3hgegST%mV&T@#NyCV|K~i{wx}3sKOg zXjkfkrxn|2creHWwq)@@ft>0cSIt3d+P$4dEfpOIH8rvaJdZV-omM%Xemic>S$`t1 zwM{#l-q^M>OvZ@Mh%0r@51meD3qe;-kPwjOrW~RzEqAjB4m-6^AGov;=O`$2x88B) za6(_LVBV_&P{fy7hP9O^tqDH*$!asUvK+~pSkdh*#mxg;9|_s&%%@5j#;AZTbw0YT z&^aTzP?%`zp*}cxwd*MT|VC}MCkC1MqSCCGN%M3yfjJ@ zm$v3x)JPPJ_JANS->TbKt4&hO?+!}1W?%+&mW5TR&}3{TC5)Y!jm2~C?xt+m#=hIZ zSvLLQkPB7i_R!xf2MSI0eV6JOjL|q!Me&SqNunZ=`}d4=7c8LBaGyq*@%ue0xeTDM zU&dOnef#L#^iNC7v&I~^zU1Bl#TuOyAJYytzK{|tc|Sr+Y3Y%J)-2v^m$f72zYV{h z|84l?vEa##I!LfX)M95R0h9p%bO3Dm*0VMoXVJ{cw9Z`}cdpx$aQ*&CX-i4P=49IJ zjW*FbLyr{%Fc66bjB{(t)O3+(0scU3CPPJGztLujD7C=4)03dQ@ivUc_9xCs1N-9Q z9?iby6N)f5w^o3q-XgYiWgew06|tnXI&oB-&2trdL(VBxa0tgP@ABmgL3tMn3aKsk zbUdA$5rl@4RFSt=zUnO(=tt_5>q26TmS26a>;Uk!)xqib7GyvEYFN#t4lb)gcNrkG z>p_&ONiI#wio)Q8rygk0B3L`R&(K+4%GaK${*t;0w<6|_x3sSdp5BCGRzvOLjPhx@ z(#;^(8Gp-C>jzZLk)5dz+9$gx0otRVkE?cAr`ugy=Lao*kRK`kWnIPKIXNjiDXCJk%mt`=KJGfhb_a)~6o}=ol_Wpu9 zxAs$O2FRv)rjgf7in}PaaX5?oE<$2UcnR9iExlG1Y4llZv$65EpZTz7Vr@1jY>31woaE%tDcx(Xet=oq^`nU4bq^dZ=GT%K} zMV7U=>W+cT%8c5PZpY*g)&=(F4ecRDPPgC91sXZSSaXYM8crFAJ8h|?MQhbT|cNreraQ*abX+5Wi!lL?y;oXqXK@dHV|~wTu?2{6wIH zwk&9JBSXq!Q^Z|bs{6K61r45cKX-*E4Y19tGnO_^iQ>fK27jNS; za-jf3|0#^|L_aO=3|gUK(2bgHlK9SzEu!deW0O`~SolfOOmJNh%Pk8b%air5R*uN2 z@giYqMYVR!-y(?$Mk9nyb4t2coZ@aLR-4~xMTLBcC+UR=qPjikcN*U62ZcY~Xk`(x zi;ALJ4aEJAURybxbg*W|CcL_0iYV3lpnzF)+DPc&dDyQmW>=zuNk{tp?@lB4uUY;0 zk>~yI>Ww$X?+Xjc6I#w8hKzbL53O*%w==UH$Q*Sge!Moa>B7R9>Vn#+O za?ObpNUCDavDR@=rkTBMg#pp?Gs?#niS56^mw%|5)i1#2<}nMT?@NLC7-J5;@LDYG zN13|bhMns*1_SVb{OY)Bx!g3-&5`xj_*BU`7b{G}UKtuMZ0dAyhGFvOCKkLj=e`3# zo;VAMhbtKaFz(E}#EQ3FHVUTXs;k=iU3QsKW5zmGMa2V#Z%we{e9_iv^s;_1w{L4? zuT8IRkWz1HE(3X{(zZZNqyKb%=TOUG*er29A>GjHQ=tDb*?QnsZO1cC`hde+L5&-0 z48n8-7-i98){m7U?!`zvf27FsKL{|~^qHt{bQZ(5amR{@#U9vm*md{Z@W#04IHl6} zc>;|6v7jlk%*{nxzl+SiUUx+W=_l@vQ(ue>qHZRD0qHLatGeo`jDz0Ud{;+r?Ixp; z?R#i+AAU=Y&L11?yHjfp1ECKa5yBf!dg5XpnEUzhc6>p?8+ThB57X`2b^HOL!m4X) zf$c=LKh;?9&09uwznU@eg`Rg@PPd!BAb&tdYQ*lohA%giAqRHs0q{Nf+Z>K1#Qe!l zs&16En$d9_jz@^v4I&TodSL6jU*Yn6c2PTS5>B0`4!G1)nF}8@qIqg}o^9)YK|Z{q zJiPDpIpgGB8uaxMdv4s@zW!-y&B?gu!+V;DGJ<}fM1lRi<9?`CQ=$#s*N$nZgtuO2 z+L1xh-i2BC4J%oGKU<@)T1@IvC3dlekoJTme!h^CQmRcMx=j!yQJAgSyDB7k`LC{#t)Fh^3iXwtM?!ka6jlcwZ&8|^sR$bUvvXJ)p-(1XczCv5~CG9daV8x6}n zKHbhwpS<5?#6{FqUxqm(iCY=NmUKSnSO55}n)hYKgtn+{q9B4RgS>CPdO8M#K9fqB@`rODxpL#O;WMN;1LBuLE@uQtJAV;z3Q1QCzBfLY}be=zv>P;(>(G%TP_~Na3SjMtNJo!6}ifyqwaG z07CXTFL&dZ9b3q`OEdt9;#?zg_wl|M4Q7#+{31S{N|0x>ftBh*ywQ~8&4?5p103ayye*= zjN(e3^d5na*ymPBAu{`3K3T2DA7T>CA$nJ1+rfMwpm5~GjhjR zO|^Ynxq@5iY&?Qv=IGO+y*KTe;^m6M$!Id;dB71|P+It5u5(CLh= zn+3k;e}_&?sn1Xf;E~?+S-`Jm*hgqObK#d-e%p|?zoXx7_%YLs<&+U?UFXMh&Za#X zUqPdnaL%nC-kE74uY_dc;6vD)l9G{`GoV-BC*q$2Ht+XFB2EdyN}&coPsW*T50Ux( z1@S-r+3Pd2v->i~j^S4457zrJt~>DO`M%@DCFBjwTzyI);*SHn#gU~F0u&B~M9Kxg z=d%A78&0`roL_LQ*;)95+seYket(22+-3E>7XoMB;E7>T1H%nSx?`0d&Llsgi!-ta z2c^2UMp4@x`ia^%WcPq308>mgvq4-U;u0fMj!mT6KnH}Bxs=Cg`ud>Fg3(;hjbJnz zcEz`H%W|5sN+1Vuz^w_7zQ>>aLz6w;Y%3DTqtnY`o&+fpUrd`?n7*GETDRv7&gIvy zvCQA9=lb`ZckXiQ&64jqgm^=gto-YH-_h?cJ~B=uS-5zkaE!dMp8=eFKiQV=cK9=6i{x^GAw-6+G+y|uoY^VD>b#e=a zA}zaOzH;hd{IfUuw>Q7P!hrIfCM;+4OxpBp_nM8*7hrNR(;r3-#)`W>WKQ){do8Yr z5|#lD7?Sr;BWs_6^RYogWfiRprhaeM#W!XdGiIvv~UYaDBUms{xCO?7|f8nPfh`^J(xf4ETBR}#EZZM6g6H+i-dx@volH@K6g6e z==eku7nisLbr<+@dAa>N|1ECO3srcU7vIdu_>*w=5xM!S^?HXrKe6`_84@y*>Kp0v zGwc;;)4$^|7`E|^yHA1zKXmwbfFe;ixUteYhmdHH=G-i1k(Em@KHbO{62hk0_}bKt z&k<4bXyq$obD$M2$@vpfq4wvcty_XP@bGUS^tK+$vSzUCb8YeXciTjMYRbgQk1+7( zgQ5Q4y@{)_6n-ZcKe%GG;^muRVz*ll@ctNe;#M1fv}mvVx72ZVDVr)e)+izHo=oa{`h23g8?=V?>nX5f_u>B#&zric=r z->rR^e`*v=6)!CT>JnLhahentr4-Y~n9KoK6I2~mT!^2io}4txFkZ4`_&L8AGr*U& zJIxe|a9=vo;%NM+k;?X)l5S*O2{w^6G8-+l3%EtDZ7!-+h?f@si|Tj;W|+pqaxf-u z#9gtdvoePz8R#U~Xb%JCCYx7I8Hxp^c^U;SK{dN4fU-6KSlVXSW8ZQw{-d&>8kF^; zs9i1Ay+`5bSXpK3`N>)I?g-=m4Er$uN%sN|`}R?QJfsHrmLvUi+XQ7h2a?*^zCyL%|y5z2EtU!3x>jJ5M-r$ z{=Du2Tm6nO?@028Y#qC!U8e*|(m#%Zmt;uUurEe(Tyj)y_ZX>LyrPwc=CU#+fPnt8ckkHSbbK1_rjS#E7Ua(MVRp?2oE zYd3g7FXlEwj~>0?mii|EKSdrhFm;~^JH6yz43cWdPX~_#phHfGTFY6KkGO81ZpTeO#!dcrEoV-fUtLrpjLr8V z-aV0L$q_u5za!E1VMdeIGE#|=lZjCk?+!I49yrbU;5HJ* zFS?@hW&XH6B?X?BA8tMKj9M$y##sU0^!KGDGemX5?=`W0y7;)YSa(`-+yE|)XG{zn z5r3|6N>iVpA#0Fhr7QCv$ki6{!GGRl^Ev{%>-YattN*B{A5s3H8WBMP?ChZz9vDc^ z!^)g_@U5m0)XAA4ou$@z0#jfJ{_vbVqbZXTo`du7x+H)>J4D#^MwX{Wq9OaHPlb1S zjY2!VW<%Ctw7)xFC+uw!Q2g1!mlid>HH z?&{RP80)l|#;9z=VRd;nQ11I+YHEW(aVxhS3klm0o@eB@vi}GQe1$s93lwTPC;C1w zw}Tqa!om@j6b*lijBGlprqoY>veOQZ7aH%>F`Rzha@~5d#ALjn+YmL)pEyBoVXaipW|e zx0+vod(8dJEi~46J0$Ky3xzm5#*$ zd(MnEskrm}=ki_EphDGF^WN{4enwca3ZR9$j#y z1+OlWT%|sQ#U9>F)pW$5CRAtu9zMZ45g*WWc@nxoFJ`*|dk{by2Ko-$%t~U2)2vYO z$L7BTQHD~qN;G5hQz))v`tzFw1_-q-Wbz7eL1oSrd6ii@!jYYDjL-WlTsZyaRbnc~ z1Zqi3*7S^JFM({D{H+`{W(*I;MmN=RhKqv-oJPGl{KW098Tt2~ZueTs?7U0k$ zYMHM5f6TpQRGVG5F5H&lR;)O*xLa^7?!}4~x8NG26l-y(P&BwhaVajvf+e`Slu#TR zip!U0zwh(xcb_xP`S*=+?vX#STh^LuuBq2GvLo*Qnl@-mjrA=~_K&^8MjPpMoQ`1g zEUB~9l)tgpWfj%)*&3BKv#|8Wia_?Dy1L$*`8WFDlTnNF^M=Q8cT=rPZR1$CxA6#z zMq_?7+Xm>6W#;@C>+Y66cqC&UR4jYs_ZGirsN@n03_O|@o(jr>Q|_|uR0IX!`ddi3 zT*18k15dG$MV>e({--`!w|&@dn+ar-cdsG45FWMNo3U!4(+cOuu((VRhWz_iX*I!p zT%TLR7#%s}hmgDv4szrKREe*MY#;hb>{NwbQP&iHaifg2ZjATi@ykYZL7(Cy_P7*> z^oMUF0%lKJ(+fJ&gHQ;9aN`(dV;4O%8~JwJwc~Rt(J1}m78;jAj|BzZ)BnL}9rb^RoVtUcgyH1sCM3DNr zZbo}g*pS|Pb&df;@O#22h?C)kZCy!o4djF936o&kvupj_?JUmSt?bKn)LSgF>$?i) z`Pbbo7_HTBvxG$=>&h|J#?&l5TnGc0Z{2<^ygtYOgSbVmI2^%dlKFK{l=wfieZPLP ztnbz|-&3G{eY*J}ooGk@Zl0v&*Y6mM+Kk$>U{LKXpu~=5@ahob4|$$1-o|K+)Q=OF zb~N4DbM;?X$lP0Kok;GQK5lb@LHAPV8XmPiuU|GQ2KorWX^OVtUj-O-nk3(CPMTwH zeE=tLr96r=P3q2*_cX(HC`(ByE~QDT``cXJM&cN%-Yp)R#$%(&h8cAXT(|oJutxaE_A3mRCb*SiH!PT2*ss2$zBD@h>B(;5)weo+y-QHR1+VdbTUzk4n! zQ!7dupxqhAhBC6i?$=3AvHk%Nz`*eL`6ql3W#=;b%fi7hoN1NrEx!iRp+KvFFX}Tk1|OUj1px-Te8>0%}Pnw(d(V^|+&P3R&j1 z@^^T`tQfzUICva3S9R03Cz)v=n_aSGWP5`P=NQPmS?w-D!)^5F5!-Ab2CbObW|nbAP|1oG zf~=0|?X)%hfVIy=5Jd)4&-S3S)&wbk(RawyY|!F0Z&Yx_$P ziUt9uo$+*9{)0XaQG;ITk{sak=E0u39#sNc22e%tSEw_qNOOz1HAw$$pE7*y?Y+OH zltY?1g5jrKoZ!TQzzQum5k^g}V0}=Yka|%d$2RvPq#}J!ZK;96b84wx9Mgijw}*nk z>agRTu=INReJ)-l-oKN#Cw;Ro_90(24AFZ^(0`)(`_%Q zM;u359B!K0UHUL!qgzLpk9ZAz;flHD7c@{ND~bfEd;TmJd{MuT?jF|37n1SYDEGpi zhdk@c!*v!EK~H42=|5Z{QIy50S{@*jZeMZ$G9NuXaFzo>^svR}g|ng7Okw66IBDxB z87figMq`7=ow?>7-eC6T%b;skQ~zts70@~-(pGm0H8sOaFbdDpKuhZk?xv3jyLKb} zv0XHkqtPSYrtdZ-@{6;$saPD7hTbV709Exg%~3Z`;Pv)%fX>qfb8m7EX@*;r&_OM% z?g|`f4UtQivHpxDLxpt>6y~%HTYw*Ae|Z`!N9~J|=R|{NWe$1+B9ceIv+SjX8DoJo zJb~k0F{vlM68v>I``z#FJW{}t$P%xTbZ)?a_%O61r`RrLbdtjU)B3P~345X{Yl{Zr z#uVASR5vC39!YqYk&Uk7IBg8Y7tMC~c~*4-5DRg6U-LgZuDitkNy^IcEB3`{R=hkl zUxR);qNAbsv#2>CL7z$T8<3~XvBA(#f2PK7pZ3Fn>k~}-Nd;@JpI{~qySdZ*v4D(w zt`ZL2SJY|rKMn<{ip72NM)i%ejqdhx*Sd$rVM)C)bK3Pf$>t?cN^$ zIF*x1SoK>Qq&@DU=I;1SN<}Liym|KZ!%3gd=6S2RyMg;+kTGQVCpPz5G<)Dh?!wv6 zZDEf)7N!zrUVu={kGCzj4cZNlq(Jy& zl-Au_)xJt(~Zdbf*7;_A$L)u2^V)1a!eCqj-_= zwXHx&9{bqJY<=SzLX6k$7+JeMjoNVysy2?Cf&#FEsZ&Jn12nSGb>50tEex z#c~t{*OoLynE~V*a^arKSgpMo1e3Am@gLYDpFH|qiN^-i_&`~o(G{@dBXqQR2U>0U;20UWcJpwO@cn-%Qz^{9m1#gE-0&*k z&zG5dDgK!tTE4w*nJRH?(IZPTaB6;(iq$9<|Md5AMtn}#=-wh7|6ZD=#42Sf$OyN= zozc}XzsVaUNtODnrBba}t+)z9+LiHH4%S<)dO3&JX0!g90qMSH9<)ZOvFg}qNPv@x zDCV5i&u1CP2FpwL%xDzFu7ck@H)yv!{F0$Hc(iXgQhp4inT6NjQTIR8boHB=JyLSD zom*C}w$S7ru7;cqW2%iK8}Sv9SgAF)0&5GcL=kwjkeu<0E&QMxdZ)MO5B}q;!hr?& zL^Br%?(QF`q^3`Tek446ej0dTMP&8CdazHkjKY0yCvBsmrn|qV&un{fH%Hn>gS5JR zQ4xbRi`zdFLI+x!#!rGK2BUz;cm`(2`8=rwA7!2ABB8Zdy?<=tq{T^(yLp_;(nkjMvKynw$c6K*V8@@aya3N&S1KDvZ87bm2^7L@|QH1M$GQoXwAZ;sT zwY{9WDga0r;{aHuZ49ea{r?}7RLJv<7S)_^8Wt9rq_t76_=8XBB9YGbDg5OCw{ z6vNaePBdo>e((srBN4}Z)lg-Al5TQqTzn^HJ_0ue6Li0CEPkiN8-sNtNM;W`*{J{7XT`nsl3XiVE|QV4a%-NtN~LlS%v|EP^gT-s@0#tGSj83tUpwB% zQIQO&vfa1bM`XlP5=NX+gmdJqay}(|pif!n9*4PGS?8VrFY88oO4@K%wqLw_x zV-sDI?d6*a)BYo#7Ul*++ZRE5+#8O2;I>yxx?O={_S?I!kJ)`YZrP&2NK^Yg zPVRUJ(S59WR^QBwu<6G=kw!9HWNPD4s?o-%4zzo~e?k^CHnX$6O>}Ph*3$Yz=$u5z z_TbIw*W5c|=L{;7+>Phd4r+*PKuPg*}6Lv2D3=I zXbZR%v~F;2+0AJe$(Ilki&>=&2!-utMIa+<3HXwCU*-grx79X~h{NHavAwXqYFP#+ z8C4KT06O%gOq{1moLWV+A0E2D4aSYCubk{omRo#S#aAGDli=)m(wKwsH* z2HD2%Ti;VwLs9pJlkhdB1_oJa5+f?KSohZvpShP0_FQ#c9(}2N^jv>3O!_@{!}Jg^ z`PZHU;r~)H@cKW3Rqdi26Xm4t!AN$GXK@Y$$#J&%#lEQeY?$ zWW1Wio!?YicaK$z-ToCNxq|ehHY3uEgH31;d;71%qx|NxeauIFcejujSN*?+cm`gq z`EPqUE!EO!Nc}<93_y4UMB>LUj(hOAm!j<$V!wvO+U4|JEnwt)a6+G9Lk;wWar1}) zhL?kG&Jj4RKLNeQN`Q0_NkFD^gP zf~<5P&0AUtEJytszGd%C1oiUSLg8bN8CN$>O`NYTWb6pB3zH29Kv9Sj#f>55d`bTJ zhYJvNpl3eSuYWo+ny^`?=pt_;&-U9xX4AX4_vo!)%^6TF5!l{pHf!M~483PRqW1zj zI?_#U#7l!@pM^ISTWP9(yUY85dU1tL@vDR|>4RYc3Au$(q#1TFcHeI_3#7>ztIchc!c|9K|-0O35ulEhpxlJ4d zasxgRj8U1e?C^h}R&ntW0352nlP?@j`2_TN7T#!WY2YY@<2ni%dfiGUStf-h0H738 zUBjnNj!T8S;}vy+|d_w^XwSp}n zj6OUqKd75#Xl$P==FY~(Q&xqL^zk5C&G5+=_xj`s7OvlE`YO9y1zjv0r?Q6jPrg<3 zZkFMM$7EC+z>Kj?F==XjJnL~6LeFWjgC^H`Ias5~sed_! z)cn3pci}q-z3cu)6$(CM3deI9$ZG=nUk`YG1THatm^tLUEdsxpTd*pHQyf+z(})#;`i3k z8crzvt!^X(SIB5Fizh`9Q=;VP(;iw7WC9 zN|R3Q=e#u%zFPi$Ik|Vk7Rw>^J}(k}yD594{Fn}d9)7$?#74uTb*gr~;cXl^&u67BOWl@5AzIOWNAXjT!E%D1YANPlcMh>G_o_3nEX)Wlp;XpeJwS$Vvyr>DMgz>$qX(;cVNO5-!5rfNu`#!Ae2<%d`@kr%Gr03<&^ zO|JscqwirL|M!`#%b<=YAh>$px7krxm)Dik;9ug2Hk!uSv%ou^pFj5WQL;3zyeG;w z*O!=G(jR?U$|Qk$S!iRr%CXMd)Y4t=FhSrOEwMnxJz@ z05`I=htM4n_xM9h6+R*&*5umDY&2&M`({%DYd*`3cnGWCda4wQujubY=HSQ%y`}|w z&zWzB?|iubZmVUS9%dCdiArkn0dKD6cH$gFB3NnknK}0ZhCOTD6{47QYqgow908dn z)DXXU_27M7Z16q!sB*CP8jzUz70plnHKhB3PV(M5yB~X8B0hG;KrS48V+#rMGsC)F zbg~SUjJHBS5@6|TgN{JSF-b>;0=i({k27x2OaScAEJmGDk}u$Hf~pzENU$@;-c=GM z4M2o?4ev@*EGXia8!3HPx5i6)Uy%^DO!8S2#GSdH5kM8SKk{N`_H>QO!rUs!nooxU zAeK?g!IEiMm_ZK1@$urISP>UADdI%s8Q`|z87*IMh6ds%UUFo=E*3BVwMNfx(Iq9) z<)JqN4-GxPF!Ee|+^>Bqyc4;+YYqhdHvSsNUq&(_vhUyt1~TikSInHOal5#=`aMsiOR=Z`2%7 z1RT%Wrr-(SZ<_BitjJ-wb!0POjtpoOEXs~DJv7NOh=IYgua z7cW<+MHZu4;QLaWWB?;$T-c3b8Q61wkrxtr_5#y3?^baGbr)Xt%-KP9N0Ohd|7iYYc|E%s9K?mk*- zNZ6|223!|jP+ejS|D^zcI6k;zKAc^~z*@K=f96eNyYF|@h)ES*NpWNB*buOA;_?!( zT;>+Ema`JX!Pf+YAgY1@z|3g0U!UHTerUt=SnMAbg2CigsGf`CTUkddw)Yg;6D-A? zgp=9jKz zq2kSJ)XZMpe8=_OFOAeJ3aBY|v*wlI8mh?k4Y*pMXdWArHn z2&W;La%w``HfO$#fi-1vF3aWSN98Xn9Lg!$kfnN_P_I(Omm8IUQJ$sqs_)-(uU-0o z?5RN@asR28iT}~dD@(?oIv${Mc=GmIWGc}zP)7`^lt~tDTv@F^vm%B^tVP4pJf7|} zIc45ayQ}*laQ&coU(5?Z$MTY;rPmZ8%@N99Akzg97qF0PF)w{8?2(E z29Jc`s0`zazMl=o$Y^1r{)?#k-*Ut+<>~1GFYfDQCxYGx*EjTiNwdA!9el+pfx%k- znT18^&D*y)TSPancoPejXjxgM$tI-GNOs7FMxzEk9kMKp@Tsj6eF0vO{2a(UlcNi| z?8IHtuQz8`7qTKZhk&E|uey}p;_wWH z4~ty4y2C~HfUzi=#_6FmMNf*-?|Bnf6JMM}oHsrpe%W8p$S`aywsig)2jxaSuk6*D zT*3M0fZ?%Nek9AY%?)xPeOh3VYTz94kAG9Lhfgmc@`6`OV&^8M31_Ai8|wJd!SVOz zx3R>!hN|-!bC4-n60DR{kgCNtI~x8NQ> zFaK=39T_~@qd#X6&mVvI9e85E+Te`QLJe4oHU-B{&jU|IhK=ymQf=uPD9Km-O$sLt zN!U}nviN0Au#VgpM1YQS(u^H4r5C`a3L>`p87V#ttOi(B2q50MF4>ze;`<~)^DCIB zGyjsdzqKmwzqP7bf$zi*OKaN;$frulp>a$(i$I>=9yfGS&D?0#mwYvWzj9YsoerW_ zp04ubJ+iWJUC5@;Kc7v8%;uMQDdtrRJ`>RGDJ#cIECVc4>=$U-lwusQ6Sfs};J?q@ zo?Ig2zBK|{@=o}Bi}{+?UT1|`$bz)Ya-X(K;4v~Fok;_2PY0nz)C20Yhy!-*a7W#@gGg#!sJzub<)6Kj0#J0z(MIwTb#;+W^x~Zjk zUEZJ0bGN<38j9EL%ab=d8Kz)u;CU%%g(>H(9%?PnEC3J_#tku&h&JjRjGqn|ox4nubOH87wdl_nn`X7Rn- zmlHknjH5|7>2+E0I6ulWy;O6uPp)h+>SJ+Dg;{fu{z{`JN4B1Rv1 zJtkCJA?qS9`S<(7e)|CZ1sR4+5uwrwU)snL$OjxJ<E6| z`9JU(Y-?@D%3FMvQi)cljDxbp%U6k!&&C^oT?6LWSSm5}Hbx*>!EjvG-bGtIy~fRMcLtPp3^5dfUb9d zphS4b2T&XiNG+J$Co7|y7;wzckbR`k=c3AUFPM8Y3!qF1Y%%E#`buX~;dQrxh=$6n>`LB;3evZPEWl28S{^0j>Zr z)sIGInId%0fX9V4?hq`a=PD}Cw6rNARs2FZT=aE_P}=FKY;2yE)N~dTtLVszqcxXU zOuQMh32seQyz;1%OzklD=3Up}Nh3u~AJ0L3a^Of9e7Gdh@<#uHnLd!S#t*tLI1>H9l|8|gwca+}z%bQ2ao>^fD8&RFs;A_?g2BhrC z0zYct{F^_7Y*RA3(tNsNDR5uhO5gzo@v#kM_HoPCE@me&A3*0ir$CvU^239v6+u1D(W zd{J)lc(`9QO}PoP6{yo%Y1#Ce64=~Oap$UHh3pvks1t%I0kA7V^?v1S3#?lY)~5uP zbnp1gTE7^}n($FjYV+1IjzV#7`w{6?RQAC@NDFpvrbp2$Z`aID~;kJsU`QJ3FIv zMunZ(`smH|Y)Jl(x9Yo8iQpx#lTh2We#rRLug2tGb9YEl4_D zQNw%hsPr}LN#=phgh6iO?O+&krw~`PN#_tBqNTnXIZ7UhD?z4mSdp{;#+frWml%g8 zIjU*S(^twiw4rT4%-vVRQ5q_^LbJJa=mNk zIA+8o{(ui)9UeHG#uyVT+*}R@I@Hd`hJ4}pqdoS8STIMf_wV^8%#W?CtaA2@L`t6&PwI;}rs;fQs@ITLtm5Dwg_1j9&5|*+}~i=smtJ_Uc|J5x?XyYGzi6 zzRfGEC5?{xnBNOesOOXVxszkv`P1Xs#pnetIhf6BdnAh zsrJ<}1-ygarQYkiAunTc2cVS)c*{4TC>#bBqWDRN#C))K8Vodr|Vq=E5OGLZ--FC!n4KYjA!J z?ukPSwt}PwtAL!OO*n>P1jP(=k@J1;YkWXd_9mbd^1PB~Uvdy}&b% z!yt{0k$geV^ewl2MF;=$ChZaXk>8raY_i2OawG$ldtzpWrRn57l_F4WLg+cQAOods zEEnO^QQy-UK47Z*3%1c`nq!1y@SN1N3xR+m$0a-kRlosVsY;M>r7wqJ|72N>hMb8> zfwh3X{$SpSaR|0dDk-Q-c4LV%LlxVv&*G*ZXA?LbkqTkp7y7VIJ3reWm1VoN^>_d6)DPUk1XJf*_F9|!TKUe z#E?ZRe1)GM>^CdOLsW`4-WRz^Jer|h+6qargnY4w=;e{a{vhj9}Ot zdRUkV?4qm8<6J(#nQRFSVtJbPoL@+bA6?~LW&lp~uW-?ki8sWgISd{dxfK;GStz_t zZggmZVFl$}Pi*5=AilP?7Uz-AV`*|CwfYD*Di2?4nM|Sxqy(1L|9Rs(In2T>KG1Oz z_HugewCTf)h^*#Gd~#b4(&;9fN)3nH2+y<^K>bp!B4yQe$-pp2M-U+uiSZ|{Cn-Bm zS#0j@+ieru!TUT5D)(AiFC#_u`N8`{69%LSCE?G9W1U@;&XwK)=RKrFjd(_4Zo!~u z=Iq#Sq+RfT;{M_y?2kIU|g+z|froq-4Kram1>df)Ma zP~zd_RZjStyA+^W!bUY<+%EsQ4oMId*XVw$J2D%V#q#%=DG z_Oz*#Myg;#b_S~Yp7A&s3MaQdf-m8`dshVB6+@>Ka)<-|y@4jvqmm#c(331%kv|Bf zofB93!Q=(;1nO()?l*D^b&(hnXp@P9NC7lpV9tqz#vW_awnE55ZrVP~%t7+1xzM=u zg`JwZ%Bt~Xbah`*P|!`<70glZCysju@e9eTlH;ntr;?7#Do*!!~7; z7{VLfd0Sa##BK|k+JwL>PwlF46Un9sLteusYtuaCzp}shuG=~BYoER5BI&Ev!LN{f zc=~jB@#e}jh}Xj|+@2ms-K)-yC!ne(P5~Fyw^eFo%&p~{tOa{1Ros`bLzWNhFk6Cd zuSq+XSZP%&sw~~tES$eXs`|}FJo#I!w6ykJoBr?)qN`lE6vs^oY8LUxH%!w2FHrM4 zifXJcQZqrUxvW0U#hvn>gl6ibpg)hXjSO8u)Tu0c&Pzw1S00$b^Eqzso~u&o0o|O{ zV>5DzV=3lnWzk9s7SC<_Cpd6i)OiuHzrzr5u;>Q_~lw$;6j3<-S$bSiKXkS(A7jYkr~30bN{T!wU<9 zvMwa;DsiP%Wf{<#`}4Y($h>Bs+Igb(nJl7zhiBbX znU_o;*|1c)tZE-q--erCIE1%_mxdk0J@FWJSC zr9Sl$80CMS*ZLZ*iW9TUD=dHNJS6~FQa4S&X4$HuqARBiskH6^kL!|f*h=S9u#&@! z&Ep3(yR_eea{H*Z8zWScUtT005l<)AK|QJ)wT95s9X->Q=9;EVS)d14Mk0>J5i!;! z9L2)Uo`Z!|tf;tfQ5Qvq6^8Iaa2`IKSM8EBaaiDerqIK zAH~o($CzIyx&*aN&e(qQF72iejCf~x)6d5+5Li}@5xPthy{L^;^?Ib zY(FweNmX%e$NY$P*Pv$XsP*R+C#Qkc|>GkSm|M*f6}yJzpVJde4?_ zS=^R!pHTYOq9}7blJ8c3q->qP$T)YwfCZnlE@QF>4$05tY&LO(5EHT;H zIGsyR4kSyh%6R^{Vubel*c*beS&~kz_ilu2s%?c;uL)5c=_erak1M=o6*#gX5`=NO zT5jM20TEf=Mn*#V_w+nvycoD|rG%X&HaJe=mZHsDMtU_(USbJ363=^Vwz_GQN9~_V zNN*nU-88Rnb92&sP@Pa}ufgRd(4bNpw)GIMm0!yC5*=@LN z8ki5#yrM%G{L*%{6aalq9l}pq@7wZukhUz=ZC=65VFgYS$9~)U)PU18HMeHK^PS>A zc-!YBo6!RSw@nEnvO}BKWnB33@`?8CEKlM+H1^QvoV?quidW>UtO&z#jv_4jZd9oq z5@FEO;$(rN8BsMUb~~Gnl?);Lwk;JPHhH$8-CTGnf5{`g5Y%xEUr%2>jTqUF#$?v&SA1CNYd+!BxN<< z6@yHyH2NO@xBMXDsHJ_mhU~;p3O|k!EZ98@7GFT@%cl|_%$dS*_iar9JRS?5R%1{; zS~tviz-59fu~(sd?8&nPbQhVhJ6uo3yePi*sYaU^z8ZLIwKCthCDW1`e~UqCU*pbO zX`O&@4hH4L4*Q1GG0lIa`H1C3v^A0LhDyX_h8)P(9_!hSx3t}mPk{(iJ^NNcJDp@x zW>hsCai$b2MQ(4unh5^Lm-ID3@goFtX#6C~1BkU8vf&xtvx$-FHXGV^6#XW^gcwX= zrL`OXMkj#y8`aa=l=?#%Ap%%coE9MpC7sp@jtt0ISOM>C?*v>Jhc-P_+RMGH3PZM& z5j=XQWxM32cm8R44L7cOjSjr(O_cHgZ{cYfrQcw|lvWKOTzb!PyEVD;CTb`NGdb>c z(k8T?SqphJymHo6)%pZO(;j;8S9z}a_AP5(kr8DyKsdWf zdNCEpU@A$S6B_BTm4vm*0!A|N`M@WrjwVkYGL?Q`^jz^f{c|o`7K(j3>rJ_?x%NJb z8lGKUG-Q;j)7&(nU=diYrAre%vh_q5`rY9$r)|(!SOe);CCva_9Ns1l{03*Xrkgbm z*_DdKlTXaIgN9u1I_RFGdcF3tv!*KvYJlir#$&hy2e4X7lb%;GZmNjp;PV1|mk;dig(A%Lf6vymC zP%sDB!2XyTu;BY>oK*-G#;I0Szy=hK_G+0CIMbo2&tlpM%-cH;y_i({{C0B1u4#6T zA^&@^lwbui9ry1YSH$wa9#kNRX^=1{K}O*C}_#f>j?HX6NTvpc!p8z%)X+0HFc-PFTlnVeJt9^|i#8%;i-lglvAROTMow`+m&+?^nWVl;Yjt-;-2>d0My# zoWHxyF?C4v)6Pu}zCk{nHh$vc5xW^ZEl-`ph|Dg9z8!1Umw;}R|5E}P zn$AmJli-KBJ=h&AMCRHNjp-Vjm@d58o-XdoQKIGx>o-6I;;i|DVnr^!pcw!H1YEth z($Lj>sj)6yGJpy==5JyNiJj;91Jmbq-Y0|d<=T$ZBxC@W&-bi&20P3e-LO(Qa>C_Z z)q8?{iFkvKpL6^?$ig(>@u7o*1#Q**Z~>)peH_!rvE^3ku{|@a+*UU;X}q>`2GDGY zvHpW}pVb~=~9O+Wz4YqM3=`oSkJ}COg~tVKc7Otf&9LF=yY_9qctEffjO=Q}dv`3y3GS10 zu2^kz!#f`9ycxWAwO^6lW#;&|FG0K$6#8!~j^V#6X^@Ifl;~gKmH$YlA?-e@?0@Cc z{?dp4L}H>nJPZCS;wSsx#v$^{k;$n4Iy25+&dPuPT9Ntx=My6XLTj}aD0(%fRr&w( zaGx1WGYgCw4`gWW{p$%vp6;m?T!Rp_au4Wwvj6kgw!?3G0sB`;h8pte{@x3;h>sJ#mcDQZIAQ|Cd`s!4Ie+wP0=v|1{WP$u&bcEiA3!jmMde6jsj%OzR^AO(Osz9=j z!C{$46+Hm&)B=mZM5}2mWEdH*r#atqlw!X zE7%wRKYx0+%a$qV{Co93)}10J8~D)&IabJJcXy~c>2Jqj+F`Jy{g0?7kM-gcz;XlY zNphWUpNon5y5R|vb|FBu$h$|1aG-aYog_aCILOX~;U9eQ-fGW+UgXMv+PoouxKE1f&Xrhl}F_NHE(}keb33C zcCzE>Yn4HZ6E4^#D>^VuU2cuUmF{GuJX`R_M3SJ|vD6$QR#AJzW8Lt7JPB2`9dh`w z1znb60Vf$@4?!8Rza4G8ZfZ@0utkD0gY%m;2El(at>gW7t2+Plx&CuuWXJW9z@Q5z zwNALvHR+*Kj=*Dj6EVl+jMkys135JlWF2nW#Q*ir$z{{)reP0h8Via|-WNkRi084H z-7+uH&$F8^kojDsqPt%FOB()?i@&v*}vdTM}3VB|0%|J@B^bglTck z^y=94WZ|NTI%?Oi`%(hpvpne;=;wTNLbg7ESe6P3qOx1>`D;r5(zkz4jwmG?IH&{p zDYBc8|27&~SGw+xfJrXnF$a$AB3Ak&)^9&ZJns5-d5v9)tYe*=Z2n(PF*#tAPHyyVFRlb!2e@lTH5(ak2W$IF1ua& z0;W@<*3)^p787)TV1_iW+D?v9ej^wD*2S{xAQ;JFuG^*C!**|q zRia%dUAH{8V^r%V|A>VYwx|lf5#9A?A$^PKi<%4LAcU>iu;8*+13p`3OifNy)QJn^ zs#&P;%5TK{ByS_o`o^K7+XdahH6!L5rR{djICo#!?aTaga6p2T0aCHuEvWL}V%5oF zKImAjJ5x|(vv!^M{J6=v4!raaYjAZcwR<>61av{AY|8A=Qn^f`#ZEeikSexUkv4!~ zad5}FZ$7?z$%)0K$6NYdV6F{Iew3q+CwT?*^B?JIecmIEW8VGH#)^ z)0Ttr)Khr3y^bJ`Lwp;~+wogrpJQWk+M4gy!%3aToKg+yG^3yoID-~2qFr00VecV% z+q2t8dXcyYM{Ztpf5DYins$Dyxt|k|1dKB;n;UII;=bP6-dB9am%Mrd8H|P7=J+bq zj|d?_di2Hq8F58XYDO@tmn-FCqA_S{inHZcl#lzcjwyCsVP~1(=jRVxIel=sj^4cwtFc?V8Q!I4%M@$3 zgLmlsl>9TJ;~|wke0SQ1A4B|It{63panw>}4(3(caiUk}P5sozY^d#y#a>jB!k{KOj!{6YxzgOg_tupI z!Km0p<46|bNDL5`!H7rK?pAoHW!Aj)qX^zbSZ~_knMBwzk%su|`|TE&_9H>^X4Ku3 z+v|~oh9jgMV-`%9J^%l;_1$4j7eu^ zN|7oEgc7igcu0laqjcJ^WEp({C)mNerKQTwbou| zZTRl^SNo9ZSoxwz5l19vsgMcouj*jheQkt4%wSVcD3V$}cbCWR3=|}JIELP2F#sf! z<*LvFLj1e~WxtA(PVcxI_+O*icK$boVKX(ol^*jS11a`@wLDbew}YMzgG-t^1HQP8 z9Ffp(`T_ye=p^S!gft(cpDSj3Td;GJp+(sBl;o$hUpCzA-9=ap`o-&M;e;SGh-?d^ zk+ZdvVJi2XsCnUCUMv$7A&Cm7>OU;bn)TJ_@8lUW(=aNIy(h zT~cYXsFA**VX=Bw$#1gJhor^ID0$3UB|BVSKZ?yFYxP#JTL@e29BLl>OCcOab{w>& zi=5rQno&1?LA8tEnet*}!q(rsP|s$9nKdFMPkTPr*k|^Pz@n0hI%#(Br$N5t^EkII z4*OjY5%62LW&NNvaz)wiX(1iYP3_ri_+$zTp0X^P;N0%gE<$IzlA2s@z zV9x+IqBy|gSi2%gqKth-ygwnX$4cp62{6@~%OHg~bNWcND_$fXA9edM-8+lqi0T=6 zU1$I}nTzt8q*q`&BH0G*Um_kXM#7<>TZfMV=mBKkCP(galh}3fZC8Iy2=DCF`GnMW zB*8xMUN1$`f6Vl;XG$=Wk-gYz&qg-r7G@g zRZTM&HggcKK9sK38$$3`FP<2;Wr@tV4w}QwKsPiXisU9U{z*6t+{Hc!L4bo1-2N5y z*0G6OcLLZBFpNAvOC`jU}T zA24}~;J^c~7dDGr$VNsLAMEk-~Qm3AM$8u2q<|mhAq?4L~7OtboH!hw^qg7|;4o9y$ zJO$p(lnzF2R=H^ice1Mxef|JK20&K%HHcjF=8SH|i1z0Z+vl7_HHy#m5ngofa3GKE zbDgxyAkiY|YimXkGA->vZK>o&gkM)>Necq0G)w^TDcq-b?jnz8R8HfI7u~d!u`Zme zdtVj5IkcIH`}jcclL(Pyz^z1kc>3jg?*h4KLGN^KnRsQz6`L(-hVppDr#wKyTG|J{ z7ir#V?1Gtr37ojM+M>kOq>yXsuRgG{T9uT$JDB9gVoz!$Hzm3RnDb-_(!vO3WjSo2 ztp4IfX&ACi3g_joH#2s|uY*(OR{XE1x#aokwIReyhQ>o&%JnTu&2zAQCIRy9&8;fz zv_*6`QxHih0g{|aUfmn!9bb@2+gVtsJ7DkOEYnQOqZ|wS>DwZ4D2nK%k z!Y^6F_016|MFZ>3tKTnLX!y_nMsMEnVNW-8F0h)WLnyoyW+bX|tbL)CULddKGoi3MMX|aOAwXjp zVdZ-`<%vT|xnNC9iy4{l$45ekdrhBeESSwO$iP565+i--?+KS#Vnp<-8F$Gx=inb* zhLBy{v-HeT1%(e#2LlTw?SSHBxXN>!wH((oo|U^As?6iW%9F%OU?GYMe}51H7w}IV56kK6AC!=2xnb_Hcq7yxVg{rzGQye; z)_Fyvx32k@V2e=oMh_9q6=Ku}-aP18!eM%l7#A zft(!`FfD%-u=Q#kMe7fW5c9=ZuiN?dgRQGBeUUmb`KVgBveoSPkhdSt`B$e1aaA@J z6;b0@iMjbNi5s!`c4*jcgM>L1O4~PJhh(-Z-#s5ac?yFnNQ^75%Ic`8=QmW(>^h^373B$FqFKpP9ob za&_!s39k>GiS`U&VZ)Th!KRLNj(As47V5o++GXC&we`b_rBl5Crbq~#*(O{0Q`E() zRmnf24Vg6E&Mrj(?c(K>u$l%)3c&WMJ*g_?`SZd@kG_zVzh%&(ngZ((f7iAFl9`J1 zy26D65YL06%1J$5a9;yk$l$4?m7AZ=+|-_~mp_q%Dz>PptK4sNQC!50d=$PXi2HrC z&@mWnX>&DaGDoxBscTcl@|E|mIp^1B=-{`Bd=CbVuu^H^Fuv~uYQAw@UhJum8 zvARVSIjmoTP)h0;gGeY_grh2Z_k;<>LaRxu@F9I8sJoi@o_aS}b=M!njuOmB-k+1- z1t8tvE;4LJlF}>oUuh@hF+f(tcZ1G9=dCC!*{pFQ*k4aO4wOWSk|sOHV-+3jp0{;I zeEh*u)kAgCWDu22Z991T(3OR?R#Oje+`H<|%voMRYsYcuc1RF%SFtRC;J{uU(l6m8 zAJi|w%uk3AA+YTOAfI1$hD^MGcK_xdetGgXU;OCnl#zy_ghL+Gdb&019};~}!^>0Z zS-@04`)sf_9`zS%=+O+{SJy^hi7STK;kW^ky;R{@wpIF}UX1G{><}$MAfz<7fgwIE zFVu51(cnPf{WzNjQLLDhtm&iMQ5)7B>WUcD#XAPJ`|9;`o^1~*R>#{>qheO&C*cv^ zNedK^=sSWAaC8%&ML%>ToeI?R#$8Hzk_Ww2agJ|pXWm+w<8=jW*0sVtSSiA%;A=0>5sNQf-5UleC7;y0>V?p=Z%t^r%=HKX*+rmjiQD+CPs#VIB^R z$ZfxRN?!UeTY<>~M-EZQ1N*6lJpxPTzW;+XWFP0{xO40(-A17yD@+hlWZuqJo>KSa zdk4Zw$r8Aq*BxUZpa)Ta+%vdhbAAXJdiXb%*o|De3N55r90_8;RE~JI`esQv27R`( z+xpm=euYR%9u`*WgYMeMjJa?tPv0CMkfP}hPm_3Dl_7|af7ca?nv}vl4G?lwE|)`M znd{ZE4MtXQhZ^xL1SRBgzW<-}ofO8$6WsXOg$lX>i8n)y>_jv|DwXavQf4|Fcm9q; z8#-;R>n^|Pi11lZn_8btVIvzuid`!g^bi}m-)dp!gc@LRBCsXMR?rcHxUjI&@fzp$ zc#Vql7o#PJwvEX1QJCtEQV8-0dCJC!DICitTKTT0?jKm)e+9hK>Qod$PKkCN-3L2G zX+oVgjtF}7A4O^e^Xha_SLo--M4gil59(o_C3}lUE;M zcGxz3K&0$+Tv5CJ_IvB}kwHu89)^+Oxr9@jV!7K=Qc_7%Q|e($9$p~sF5WTt`i&b> zA|iEfl<8izjlQYUU8gPQercM<`yis(FU<<_&Eu(8{4uts?;1KQMvE3xq$JGnm zp9z;qg=e|RU7xc%4hbe-F^ZMs@zS`Nt*fxfbxP-{fdMiscgjO@#+5cz1Ix_x{khh< z%abzs5vBlsn@(H6A^B0hT^^dSPBTMn!=3`UT+Fapm-hfQrT@|Y@-devTsJtaoSRDh zjRnt^U^de429oQ98C=S)MCbNWNc!gGfHQBuwWqG_{2CJnc5>gBxeWli$S@$`0`XyW zbbkZ=wD;@A;4_aN*bsR=!r@O0#*r`C}30 z=Yzt+UVM45G&Pjhv#`1Sk%iR2cW`*OiEmHEg{a{kYc- z(lOoLgY9n`PomM${4X?w*&-68vYUBo0rR4tg=u@;+x)f~@hzU7r(*v8`-qyA{!RGoT}U^N%otwxTN(pZ?jKH`UD3AtQJ*Hg#V&72?Uy1`&so6fFuPi%5fn63S7&8(0+g4+VUD4NF0LB`+HHTv) zIp#-gZ3({h`8A(ZT~hPuxwu1U?cp z>^C@`(-66oIo1!GF59B&=&%G-+YXp!waMB{i9Yx7DK}|F4~o`{#-m4pFO&T1msZO_ zSeTYngm;s^tCOx21U9+$L`DjWO$s2W*|YltP0WTm@uM>7>zPN9DwG)#+ZtTbh1 z9VZwaunzX8+z{y=HS6v?MA-q__T%Nisdet~31Ph-HH5uvTT((fY1-(oqCk_U^$+cP zxSZMv8u#f_g)~m2fsR83NDs%BaBH0*y`^3HiY51Wco%Y=h#73An-5#BY0V=Ni#{^R z^zHec`UF7_uwi;O_}P=*YZcw}+U^*u>lPu}B`1^OlMiFr-lR>P6U{>5h{3Iza5 zg*p6I-aM@|KtTlX-Sbg4Z9%jl@dVxQpVdYacT(;gqlB;6rx)l(uDcS{GtHl+Y zeRhx+{q5U5@>qMsF_o6Zz_fxODgifY<)}KgLxcID2PWTdo@m<`w1yS}9q;?n<}cQZ zmuBROckhMl-#=Ch5G)M;4qbN5R*^5M@;P9zyDjjhoR)a`rtz}h#N>15o@;oXHYGpB zI0GP4xIvD0wmK9HmfZPuGV$l8R7BK`KTX`>V8~>4AfsWdFRvwZfY38wT{Ek!w>2vp zSnzCr+#Hr67&9neU|+dyRFJ?I1&Wob1JXpt@`Z!*Bm>pH(Q38d>Vmpax z87c5_WmEN>*Xe(n6Z^HlRB1sPCSTzoo^zX!o%KXxT-wvKc?Vz241aSfGVZ zUwZIHl~2{wyGD3bmR43={O6kpIJ{%q$$w|FX|OFB1iSKP7s1w78jGM}XiFUh{!z5B z_tf0HMJQD#%$}0$tr)$%TwbK@qjwHx@blB_zy5yoVZ62)?|~G*1ud<j68_BvVke?64vmj zC`KDoMSTsS4rRB30q}BEg zeJt_gcIPfP$?Y_DnzBuG*`61dpL@UJ+r7>48vy8{i4DF{;aE#&!;_ewJI^J3d+ErK z!!y9?roF&{DEn2cu!m}qJBfxucXNQbsfElFiXm7XVL&NF~XNVEPI0*bGHEVMF~;sO+I+&NMhDqDbZ<3ZC`Ytlqd`R^6K;a^Gi3r zR#Tk%9%Gjy#-?p>`it$EnztSjylTGSK)azWhgJZ|S%g-|)otl)Lq&>(7U_r$LFM$^ z8C<$*hQa9QQS|#S4<|^e8-p?gP@7O zEWNQMxFOAmi$7uhv!^-0&xx4Zu(js5`&7O#)TED4i|Z@H3m*igCz6!mC?FZ)4N+l)!0Bt9XMHVlO6zDZTwf*?H9NxmBqju%(P&>-wjXrtX-)8Ocx$Kvu zQ@<}Gu;+v~Vkvd*itpN4=j{S|VM`oZNc6tEAWiR6C=H#xqoak;;sZ1$2_(9871*-0 zYyZoTJVg6orEXgSRVB&hlF!ps9VKj=?rPA7JPj^eV(HmCasT)y{xhS4ZrmB(UigP4 zAq<%u+OG85w@|=yVc*)jad?BY3%=!u*wlP$1ypjl-Q(fiwHeymVbjBAjDA7NmoIz! z2QP?*HHB|JNGn3eWqAT2`$0zEYabuL#^mM2CGK%Muk0lcVWLMcYpv7!-9PQ;N5^ly z`Le(n9tH;*V6TI(gI_x$!z%M#-Ipf3Rf5``j#sV9LQ=sxHt(Xk_%=gT(Bt{G}N?w0Q63(Qm~R&ST1Wy9FhrGfg52;6CQfA_Pg zTgQhyN-HZTlEY-zJGc&!$Y@z=#uyVh)BwT*8vA)&AwohoxXsO)xl<4_BTL3dN^^Sn_zdu`{X1;T^V!oE zuyc~q!*=geJ$Wjx$$+S1L!`I^9z&kczY3^=i3c!5|39Nq)^Q?1Mny4bFjiqrK@IGL z88sWY0lq%>F?LB|CDdR^qK5)AsWY;wdZ66>a!IZE8wl?ongq{Mn2k8%Ie5uL8~!CQ zt!*Hqd=BH`@N%uP6>s(cu45q;ydtJxQ;e<^9Yx1fRufz?4DR=e1(sWU`1e`XF|w)i zVBTXUu?fHLspxaUOZVM<`(%s~RqUE?4Eoog_r|KhAY3$gnvheV@$hf-mqSNi;M}wA z!8@f2?h*n=)P|BWhY849xSIPy7|lLj7-r}D?u0Ve=2Fy{`*Y1wZIbJ*miPFXoms6! zi?0Fa@!6;tHf8s6)RU?>Vc6A0%~A-YMgK{0E55Df0Kfb0#*(5LcdTE3ZfbW<7}ZzA z`>CB)tF*U^8QSp#6=q<+Rx*u}#FBL25t*3{=`94-7WohHB0hLHBq@({6xSapE=p8-=920C>MxHe zBzHOo8kFLV{cfo0&}O)9b?esKLpCWdN^e*{@6EMXfl`CIK?*swoxyAGr-Qi9ekPDq zt8u{qE>b$(&37)04z)7Z->m%7NsYWM1Bu8*l=M?;+oVwk=28!$wtC!0q-klp`#z+P zqltPHtBZ7HfPu7K>$_^>J3vJkVy%7){*?d6OrggtxZi(gxXs+nQETslIDoYBfd*$G zba#DyVza^R4~s16AhVFS7(F;)MJ8jcYVwTSfy!ifYrCa-aFxh@rMVC3$I*FuETKvN~E&_!sc z{Gu;kq*kXmnQk@~8h3x*9k1(C<8Ex=UC(`<(6!Bd5{XH}dt1TnEW9n?7OS^%E)GaV z)WutNcVM%CD3>so;ERxk!|wG@gmbD0>QaSXDVcX=+9Tpjt6WnO($VB`V5AOP)bHFw z5Tr6h3(XBU$^8L|{%}KxqOP9a-jgR)((<>}p5pHOxT_H)4fu_*B)`rb$B2&*l5MSv zT$T#LyzZ?{Ni>rI_=j@U&Y4tsejT9-|FH_9%T;$lX*yNO@In7XIO&N#8_mw&$4-VI zW!1Au`|0|Qz&thPxa^Q0qgwp2eVY~UJsO(7Lr zD-me`PUS2ZTU$pFG$FI#3UHG0u-ToV79pXO43E6-T+FUgc~{u0ryg3GMo?}ST>uw6 zn@m+_nyor&L}ZhS53w=XCtz9ipE~db;ppx+zbl?xp(6Cowj<**10PC%>PM59};YmRmJaabI@n*-+)L)@98O z?|&i&-mK5)->&loAGHa5nw~B2Sq+l&XPV8Y$~@iB9|{AJaN{HJQS}T>9UaR$Q%osg z2i+|ti7kdA;Z5NN=3)*kC0SFyzjA;w2`!pbOFh90NGo{$Sr}zPrC63Aa>5Q9Bkt^ z^{u(3_9_QxqmFokBmJMvVdm#FW5Fw`=6PBcCbs#(6V*%C4`itsn-S8z?uKu4d2j^3 zO7J%D1?nBe#is^-E=At;#u5%N4pB1PufWzl-ioqf*{>TLjp`DiLZ&Y*9$>Y!jZ|Ox z)9g0LF4r#Ny1D(Ah>^FfWn$6`OJ&C8Rm}Q9+LeMHa6u;2l^=@x5M(V9vmgmWmi$bR znY^Gl&$%;a2rhTdY@clgg?~$%7SNTJkIKg|2$wt@b&3i(Fq`$GUh2%&t`#Pfsy}zi z=8uKVF?m3arnfhlskcH6);pO)C{s}LDH%Bk?Rk>-22L~)2{bVsxwg&feUS^Ty?fZI zxj+?r-MM^{81ef5a%chOD!4cIvEsTEvk{%B@GEYRI$z;&22beKB&muVr|UX$1x^xM zfh^+-V2(8?>{D@?3_-yWgxpda_`0|c*xg156eVmdy7&Q;?&vxN=_O@%V5%#(0bqyo zQD|C?5Z%IDKkhdJtpVor^?Xg7{#d_~4v}cqPlMn&JaX z^8u@$ON>nYCw)SU9Tg(Lr@-fdcD_Byi5tl z9P4ywC`TVieRUB8L#7`>WT`suz$@nPD?Lv zhiq?mFCxcA8wMH_;%sztWwLkXx>1&(7SD62W3%IA18(n%8YFifs;_Tus7+Vo(g&`s zKZEfY>H{utZkx0?c2P>jVLL@BaKIxo=;NBX9;1E=;*bQ>@*iqWmAeJ#OJZT~bZB;` zHbOeNuYcm1N)x5$HCIfT*!#~H{7|hA3Lo;D7W@dKayrtUlnQy%cDM-*nf;{Wn_tQN-^R`B9Kdn;5KDML zk6cr|r$HAZQU;Vw`e!x@)zf8OxQxrXcH-OKNG*84I&X6b3rd(iw$j0_x|5OS2`dk) z+0x|iJ2S2N70G+pcV;&x(9xDfq4}foi9V)313;-wA1_YMc~tn;G!)_3>pQz^1te3+ zE221o!r1vM;R>-cZ7e$DTnyJQwijcmKl+qKjg^T%-~=TZ2``Y90c{5P6r<8ZuWuo_N_S20#roc>-(5~ zM#pkh*GqVh^aqJzk+pBa$C56%D4j0Br89|14}Y9W;ne2kgodyEaf9M-X1>k&#u1>M zaMJwSzxLj9eK^Ln%}v#hcU*uOlQ^~7>{ht<(cWL$S{=V!dp3o*N6%RIr6XiXDKA#y z8B4q?lIz*4(t_;6S&c85*~oF&ulWaapHt*_RvV|pgN3@=v!>1W(44E_j5GnXqgu7^*A==9|E@*Ui~ z%ZGMr(8s+8bsKQA#f$Lbz;3G{m!l6a8%=h``{`1wB?Z5|eb)Fy2_`1Ku>E7?>OI-i z7n{MzrWa>Zh%KuGDVCzSXFcwlTzPRN<)%${i z(~@QXt-HTo-LEdT5&STrqr0j4LDINr^oVlytDB#u_&m!Z*o%unjxQ#UT1y$e4wt-7 z>Ip}(m&fsYR8@l<_;aw{{8z9+R#sL>j$kGC=70LB(Vf@D$SaWP*2B4nb9dj~Nl-Km z5qzzCKkoAmw8GQik(I{5PmvOQ#Ia{VfeW~qRNW|QEFb$3oZ9sa{@1XcwBDj(W@6lC z=&S!_s6AToqbRqSgWPty!rZ8ffa>4;WcK;>qocWdv)z{kBdbTdP51a{LXe+qG=si> zr-{FtN#>BshLNtVoA$QSLYF|3YM-`|4+q*Yc|q5?+;?f`*5V`_K9>8q2HWE1TK5o- zQQzIW(6YAX)708(`dQ=V=bw8{(I3>*%D@enK9%vn*jQ50g$kkcC2px~Feo8jUih9{ zPf54LbS5e=MN6(7j6wwhOvg|=@np|o&#%cJ>oQJR)v7(EZf2fjhX;Q>owsDrU8@)Kc2@Q;w=w_emc{5qTdx@tk z`iPQ_6HoG{pYK_A*Ynw(^+99@pZ?6ygA{QZDF~V^Zl2&_z~qv$S=OD_`5eM7h*Lm_ zqzd2dSnJtV>Xe-9Ab@z0^*v%b`(hr2f*E?nU{|$HYlFloAz|T3yFV3EeE?tX|E06KMmWNcDbr-fX1b2cx@zuyV1OTuf85# U;9-9h?g2i=Mz;*#{pl3-Kk2Kox&QzG literal 0 HcmV?d00001 From 4c95c9ec6ac4fa2f18c51d6a3e2f3767553bc2ee Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Thu, 16 Feb 2023 21:40:34 +0100 Subject: [PATCH 019/155] ci: add step to build and verify Docker image --- .github/workflows/build.yml | 120 +++++++++++++++++------------------- 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e652ee69..8fdb8cfbe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,66 +1,60 @@ -name: "Build" +name: "Pull requests build" on: - pull_request: - paths-ignore: - - '.txt' - - 'LICENSE' - - 'docs/**' - push: - branches: - - main - tags-ignore: - - '*' - paths-ignore: - - '.txt' - - 'LICENSE' - - 'docs/**' + pull_request: + paths-ignore: + - '.txt' + - 'LICENSE' + - 'docs/**' 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] - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: 17 - architecture: x64 - - name: Cache Maven packages - uses: actions/cache@v3.2.5 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2- - - name: Build with Maven - run: mvn --no-transfer-progress verify - - build: - if: github.repository == 'WebGoat/WebGoat' && github.event_name == 'push' - runs-on: ubuntu-latest - name: "Branch build" - steps: - - uses: actions/checkout@v3 - - name: set up JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: 17 - architecture: x64 - - name: Cache Maven packages - uses: actions/cache@v3.2.5 - with: - path: ~/.m2 - key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ubuntu-latest-m2- - - name: Test with Maven - run: mvn --no-transfer-progress verify + 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 ] + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + architecture: x64 + - name: Cache Maven packages + uses: actions/cache@v3.2.5 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2- + - name: Build with Maven + run: mvn --no-transfer-progress verify + - name: "Set up QEMU" + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v2.1.0 + - name: "Set up Docker Buildx" + if: runner.os == 'Linux' + uses: docker/setup-buildx-action@v2 + - name: "Verify Docker WebGoat build" + if: runner.os == 'Linux' + uses: docker/build-push-action@v4.0.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@v4.0.0 + if: runner.os == 'Linux' + with: + context: ./ + file: ./Dockerfile_desktop + push: false + build-args: | + webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} From 0f38519ecf654deda957818e0c3645a6f0a521e7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Feb 2023 12:46:16 +0100 Subject: [PATCH 020/155] ci: add step for pushing Docker desktop image --- .github/workflows/release.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c045b1543..56cafee6f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: - name: Set up JDK 17 uses: actions/setup-java@v3 with: - distribution: 'zulu' + distribution: 'temurin' java-version: 17 architecture: x64 @@ -85,7 +85,7 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: "Build and push" + - name: "Build and push WebGoat" uses: docker/build-push-action@v4.0.0 with: context: ./ @@ -98,8 +98,18 @@ jobs: build-args: | webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} - - name: "Image digest" - run: echo ${{ steps.docker_build.outputs.digest }} + - name: "Build and push WebGoat desktop" + uses: docker/build-push-action@v4.0.0 + with: + context: ./ + file: ./Dockerfile_desktop + push: true + platforms: linux/amd64, linux/arm64, linux/arm/v7 + tags: | + webgoat/webgoat-desktop:${{ env.WEBGOAT_TAG_VERSION }} + webgoat/webgoat-desktop:latest + build-args: | + webgoat_version=${{ env.WEBGOAT_MAVEN_VERSION }} new_version: if: github.repository == 'WebGoat/WebGoat' name: Update to next SNAPSHOT version @@ -118,7 +128,7 @@ jobs: - name: Set version to next snapshot run: | - mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT versions:commit + mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}-SNAPSHOT versions:commit - name: Push the changes to new branch uses: devops-infra/action-commit-push@v0.9.2 From 19d54dbe9521c6d6fa0f945eae1d2e7fc186a95e Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Feb 2023 13:07:22 +0100 Subject: [PATCH 021/155] chore: release version 2023.4 --- CREATE_RELEASE.md | 1 + RELEASE_NOTES.md | 13 +++++++++++++ pom.xml | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CREATE_RELEASE.md b/CREATE_RELEASE.md index 7bda7405a..10c969e94 100644 --- a/CREATE_RELEASE.md +++ b/CREATE_RELEASE.md @@ -13,6 +13,7 @@ committers. ``` mvn versions:set << update release notes >> +mvn verify git commit .... git tag v2023.01 git push --tags diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 44ad88e44..78aeae1e3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,18 @@ # WebGoat release notes +## Version 2023.4 + +### New functionality + +- [#1422 Add Docker Linux Desktop variant with all tools installed](https://github.com/WebGoat/WebGoat/issues/1422). Thanks to the [OWASP WrongSecrets project](https://owasp.org/www-project-wrongsecrets/) we now have a Docker Linux desktop image with all the tools installed. No need to install any tools locally only run the new Docker image. See README.md for details on how to start it. +- [#1411 JWT: looks that buy as Tom also works with alg:none](https://github.com/WebGoat/WebGoat/issues/1411). + +### Bug fixes + +- [#1410 WebWolf: JWT decode is broken](https://github.com/WebGoat/WebGoat/issues/1410). +- [#1396 password_reset return 500 Error](https://github.com/WebGoat/WebGoat/issues/1396). +- [#1379 Move XXE to A05:2021-Security Misconfiguration](https://github.com/WebGoat/WebGoat/issues/1379). + ## Version 2023.3 With great pleasure, we present you with a new release of WebGoat **2023.3**. Finally, it has been a while. This year starts with a new release of WebGoat. This year we will undoubtedly release more often. From this release on, we began to use a new versioning scheme (https://calver.org/#scheme). diff --git a/pom.xml b/pom.xml index 7dc376ec7..1733586bb 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.owasp.webgoat webgoat - 2023.4-SNAPSHOT + 2023.4 jar WebGoat From 9f6cf39ff231b7148b0725248c73c86496b13ab7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Feb 2023 14:48:01 +0100 Subject: [PATCH 022/155] ci: add distribution in snapshot job --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56cafee6f..9304e5d8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -123,6 +123,7 @@ jobs: - name: Set up JDK 17 uses: actions/setup-java@v3 with: + distribution: 'temurin' java-version: 17 architecture: x64 From de2f56822987f902a7b53097418f3628495f53c7 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Feb 2023 14:48:37 +0100 Subject: [PATCH 023/155] chore: back to snapshot version after release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1733586bb..2b0a8010b 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.owasp.webgoat webgoat - 2023.4 + 2023.5-SNAPSHOT jar WebGoat From 8269207d6bc5c11132d6404bb3e9c734fb2b190b Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Fri, 17 Feb 2023 14:58:02 +0100 Subject: [PATCH 024/155] docs: add documentation we start using Conventional Commits. Fixes #1022 --- CONTRIBUTING.md | 12 ++++++++++++ README.md | 1 + 2 files changed, 13 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a97e18ee..780a2644a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,7 @@ [![GitHub contributors](https://img.shields.io/github/contributors/WebGoat/WebGoat.svg)](https://github.com/WebGoat/WebGoat/graphs/contributors) ![GitHub issues by-label "help wanted"](https://img.shields.io/github/issues/WebGoat/WebGoat/help%20wanted.svg) ![GitHub issues by-label "good first issue"](https://img.shields.io/github/issues/WebGoat/WebGoat/good%20first%20issue.svg) +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) This document describes how you can contribute to WebGoat. Please read it carefully. @@ -41,6 +42,17 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change ### Write a good commit message +* We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and use the following types: + - fix: + - feat: + - build: + - chore: + - ci: + - docs: + - refactor: + - test: + + Using this style of commits makes it possible to create our release notes automatically. * Explain why you make the changes. [More infos about a good commit message.](https://betterprogramming.pub/stop-writing-bad-commit-messages-8df79517177d) * If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) to your commit message. diff --git a/README.md b/README.md index 8401d8d41..b6fe80b62 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![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) [![Discussions](https://img.shields.io/github/discussions/WebGoat/WebGoat)](https://github.com/WebGoat/WebGoat/discussions) +[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) # Introduction From b49c61636bc98f96da173e46c89763bcbfcaa060 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Tue, 21 Feb 2023 20:56:53 +0100 Subject: [PATCH 025/155] ci: add maven and docker to dependabot configuration --- .github/dependabot.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 583decfd1..28d0b0bc6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,15 @@ version: 2 updates: - # Maintain dependencies for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" \ No newline at end of file + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + From 1b49b2fd3be058871ac0aa51b99824424f32e095 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 22 Feb 2023 10:31:10 +0100 Subject: [PATCH 026/155] chore: format markdown file --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 780a2644a..bc3d34416 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,16 +43,18 @@ Pull requests should be as small/atomic as possible. Large, wide-sweeping change ### Write a good commit message * We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) and use the following types: - - fix: - - feat: + + - fix: + - feat: - build: - - chore: + - chore: - ci: - docs: - refactor: - test: - + Using this style of commits makes it possible to create our release notes automatically. + * Explain why you make the changes. [More infos about a good commit message.](https://betterprogramming.pub/stop-writing-bad-commit-messages-8df79517177d) * If you fix an issue with your commit, please close the issue by [adding one of the keywords and the issue number](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) to your commit message. From 5dbe2eaf193d9397b5ba86623b9b8e17630597be Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 22 Feb 2023 10:32:41 +0100 Subject: [PATCH 027/155] refactor: update challenge code - Flags are now wired through a Spring config - Introduced Flag class - Removed Flags from the FlagController --- .../webgoat/lessons/challenges/Flag.java | 88 ++----------------- .../lessons/challenges/FlagController.java | 62 +++++++++++++ .../webgoat/lessons/challenges/Flags.java | 27 ++++++ .../challenges/challenge1/Assignment1.java | 17 ++-- .../challenges/challenge1/ImageServlet.java | 8 +- .../challenges/challenge5/Assignment5.java | 11 ++- .../challenges/challenge7/Assignment7.java | 17 ++-- .../challenges/challenge8/Assignment8.java | 12 +-- .../lessons/challenges/Assignment1Test.java | 34 +------ 9 files changed, 131 insertions(+), 145 deletions(-) create mode 100644 src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java create mode 100644 src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java b/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java index d78186585..483b738fa 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/Flag.java @@ -1,89 +1,13 @@ -/* - * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ - * - * Copyright (c) 2002 - 2019 Bruce Mayhew - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; if - * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Getting Source ============== - * - * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. - */ - package org.owasp.webgoat.lessons.challenges; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.stream.IntStream; -import javax.annotation.PostConstruct; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.owasp.webgoat.container.assignments.AssignmentEndpoint; -import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.container.session.WebSession; -import org.owasp.webgoat.container.users.UserTracker; -import org.owasp.webgoat.container.users.UserTrackerRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +public record Flag(int number, String answer) { -/** - * @author nbaars - * @since 3/23/17. - */ -@RestController -public class Flag extends AssignmentEndpoint { - - public static final Map FLAGS = new HashMap<>(); - @Autowired private UserTrackerRepository userTrackerRepository; - @Autowired private WebSession webSession; - - @AllArgsConstructor - private class FlagPosted { - @Getter private boolean lessonCompleted; + public boolean isCorrect(String flag) { + return answer.equals(flag); } - @PostConstruct - public void initFlags() { - IntStream.range(1, 10).forEach(i -> FLAGS.put(i, UUID.randomUUID().toString())); - } - - @RequestMapping( - path = "/challenge/flag", - method = RequestMethod.POST, - produces = MediaType.APPLICATION_JSON_VALUE) - @ResponseBody - public AttackResult postFlag(@RequestParam String flag) { - UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); - String currentChallenge = webSession.getCurrentLesson().getName(); - int challengeNumber = - Integer.valueOf( - currentChallenge.substring(currentChallenge.length() - 1, currentChallenge.length())); - String expectedFlag = FLAGS.get(challengeNumber); - final AttackResult attackResult; - if (expectedFlag.equals(flag)) { - userTracker.assignmentSolved(webSession.getCurrentLesson(), "Assignment" + challengeNumber); - attackResult = success(this).feedback("challenge.flag.correct").build(); - } else { - userTracker.assignmentFailed(webSession.getCurrentLesson()); - attackResult = failed(this).feedback("challenge.flag.incorrect").build(); - } - userTrackerRepository.save(userTracker); - return attackResult; + @Override + public String toString() { + return answer; } } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java new file mode 100644 index 000000000..1b2c497bd --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/FlagController.java @@ -0,0 +1,62 @@ +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.lessons.challenges; + +import lombok.AllArgsConstructor; +import org.owasp.webgoat.container.assignments.AssignmentEndpoint; +import org.owasp.webgoat.container.assignments.AttackResult; +import org.owasp.webgoat.container.session.WebSession; +import org.owasp.webgoat.container.users.UserTracker; +import org.owasp.webgoat.container.users.UserTrackerRepository; +import org.springframework.http.MediaType; +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 { + + private final UserTrackerRepository userTrackerRepository; + private final WebSession webSession; + private final Flags flags; + + @PostMapping(path = "/challenge/flag", produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public AttackResult postFlag(@RequestParam String flag) { + UserTracker userTracker = userTrackerRepository.findByUser(webSession.getUserName()); + Flag expectedFlag = flags.getFlag(webSession.getCurrentLesson()); + final AttackResult attackResult; + if (expectedFlag.isCorrect(flag)) { + userTracker.assignmentSolved( + webSession.getCurrentLesson(), "Assignment" + expectedFlag.number()); + attackResult = success(this).feedback("challenge.flag.correct").build(); + } else { + userTracker.assignmentFailed(webSession.getCurrentLesson()); + attackResult = failed(this).feedback("challenge.flag.incorrect").build(); + } + userTrackerRepository.save(userTracker); + return attackResult; + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java b/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java new file mode 100644 index 000000000..d3b92b149 --- /dev/null +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/Flags.java @@ -0,0 +1,27 @@ +package org.owasp.webgoat.lessons.challenges; + +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 +public class Flags { + private final Map FLAGS = new HashMap<>(); + + public Flags() { + IntStream.range(1, 10).forEach(i -> FLAGS.put(i, new Flag(i, UUID.randomUUID().toString()))); + } + + public Flag getFlag(Lesson forLesson) { + String lessonName = forLesson.getName(); + int challengeNumber = Integer.valueOf(lessonName.substring(lessonName.length() - 1)); + return FLAGS.get(challengeNumber); + } + + public Flag getFlag(int flagNumber) { + return FLAGS.get(flagNumber); + } +} diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java index 0d07c7427..de99c4470 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/Assignment1.java @@ -2,11 +2,10 @@ package org.owasp.webgoat.lessons.challenges.challenge1; import static org.owasp.webgoat.lessons.challenges.SolutionConstants.PASSWORD; -import javax.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.lessons.challenges.Flag; -import org.springframework.util.StringUtils; +import org.owasp.webgoat.lessons.challenges.Flags; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -43,12 +42,14 @@ import org.springframework.web.bind.annotation.RestController; * @since August 11, 2016 */ @RestController +@RequiredArgsConstructor public class Assignment1 extends AssignmentEndpoint { + private final Flags flags; + @PostMapping("/challenge/1") @ResponseBody - public AttackResult completed( - @RequestParam String username, @RequestParam String password, HttpServletRequest request) { + public AttackResult completed(@RequestParam String username, @RequestParam String password) { boolean ipAddressKnown = true; boolean passwordCorrect = "admin".equals(username) @@ -56,14 +57,10 @@ public class Assignment1 extends AssignmentEndpoint { .replace("1234", String.format("%04d", ImageServlet.PINCODE)) .equals(password); if (passwordCorrect && ipAddressKnown) { - return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build(); + return success(this).feedback("challenge.solved").feedbackArgs(flags.getFlag(1)).build(); } else if (passwordCorrect) { return failed(this).feedback("ip.address.unknown").build(); } return failed(this).build(); } - - public static boolean containsHeader(HttpServletRequest request) { - return StringUtils.hasText(request.getHeader("X-Forwarded-For")); - } } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java index 1de00e012..6ae34384c 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge1/ImageServlet.java @@ -4,8 +4,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; import java.io.IOException; -import java.security.SecureRandom; -import javax.servlet.http.HttpServlet; +import java.util.Random; import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; @@ -13,10 +12,9 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -public class ImageServlet extends HttpServlet { +public class ImageServlet { - private static final long serialVersionUID = 9132775506936676850L; - public static final int PINCODE = new SecureRandom().nextInt(10000); + public static final int PINCODE = new Random().nextInt(10000); @RequestMapping( method = {GET, POST}, diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java index d6b8dcceb..c8b3f3d10 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge5/Assignment5.java @@ -24,11 +24,12 @@ package org.owasp.webgoat.lessons.challenges.challenge5; import java.sql.PreparedStatement; import java.sql.ResultSet; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.LessonDataSource; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.lessons.challenges.Flag; +import org.owasp.webgoat.lessons.challenges.Flags; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -37,13 +38,11 @@ import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j +@RequiredArgsConstructor public class Assignment5 extends AssignmentEndpoint { private final LessonDataSource dataSource; - - public Assignment5(LessonDataSource dataSource) { - this.dataSource = dataSource; - } + private final Flags flags; @PostMapping("/challenge/5") @ResponseBody @@ -66,7 +65,7 @@ public class Assignment5 extends AssignmentEndpoint { ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) { - return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(5)).build(); + return success(this).feedback("challenge.solved").feedbackArgs(flags.getFlag(5)).build(); } else { return failed(this).feedback("challenge.close").build(); } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java index 30e17288c..475e59b37 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java @@ -8,9 +8,8 @@ import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Email; -import org.owasp.webgoat.lessons.challenges.Flag; +import org.owasp.webgoat.lessons.challenges.Flags; import org.owasp.webgoat.lessons.challenges.SolutionConstants; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; @@ -44,10 +43,16 @@ public class Assignment7 extends AssignmentEndpoint { + "Kind regards, \n" + "Team WebGoat"; - @Autowired private RestTemplate restTemplate; + private final Flags flags; + private final RestTemplate restTemplate; + private final String webWolfMailURL; - @Value("${webwolf.mail.url}") - private String webWolfMailURL; + public Assignment7( + Flags flags, RestTemplate restTemplate, @Value("${webwolf.mail.url}") String webWolfMailURL) { + this.flags = flags; + this.restTemplate = restTemplate; + this.webWolfMailURL = webWolfMailURL; + } @GetMapping("/challenge/7/reset-password/{link}") public ResponseEntity resetPassword(@PathVariable(value = "link") String link) { @@ -58,7 +63,7 @@ public class Assignment7 extends AssignmentEndpoint { + "" + "

Here is your flag: " + "" - + Flag.FLAGS.get(7) + + flags.getFlag(7) + ""); } return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT) diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java index 535b92f18..507b7b4bd 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge8/Assignment8.java @@ -4,10 +4,11 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; -import org.owasp.webgoat.lessons.challenges.Flag; +import org.owasp.webgoat.lessons.challenges.Flags; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -15,12 +16,9 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -/** - * @author nbaars - * @since 4/8/17. - */ @RestController @Slf4j +@RequiredArgsConstructor public class Assignment8 extends AssignmentEndpoint { private static final Map votes = new HashMap<>(); @@ -33,6 +31,8 @@ public class Assignment8 extends AssignmentEndpoint { votes.put(5, 300); } + private final Flags flags; + @GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity vote( @@ -47,7 +47,7 @@ public class Assignment8 extends AssignmentEndpoint { Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0); votes.put(nrOfStars, allVotesForStar + 1); return ResponseEntity.ok() - .header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)) + .header("X-FlagController", "Thanks for voting, your flag is: " + flags.getFlag(8)) .build(); } diff --git a/src/test/java/org/owasp/webgoat/lessons/challenges/Assignment1Test.java b/src/test/java/org/owasp/webgoat/lessons/challenges/Assignment1Test.java index 3628cb904..3d360edfe 100644 --- a/src/test/java/org/owasp/webgoat/lessons/challenges/Assignment1Test.java +++ b/src/test/java/org/owasp/webgoat/lessons/challenges/Assignment1Test.java @@ -37,20 +37,17 @@ import org.owasp.webgoat.lessons.challenges.challenge1.ImageServlet; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -/** - * @author nbaars - * @since 5/2/17. - */ @ExtendWith(MockitoExtension.class) class Assignment1Test extends AssignmentEndpointTest { private MockMvc mockMvc; + private Flags flags; @BeforeEach void setup() { - Assignment1 assignment1 = new Assignment1(); + flags = new Flags(); + Assignment1 assignment1 = new Assignment1(flags); init(assignment1); - new Flag().initFlags(); this.mockMvc = standaloneSetup(assignment1).build(); } @@ -67,8 +64,7 @@ class Assignment1Test extends AssignmentEndpointTest { "password", SolutionConstants.PASSWORD.replace( "1234", String.format("%04d", ImageServlet.PINCODE)))) - .andExpect( - jsonPath("$.feedback", CoreMatchers.containsString("flag: " + Flag.FLAGS.get(1)))) + .andExpect(jsonPath("$.feedback", CoreMatchers.containsString("flag: " + flags.getFlag(1)))) .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(true))); } @@ -83,26 +79,4 @@ class Assignment1Test extends AssignmentEndpointTest { jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("assignment.not.solved")))) .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); } - - // @Test - // public void correctPasswordXForwardHeaderMissing() throws Exception { - // mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") - // .param("username", "admin") - // .param("password", SolutionConstants.PASSWORD)) - // .andExpect(jsonPath("$.feedback", - // CoreMatchers.is(messages.getMessage("ip.address.unknown")))) - // .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); - // } - - // @Test - // public void correctPasswordXForwardHeaderWrong() throws Exception { - // mockMvc.perform(MockMvcRequestBuilders.post("/challenge/1") - // .header("X-Forwarded-For", "127.0.1.2") - // .param("username", "admin") - // .param("password", SolutionConstants.PASSWORD)) - // .andExpect(jsonPath("$.feedback", - // CoreMatchers.is(messages.getMessage("ip.address.unknown")))) - // .andExpect(jsonPath("$.lessonCompleted", CoreMatchers.is(false))); - // } - } From 36f99dede8ff4584d82256f0e9a1d0f28eeb7bb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 09:15:51 +0000 Subject: [PATCH 028/155] Bump actions/cache from 3.2.5 to 3.2.6 Bumps [actions/cache](https://github.com/actions/cache) from 3.2.5 to 3.2.6. - [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.2.5...v3.2.6) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fdb8cfbe..6e2bf031a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: java-version: 17 architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.5 + uses: actions/cache@v3.2.6 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9304e5d8f..8a5011282 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: architecture: x64 - name: Cache Maven packages - uses: actions/cache@v3.2.5 + uses: actions/cache@v3.2.6 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc56939fe..3b3344596 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: 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.2.5 + uses: actions/cache@v3.2.6 with: path: ~/.m2 key: ubuntu-latest-m2-${{ hashFiles('**/pom.xml') }} From 5243fa2bf2b1972c1c9b03b38acb3c78b719b49c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:35:01 +0100 Subject: [PATCH 029/155] chore: bump jose4j from 0.7.6 to 0.9.3 (#1431) Bumps [jose4j](https://bitbucket.org/b_c/jose4j) from 0.7.6 to 0.9.3. - [Commits](https://bitbucket.org/b_c/jose4j/branches/compare/jose4j-0.9.3..jose4j-0.7.6) --- updated-dependencies: - dependency-name: org.bitbucket.b_c:jose4j dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b0a8010b..9cce8cb99 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ 30.1-jre 17 0.9.1 - 0.7.6 + 0.9.3 3.5.1 1.14.3 3.8.0 From 8467ae8a0b74599fcc7d8724ad1a0f83a04c095b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:35:20 +0100 Subject: [PATCH 030/155] chore: bump jsoup from 1.14.3 to 1.15.4 (#1430) Bumps [jsoup](https://github.com/jhy/jsoup) from 1.14.3 to 1.15.4. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.14.3...jsoup-1.15.4) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9cce8cb99..61f84c5f4 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ 0.9.1 0.9.3 3.5.1 - 1.14.3 + 1.15.4 3.8.0 2.22.0 3.1.2 From ecfa0197af88dac6732dd8447e128052df6fe73a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:35:36 +0100 Subject: [PATCH 031/155] chore: bump maven-surefire-plugin from 3.0.0-M5 to 3.0.0-M9 (#1429) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0-M5 to 3.0.0-M9. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0-M5...surefire-3.0.0-M9) --- 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61f84c5f4..e51c77b9b 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ 3.1.2 3.1.1 3.1.0 - 3.0.0-M5 + 3.0.0-M9 17 17 3.15.0 From 6d3813c2ce8ad1fc34cea450b7298acc8aaf7ff5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:35:52 +0100 Subject: [PATCH 032/155] chore: bump commons-compress from 1.21 to 1.22 (#1428) Bumps commons-compress from 1.21 to 1.22. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e51c77b9b..54929d1fa 100644 --- a/pom.xml +++ b/pom.xml @@ -241,7 +241,7 @@ org.apache.commons commons-compress - 1.21 + 1.22 org.jruby From df8c83fe744546e05d3dd189813cad3a19d202a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:36:07 +0100 Subject: [PATCH 033/155] chore: bump eclipse-temurin from 17-jre-focal to 19-jre-focal (#1427) Bumps eclipse-temurin from 17-jre-focal to 19-jre-focal. --- updated-dependencies: - dependency-name: eclipse-temurin dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f89bf98c7..fdc0f4ff2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/eclipse-temurin:17-jre-focal +FROM docker.io/eclipse-temurin:19-jre-focal LABEL NAME = "WebGoat: A deliberately insecure Web Application" MAINTAINER "WebGoat team" From c5629be61893232e1a2724530ffefa0b00a772cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:36:24 +0100 Subject: [PATCH 034/155] chore: bump spotless-maven-plugin from 2.29.0 to 2.33.0 (#1426) Bumps [spotless-maven-plugin](https://github.com/diffplug/spotless) from 2.29.0 to 2.33.0. - [Release notes](https://github.com/diffplug/spotless/releases) - [Changelog](https://github.com/diffplug/spotless/blob/main/CHANGES.md) - [Commits](https://github.com/diffplug/spotless/compare/lib/2.29.0...lib/2.33.0) --- updated-dependencies: - dependency-name: com.diffplug.spotless:spotless-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 54929d1fa..180e3a2c0 100644 --- a/pom.xml +++ b/pom.xml @@ -514,7 +514,7 @@ com.diffplug.spotless spotless-maven-plugin - 2.29.0 + 2.33.0 From 61dac201f01f9bc1b2cf46dd0a379eabecc65e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80ngel=20Oll=C3=A9=20Bl=C3=A1zquez?= Date: Tue, 21 Feb 2023 00:40:46 +0100 Subject: [PATCH 035/155] Add coverage profile --- pom.xml | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 180e3a2c0..75e55b8cb 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,6 @@ - 2.5.3 3.3.7 @@ -120,6 +119,7 @@ 3.12.0 1.9 30.1-jre + 0.8.8 17 0.9.1 0.9.3 @@ -488,9 +488,9 @@ ${maven-surefire-plugin.version} --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED - --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED - --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED - --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED **/*IntegrationTest.java src/it/java @@ -727,6 +727,82 @@ + + + coverage + + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED + ${surefire.jacoco.args} + + **/*IntegrationTest.java + src/it/java + org/owasp/webgoat/*Test + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + before-unit-test + + prepare-agent + + + ${project.build.directory}/jacoco/jacoco-ut.exec + surefire.jacoco.args + + + + check + + check + + + + + BUNDLE + + + CLASS + COVEREDCOUNT + 0.6 + + + + + ${project.build.directory}/jacoco/jacoco-ut.exec + + + + after-unit-test + + report + + test + + ${project.build.directory}/jacoco/jacoco-ut.exec + ${project.reporting.outputDirectory}/jacoco-unit-test-coverage-report + + + + + + + - From e50986a098ac2a287012557d29ecad7e27af9109 Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Wed, 22 Feb 2023 22:55:48 +0100 Subject: [PATCH 036/155] fix: challenge 7 (#1433) --- .../webgoat/ChallengeIntegrationTest.java | 64 ++++++++++++++++++- .../org/owasp/webgoat/IntegrationTest.java | 11 ++++ .../lessons/challenges/SolutionConstants.java | 2 - .../challenges/challenge7/Assignment7.java | 11 ++-- .../webwolf/mailbox/MailboxController.java | 21 ++++-- 5 files changed, 92 insertions(+), 17 deletions(-) diff --git a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java index 133721eee..f7f4511f8 100644 --- a/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/ChallengeIntegrationTest.java @@ -7,12 +7,14 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; public class ChallengeIntegrationTest extends IntegrationTest { @Test - public void testChallenge1() { + void testChallenge1() { startLesson("Challenge1"); byte[] resultBytes = @@ -67,7 +69,7 @@ public class ChallengeIntegrationTest extends IntegrationTest { } @Test - public void testChallenge5() { + void testChallenge5() { startLesson("Challenge5"); Map params = new HashMap<>(); @@ -107,4 +109,62 @@ public class ChallengeIntegrationTest extends IntegrationTest { .get("find { it.username == \"" + this.getUser() + "\" }.flagsCaptured"); assertTrue(capturefFlags.contains("Without password")); } + + @Test + void testChallenge7() { + startLesson("Challenge7"); + cleanMailbox(); + + // One should first be able to download git.zip from WebGoat + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/WebGoat/challenge/7/.git")) + .then() + .statusCode(200) + .extract() + .asString(); + + // Should send an email to WebWolf inbox this should give a hint to the link being static + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .formParams("email", getUser() + "@webgoat.org") + .post(url("/WebGoat/challenge/7")) + .then() + .statusCode(200) + .extract() + .asString(); + + // Check whether email has been received + var responseBody = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .get(webWolfUrl("/mail")) + .then() + .extract() + .response() + .getBody() + .asString(); + Assertions.assertThat(responseBody).contains("Hi, you requested a password reset link"); + + // Call reset link with admin link + String result = + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("/challenge/7/reset-password/{link}"), "375afe1104f4a487a73823c50a9292a2") + .then() + .statusCode(HttpStatus.ACCEPTED.value()) + .extract() + .asString(); + + String flag = result.substring(result.indexOf("flag") + 6, result.indexOf("flag") + 42); + checkAssignment(url("/WebGoat/challenge/flag"), Map.of("flag", flag), true); + } } diff --git a/src/it/java/org/owasp/webgoat/IntegrationTest.java b/src/it/java/org/owasp/webgoat/IntegrationTest.java index db551afeb..21ef208b1 100644 --- a/src/it/java/org/owasp/webgoat/IntegrationTest.java +++ b/src/it/java/org/owasp/webgoat/IntegrationTest.java @@ -11,6 +11,7 @@ import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.springframework.http.HttpStatus; public abstract class IntegrationTest { @@ -252,4 +253,14 @@ public abstract class IntegrationTest { .getBody() .asString(); } + + public void cleanMailbox() { + RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("WEBWOLFSESSION", getWebWolfCookie()) + .delete(webWolfUrl("/mail")) + .then() + .statusCode(HttpStatus.ACCEPTED.value()); + } } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java b/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java index 890d80d06..ea34d5ebe 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/SolutionConstants.java @@ -32,6 +32,4 @@ public interface SolutionConstants { // TODO should be random generated when starting the server String PASSWORD = "!!webgoat_admin_1234!!"; - String PASSWORD_TOM = "thisisasecretfortomonly"; - String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; } diff --git a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java index 475e59b37..31260e8e1 100644 --- a/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java +++ b/src/main/java/org/owasp/webgoat/lessons/challenges/challenge7/Assignment7.java @@ -9,7 +9,6 @@ import org.owasp.webgoat.container.assignments.AssignmentEndpoint; import org.owasp.webgoat.container.assignments.AttackResult; import org.owasp.webgoat.lessons.challenges.Email; import org.owasp.webgoat.lessons.challenges.Flags; -import org.owasp.webgoat.lessons.challenges.SolutionConstants; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; @@ -32,6 +31,8 @@ import org.springframework.web.client.RestTemplate; @Slf4j public class Assignment7 extends AssignmentEndpoint { + public static final String ADMIN_PASSWORD_LINK = "375afe1104f4a487a73823c50a9292a2"; + private static final String TEMPLATE = "Hi, you requested a password reset link, please use this
link to reset your" @@ -56,15 +57,13 @@ public class Assignment7 extends AssignmentEndpoint { @GetMapping("/challenge/7/reset-password/{link}") public ResponseEntity resetPassword(@PathVariable(value = "link") String link) { - if (link.equals(SolutionConstants.ADMIN_PASSWORD_LINK)) { + if (link.equals(ADMIN_PASSWORD_LINK)) { return ResponseEntity.accepted() .body( "